DropSpawn 是一款 CobaltStrike BOF,用于通过一种相对不为人知的 DLL 劫持方法来衍生额外的 Beacon。支持 x86-x86、x64-x64 以及 x86-x64/反向架构。可用作进程注入的替代方案。
Windows 可执行文件在尝试加载未指定绝对路径的 DLL 时,会遵循 DLL 搜索顺序:

DLL 劫持通常需要满足以下条件之一:
A. 用户在某个文件夹中拥有写入权限,且该文件夹的搜索顺序优先级高于真实 DLL 所在的文件夹
或
B. 目标 DLL 在系统中完全不存在,此时可以将它放置在用户 %PATH% 变量中可写入的文件夹内(例如 %USERPROFILE%\appdata\local\microsoft\windowsapps)。
这些要求排除了对位于 C:\Windows\System32 中的可执行文件进行 DLL 劫持的可能性,因为这些可执行文件加载的几乎所有 DLL 也都位于 System32 中。将 System32 可执行文件复制到用户可写入的位置并在此执行是一种选项,但 OPSEC 安全性不高,因为从非标准位置运行的 System32 二进制文件很容易被识别。
DropSpawn 通过将“应用程序加载目录”伪造为任意用户指定的目录,使得对 System32 可执行文件(以及其他位于非用户可写入文件夹中的可执行文件)进行 DLL 劫持成为可能。
公开发布的 DropSpawn 与非公开发布版本略有不同。非公开版本利用了专有的 Payload 生成器,使操作员的体验更加流畅。公开发布版本已稍作修改,以适应用户使用各自方式生成兼容 DLL 劫持 Payload 的情况。项目中包含了一个 Python3 脚本以及一个用于演示的 DLL 源代码,以帮助用户集成和使用 DropSpawn。
找到一些尝试加载 DLL 但未指定其绝对路径的目标可执行文件。你可以将该 exe 复制到用户可写入的目录中,并使用 Procmon 监控其执行来进行识别。在此示例中,我们使用通常位于 C:\Windows\System32\WerFault.exe 的 WerFault.exe。

在上面的例子中,cryptsp.dll、wer.dll、dbghelp.dll 和 bcrypt.dll 都是可行的候选,因为 WerFault 没有指定它们的绝对路径;因此,WerFault 将首先尝试从其应用程序目录加载它们,然后再使用 DLL 搜索顺序中的其他路径。请注意,通常这并非问题,因为 WerFault 的应用程序目录就是 System32。
从目标系统下载一个可被劫持的 DLL。
这是必要的,以便提取其导出函数并将其包含在我们的 Payload DLL 中。务必从希望使用 DropSpawn 的同一台机器上获取可劫持的 DLL,因为 DLL 会随 Windows 版本变化而不同。此外,如果你正在运行 x86 Beacon 并希望使用 DropSpawn 衍生 x64 Beacon,请确保通过指定 'C:\windows\sysnative...' 而不是 'C:\windows\system32...' 来下载 x64 版本的真实 DLL。
运行 generate_dll.py,传入下载的 DLL 和目标架构。Generate_dll.py 是此脚本的修改版。它将解析提供的 DLL,创建一个包含 DLL 导出函数的 .def 文件,并调用 MingW 来编译我们的演示 Payload DLL。当衍生的进程尝试调用伪造 DLL 中的真实函数时,我们的 Payload DLL 会将调用转发到 System32 中的真实 DLL,以避免宿主进程崩溃。

