【翻译】Revisiting COM Hijacking
TL;DR: 本文展示了 COM 劫持如何作为一种可靠的持久化方法,同时还能在环境中常用的应用程序内实现代码执行。
概述
持久化是任何红队行动中最重要的步骤之一。如果没有持久化,只需一次重启、用户注销或某些意外中断,就会导致访问权限丢失。说实话,没有什么比失去初始立足点更糟糕的了。
在这篇博客中,我将详细介绍我在红队行动中经常使用的一种持久化技术:组件对象模型(COM)劫持。这种方法在隐蔽性和可靠性之间取得了良好的平衡,而且它不仅仅可以用于持久化。我将展示如何识别使用这种技术的机会,以及如何利用它在 Chrome 或 Edge 等目标进程中加载回调。
COM 劫持回顾
如果您不熟悉 COM 劫持的内部工作原理,MDSec 和 Pentestlab 有关于该主题的精彩文章。以下是简要说明:
COM 劫持利用了 Windows 查找和加载 COM 对象的方式。每个 COM 类都有一个唯一的 CLSID 和一个注册表项,如 InProcServer32(用于 DLL)或 LocalServer32(用于 EXE),它们告诉 Windows 要加载什么。这些条目可以存在于 HKEY_LOCAL_MACHINE (HKLM)(系统范围)或 HKEY_CURRENT_USER (HKCU)(用户特定)注册表配置单元中。由于 Windows 的注册表搜索顺序,HKCU 配置单元会在 HKLM 之前被检查,因此如果 CLSID 同时存在于两者中,则优先使用 HKCU 中的条目。由于用户可以写入自己的 HKCU 配置单元,攻击者可以在其中创建或覆盖 CLSID 条目。如果程序尝试使用该 COM 对象,Windows 将加载攻击者的 DLL 而不是合法的 DLL。因此,我们的目标是找到一个满足以下条件的 COM 对象:
-
存在于 HKLM 中 -
被用户模式进程使用 -
最好在 HKCU 中没有对应的条目
发现阶段
在行动中寻找 COM 劫持机会时,我通常专注于在用户登录时运行的自定义或第三方应用程序(例如,启动文件夹中的应用程序、计划任务、注册表运行项),因为这些是找到可以在不需要用户交互的情况下触发劫持的进程的好地方。
在之前的一次行动中,我注意到用户启动文件夹中的一个 .lnk 文件会启动 Citrix Workspace。我启动了一个 Windows 11 虚拟机,下载了 Citrix Workspace,启动它,然后使用 System Informer 查看完整的进程树(因为我通常会在 Procmon 过滤器中包含每个相关进程)。执行 .lnk 文件会启动 SelfService.exe,然后它会生成几个子进程,包括 msedgewebview2.exe。
为了识别 HKCU 配置单元中缺少 CLSID 的潜在 COM 服务器,我使用以下过滤器配置了 Process Monitor:
-
操作: RegOpenKey -
路径以: InProcServer32 结尾 -
结果: NAME NOT FOUND
我还将每个进程名称作为过滤器参数包含在内。下图显示了我的 Procmon 过滤器的完整视图。
使用这些过滤器启动 Citrix 后,揭示了 HKCU 注册表配置单元中与缺少的 COM 类对象相关的一些注册表事件。最突出的是与 msedgewebview2.exe 相关的缺少的 CLSID 引用,这是一个除了 Citrix 之外,Chrome、Edge、Microsoft Teams 和其他 M365 应用程序也经常生成的进程。这意味着我们不一定需要在主机上安装 Citrix 来触发我们的持久化。
我选择了一个缺少的 CLSID 进行深入分析(即 {54E211B6–3650–4F75–8334–FA359598E1C5})。在 HKLM 注册表配置单元中快速查找发现,它配置了 InProcServer32 条目,指向 C:WindowsSystem32directmanipulation.dll,以及其指定的线程模型。
注意:我们在 HKCU 下创建的线程模型应与 HKLM 中定义的线程模型匹配。
接下来,我在重新创建用户 HKCU 注册表项下的 CLSID 之前准备了持久化有效载荷。这涉及创建一个满足以下要求的存根 DLL:
-
创建一个互斥量: 需要互斥量来防止有效载荷的多个实例执行 -
启动有效载荷: 这可能涉及注入 shellcode、加载另一个 DLL 或简单地启动一个进程。对于此演示,我只需让它启动 calc.exe -
代理必要的 DLL 导出: 我们需要将导出调用转发到原始 DLL 的导出,以保持应用程序稳定性并避免破坏任何功能
为了处理互斥量,我实现了一个 IsPayloadRunning
函数,它使用 CreateEvent
API 定义一个命名的全局事件。这作为一个简单的检查:如果事件已经存在,存根将不会启动有效载荷。如果事件不存在,存根将使用 CreateProcessA
API 启动有效载荷(即 calc.exe)。
注意:如果您计划注入到调用进程而不是生成一个新进程,则需要使用 shellcode 注入或调用 LoadLibrary
API 来加载一个辅助 DLL。
现在我们需要从存根 DLL 中导出预期的函数,并将这些调用转发到合法的 directmanipulation.dll 中的相应函数。这确保了使用 COM 对象的应用程序继续正常运行,而我们的有效载荷在后台执行。像 FaceDancer 这样的工具可以通过解析目标 DLL 的导出表来帮助自动创建这些代理定义。
生成后,我们可以使用 #pragma comment(linker,…)
指令将这些代理定义直接嵌入到我们的 Visual Studio 项目中,这些指令指示链接器转发导出的函数调用。
注意:另一个可以用来转发导出函数调用的工具是 Koppeling。
现在我们的 DLL 已经准备好了,下一步是在 HKCU 注册表配置单元下创建相同的 CLSID,使用与原始相同的线程模型;但是,这次我们将 InProcServer32 默认值指向我们的存根 DLL。对于此演示,存根 DLL 是 StubDLL.dll,位于 C:StubDLL 目录中。
当 Citrix Workspace 启动并实例化该 COM 对象时,Windows 会首先检查 HKCU,找到我们的恶意条目,并启动计算器应用程序。
转向浏览器进程
在使用 Procmon 分析 COM 对象后,我注意到其他几个应用程序(即 Edge、Chrome、Microsoft Teams 和 OneDrive)也与之交互。
这为将回调加载到浏览器进程中打开了大门,这对于隐藏 HTTPS 回调流量和转储 cookie 特别有用。
现在这就是 COM 劫持真正酷的地方。让我们继续以浏览器为例。从版本 127 开始,基于 Chromium 的浏览器(如 Edge 和 Chrome)使用应用程序绑定的加密密钥来加密 cookie。解密它需要您的有效载荷要么从浏览器的应用程序目录运行,要么以 NT AUTHORITYSYSTEM 级别的权限运行。从用户模式的角度来看,这使得 cookie 转储变得更加困难。虽然直接注入 Chrome 是一种选择,但它很嘈杂,更有可能触发检测。那么,与其费尽周折,为什么不通过我们的 COM 劫持直接将回调加载到浏览器进程中呢?
由于多个应用程序实例化此 COM 对象,我们需要确保我们的有效载荷仅在真正关心的进程中运行。为了处理这个问题,我在 DLL 中添加了一个名为 IsChromeOrEdge
的检查,该检查限制执行仅在当前进程是 chrome.exe 或 msedge.exe 时继续。此检查可以放在代码的顶部。如果进程匹配,我们继续进行互斥量检查,然后启动有效载荷。如果不匹配,DLL 会安静地退出,以避免从不需要的应用程序中执行。
在进行此更改后,我启动了 Citrix Workspace 并确认 calc.exe 没有运行。然而,当我启动 Edge 或 Chrome 时,calc.exe 弹出,确认了成功的劫持和代码执行。从这里,您可以使用诸如 cookie-monster-bof 这样的工具(向 KingOfTheNOPs 致敬,感谢他在该领域的研究和工具开发)来转储和解密浏览器 cookie。
最终思考
COM 劫持(COM Hijacking)是一种可靠的技术,我已在多次操作中成功使用且未触发警报。这是一种灵活的方式,既可以建立持久性,又可以在特定应用程序(如 Chrome)中以最小的噪音获得执行。希望本文展示了在正确应用时,COM 劫持的实用性和有效性。
原文始发于微信公众号(securitainment):重新审视 COM 劫持
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论