Octoberfest7/DSCourier_BOF
GitHub: Octoberfest7/DSCourier_BOF
将 DSCourier 项目移植为 Cobalt Strike BOF,利用 WinGet 的 COM 接口在微软签名进程中执行 PowerShell 代码的概念验证。
Stars: 26 | Forks: 2
# DSCourier BOF
这是 [Dylan Davis](https://www.linkedin.com/in/dylandavis2/) 和 [Matthew Schramm](https://www.linkedin.com/in/matthewmschramm/) 的 [DSCourier](https://github.com/DylanDavis1/DSCourier) 项目的 BOF 实现。它利用 WinGet 的 COM 接口在由微软签名且受信任的进程中执行任意的 PowerShell 代码。他们完整的研究博客可以在[这里](https://dylansec.com/DSCourier/)找到。
**与我发布的大多数项目不同,这不是一个具备实战部署能力的工具,而更像是一个 POC。在发现了一些阻碍其成为易于部署的 BOF 的问题后,我选择不再进一步研究它。这段代码几乎全是拼凑出来的。一些问题仍未解决,并在下面进行了讨论**
# 使用说明
将你要执行的任意 PowerShell 代码放在 dist/rev.yml 文件中。默认情况下,它包含了原仓库中的一个简单的 PowerShell 反向 shell。如果你选择使用它,请确保将现有示例中的 IP 替换为你所需的 IP。
使用 PowerShell 反向 shell 的示例:


# 工作原理 / 限制条件
以下是与该工具相关的一些问题/限制,排名不分先后:
1. 为了使 COM 调用成功,Microsoft.Management.Configuration.winmd 文件必须与发起 COM 调用的可执行文件位于同一目录中。例如,当你从一个被掏空的 system32 进程中运行 Beacon 时,这会立刻引发问题,因为普通用户没有写入权限。为了解决这个问题,该文件会被释放到 %APPDATA%\temp 目录下,并通过内联 Hook 劫持 WinTypes!RoGetMetaDataFile 函数(该函数用于检索 winmd 文件路径),以便我们能够提供该临时路径的位置。这使得 winmd 文件能够被读取 / COM 调用得以成功,但这意味着会发起 VirtualProtect 调用并覆写 DLL 内存,从而产生 IOC。
2. 承接第 1 点,在运行该 BOF 后,磁盘上的 winmd 文件会被锁定,直到 Beacon 进程退出。我稍微尝试了一下以解决这个问题,包括添加了目前广为人知的“自删除”功能,但文件在磁盘上仍处于锁定状态。或许可以通过工程手段绕过/解决此问题,但这就留给其他人去探索吧。
3. 正如原研究中所提到的,因为这使用了 WinGet 中的 pwsh 资源,所以会在 ConfigurationRemotingServer.exe 下生成一个 conhost.exe 进程。Claude 建议可以加载自定义的二进制模块作为资源,而不是调用 pwsh,这应该能解决 conhost 的问题,但这需要向磁盘释放额外的文件,而且我没能让它成功运行。这可能行不通。如果可行的话,这将为向磁盘释放通用的 .NET DLL 敞开大门,该 DLL 可以由 ConfigurationRemotingServer.exe 加载,并执行传入的 shellcode/参数等。
4. 该 BOF 实现了所需 COM 接口的异步版本。使用同步版本会导致 Beacon 挂起 / 无法回连,直到 ConfigurationRemotingServer.exe 进程执行完毕;对于简单的反向 shell 示例而言,这意味着 Beacon 在该 shell 被终止前将无法再次签到。切换到异步接口避免了这个问题,但也引入了一些时序问题。代码中设置了一个硬编码的 3 秒休眠时间,这在实践中成功实现了特定调用之间的延迟,但这当然不是实现此功能的正规方式。
5. COM 接口的定义是通过以下方式获取的:从 Github 下载 winget-cli 的 .msixbundle,解压后提取 .msix,然后找到 .winmd 文件。接着使用 winmdidl.exe 提取 IDL 文件。随后使用 midlrt.exe 将 IDL 转换为头文件/.c 文件,最后由 Claude 对其进行解析精简,只保留所需的定义。这段代码仍然是一团糟。这个[微软链接](https://learn.microsoft.com/en-us/cpp/cppcx/wrl/use-winmdidl-and-midlrt-to-create-h-files-from-windows-metadata?view=msvc-170)可能有助于更好地理解这个过程。
6. 代码总体上比较混乱,因为项目没能走出 POC 阶段。
7. 在目标机器上必须至少运行过一次 `winget configure --enable` 命令,BOF 才能成功执行。我追踪到的原因是,在运行该命令 / 下载二进制文件之前,包含 ConfigurationremotingServer.exe 的 DotNet 目录甚至根本不存在。这些文件位于 C:\Program files\WindowsApps\... 中,因此低权限用户无法对其进行写入,所以我们甚至无法在 BOF 中自己将这些文件释放到磁盘上并让程序正常运行。
8. 我调查过,据我所知这些不是 DCOM 接口,仅仅是 COM 接口。因此,这并不是一个可用于向其他机器进行横向移动的有效原语。
9. 由于第 7 点所述的原因,在我看来这不能作为一个良好/可靠的初始访问方法。如果你登录到的机器禁用了 WinGet(参见原博客文章),或者尚未运行过 configure --enable 命令,你就会遭遇硬阻断。对于后渗透阶段而言,它或许有一定价值,但你在一定程度上受到了限制,因为生成/运行你的代码的是一个固定进程,并且由于它是 pwsh,所以还会面临 AMSI 的问题。
# 编译
编写此工具时未使用常规的 BOF API 声明(例如 bofdefs.h 文件)。正如 [Matt Ehrnschwender](https://x.com/M_alphaaa) 在这篇[博客文章](https://blog.cybershenanigans.space/posts/writing-bofs-without-dfr/)中所概述的,可以使用 objcopy 在编译后将格式为 `DLL$API` 的正确符号修补到 BOF 中。
我编写了一个名为 BOFPatcher 的工具来自动化此过程。这使得用户能够像编写普通 C 代码一样编写 BOF,而无需担心繁琐的 API 声明:

该工具仅向购买了我的 [BOF 开发与实战技术](https://training.zeropointsecurity.co.uk/courses/bof-dev-and-tradecraft)课程的用户提供。
虽然此仓库中不包含 BOFPatcher 工具,但该工具的 Makefile 会调用 objcopy,传入一个包含正确符号替换的 imports_dscourier64.txt 文件,从而使 BOF 能够正常使用。
# 鸣谢
1. Dylan 和 Matt 的出色工作。希望能看到他们更多的研究成果!
2. Claude 搞定了其中大部分的代码拼凑工作
标签:AI合规, BOF, Cobalt Strike, COM接口, DLL钩子, DSCourier, ETW, Inline Hook, IOCs, IPv6, OpenCanary, POC, PowerShell, Windows API, WinGet, winmd, 云资产清单, 代码执行, 元数据文件, 反向Shell, 可信进程滥用, 安全测试, 搜索语句(dork), 攻击性安全, 攻击诱捕, 数据展示, 欺骗防御, 红队, 进程 hollowing, 逆向工程