使用生成的 Payload DLL 调用 DropSpawn。
dropspawn <x86|x64> <要衍生的程序> [可写入的目标文件夹] [父进程]
payload DLL - 生成的 DLL Payload 的完整路径。 architecture - 要衍生进程的架构。 program to spawn - 要衍生的进程的名称或路径。如果该进程位于 System32(或 syswow64)中,仅指定名称即可。否则,指定完整路径。你也可以为进程提供命令行参数。如果路径中包含空格或使用了参数,请将整个内容用引号括起来。 writable target folder - 可选。如果留空,DropSpawn 将尝试使用 Beacon 的当前目录。如果路径中有空格,请使用引号。 parent - 可选。用于对新衍生进程进行 PPID 欺骗的进程名称。如果指定的进程有多个不同权限级别的运行实例(例如 svchost.exe),DropSpawn 将尝试识别一个可用于 PPID 欺骗的实例。
示例:dropspawn /root/gitlab/DropSpawn_BOF/dist/dbgcore.dll x64 "WerFault.exe -u -p 4352 -s 160" C:\users\user\appdata\local\temp explorer.exe
这将把 Payload DLL 'dbgcore.dll' 释放到磁盘上的 'c:\users\user\appdata\local\temp\dbgcore.dll',并以命令行参数 '-u -p 4352 -s 160' 衍生一个 x64 的 WerFault.exe 进程,并将 explorer.exe 设置为父进程。


清理工作很简单。通过在释放到磁盘的 Payload DLL 中包含自删除功能,一旦我们的新进程启动并加载该 DLL,它就会自动被删除。这是一个颠覆性的改进,因为通常情况下,只要加载了该 DLL 的进程继续运行,DLL 就会被锁定在磁盘上。如果自删除技术因某种原因失败(或进程未能成功衍生),DropSpawn 将尝试从磁盘中删除该 Payload DLL,并告知用户操作结果。
进程注入通常遵循“打开远程进程 -> 分配远程内存 -> 写入远程内存 -> 执行远程内存”的链条,并可以选择在开始时衍生新进程而不是使用现有进程。DropSpawn 仅创建新进程;新衍生的进程负责分配、写入和执行 shellcode,因此我们可以避免许多通常与远程进程注入相关的 IOC。
当然,此技术的效果取决于你的 DLL Payload 质量。但我们可以看看 Windows 层面观察到的情况(下一部分使用 DropSpawn 私有版本并衍生 Beacon)。
就事件查看器而言,一切看起来正常:

在 MDE 中几乎看不到异常。
运行 DropSpawn:

MDE 日志:
使用 PPID 欺骗:

未使用 PPID 欺骗:

两种情况下,我们都看到原始 Beacon 进程(也是一个 WerFault)将 dbgcore.dll 释放到磁盘,创建一个新的 WerFault.exe 进程,新衍生的进程加载 dbgcore.dll,然后重命名(删除)它。关键在于,dbgcore.dll 并没有受到通常 DLL 劫持中常见的额外审查,因为我们没有将其写入任何常被劫持的位置,而且 WerFault.exe(或你选择的任何其他进程)并不像 WmiPrvSE.exe 那样与 DLL 劫持紧密关联。
有趣的是,使用 PPID 欺骗反而比不使用更容易被观察到。不过,这可能因安全产品而异。
如前所述,用户必须从计划使用 DropSpawn 的目标机器上下载真实的 DLL。使用 DLL 的错误版本可能导致衍生进程在尝试调用不存在的函数时崩溃。
DropSpawn 可用于 System32 之外的可执行文件;但请注意,如果进程尝试从进程的真实应用程序目录中加载其他 DLL,可能会出现问题。因为我们已经将应用程序目录伪造到了其他地方,如果真实应用程序目录无法通过 DLL 搜索顺序到达,进程将因找不到必要的 DLL 而崩溃或无法启动。在将潜在的劫持方案投入生产环境之前,务必先在开发机器上进行测试!
这项研究最初源于我探索进程如何组装其最终 DLL 搜索顺序(由于可执行文件位于不同目录、当前目录是搜索路径的一部分等原因,搜索顺序必须在运行时确定)。我的研究引导我找到了 这篇论坛帖子,它成为了该技术核心的两个关键未公开 API 的起源。
前面已经提供过相关链接,但 这篇关于避免加载器锁的文章、这个用于生成 DLL 代理的 .def 文件的脚本 以及 这篇关于实现正在运行的可执行文件自删除的研究 对于生成适用于 DropSpawn 的有效、武器化的 DLL Payload 至关重要。
当我首次在 Twitter 上发布这项技术时,其他人也加入了讨论并制作了 POC。SecurityAndStuff 制作了这一个,而 Snovvcrash 的版本在这里。