自 20 世纪 90 年代初以来,组件对象模型 (COM) 一直是 Microsoft Windows 开发的基石,在现代 Windows 操作系统和应用程序中仍然非常流行。多年来对 COM 组件的依赖和广泛的功能开发创造了巨大的攻击面。2024 年 2 月,Google Project Zero 的James Forshaw( @tiraniddo )发布了一篇博客文章,详细介绍了一种滥用分布式 COM (DCOM) 远程处理技术的新方法,其中捕获的 COM 对象可用于在服务器端 DCOM 进程的上下文中执行 .NET 托管代码。Forshaw 重点介绍了几种特权提升和受保护进程轻量级 (PPL) 绕过的用例。
基于 Forshaw 的研究,Mohamed Fakroud(@T3nb3w)于 2024 年 3 月初发布了一种绕过 PPL 保护的技术实现。Jimmy Bayne(@bohops)和我于 2024 年 2 月进行了类似的研究,这使我们通过滥用被捕获的 COM 对象开发出了一种概念验证的无文件横向移动技术。
背景
COM 是一种二进制接口标准和中间件服务层,允许不同的模块化组件相互交互并与应用程序交互,而不管底层编程语言是什么。例如,用 C++ 开发的 COM 对象可以轻松与 .NET 应用程序交互,使开发人员能够有效地集成不同的软件模块。DCOM 是一种远程技术,使 COM 客户端能够通过进程间通信 (IPC) 或远程过程调用 (RPC) 与 COM 服务器通信。许多 Windows 服务都实现了本地或远程访问的 DCOM 组件。
COM 类通常在 Windows 注册表中注册和包含。客户端程序通过创建 COM 类的实例(称为 COM 对象)与 COM 服务器交互。此对象提供指向标准化接口的指针。客户端使用此指针访问对象的方法和属性,从而促进客户端和服务器之间的通信和功能。
COM 对象通常是评估漏洞暴露和发现可滥用功能的研究目标。被捕获的 COM 对象是一个错误类,其中 COM 客户端在进程外 DCOM 服务器中实例化 COM 类,客户端通过按引用编组的对象指针控制 COM 对象。根据情况,此控制向量可能存在与安全相关的逻辑缺陷。
Forshaw 的博客描述了一个 PPL 绕过用例,其中WaaSRemediation COM 类中公开的IDispatch接口被操纵以捕获 COM 对象滥用和 .NET 代码执行。WaaSRemediation在WaaSMedicSvc服务中实现,该服务在 NT AUTHORITYSYSTEM 上下文中作为受保护的svchost.exe进程执行。Forshaw 的出色演练是我们应用研究和开发概念验证无文件横向移动技术的基础。
研究概况
我们的研究之旅始于探索支持IDispatch接口的WaaSRemediation COM 类。此接口允许客户端执行后期绑定。通常,COM 客户端在编译时定义其正在使用的对象的接口和类型定义。相反,后期绑定允许客户端在运行时发现并调用对象上的方法。IDispatch包括GetTypeInfo方法,该方法返回ITypeInfo接口。ITypeInfo具有可用于发现实现它的对象的类型信息的方法。
如果 COM 类使用类型库,客户端可以通过ITypeLib (从ITypeInfo-> GetContainingTypeLib获取)查询它以检索类型信息。此外,类型库还可以引用其他类型库以获取其他类型信息。
根据 Forshaw 的博客文章,WaaSRemediation引用了类型库WaaSRemediationLib, 而后者又引用了stdole(OLE 自动化)。WaaSRemediationLib使用了该库中的两个 COM 类,即StdFont和StdPicture。通过修改其TreatAs注册表项对StdFont对象执行COM 劫持,该类将指向我们选择的另一个 COM 类,例如.NET Framework 中的System.Object。值得注意的是,Forshaw 指出StdPicture不可行,因为该对象会执行进程外实例化检查,因此我们将重点放在使用StdFont上。
由于System.Object的GetType方法,我们对 .NET 对象很感兴趣。通过GetType, 我们可以执行 .NET 反射,最终访问Assembly.Load。虽然选择了System.Object,但此类型恰好是 .NET 中类型层次结构的根。因此,可以使用任何 .NET COM 对象。
在初始阶段设置完成后,HKLMSoftwareMicrosoft.NetFramework键下还有另外两个 DWORD 值需要使我们所认为的用例成为现实:
-
AllowDCOMReflection:正如Forshaw 所指出的,启用此值允许我们执行任意反射来调用任何 .NET 方法。通常,由于MS14-009中解决的缓解措施,DCOM 上的 .NET 反射会被阻止。
-
OnlyUseLatestCLR :使用 Procmon ,我们发现必须启用此值才能加载最新版本的 .NET CLR(版本 4),否则默认加载版本 2。
在确认可以在初步测试中加载最新版本的 CLR 和 .NET 后,我们就知道我们走在正确的轨道上。
从本地进程到远程计算机
将注意力转移到远程编程方面,我们首先使用远程注册表来操纵 .NetFramework注册表项值并劫持目标计算机上的StdFont对象。接下来,我们将CoCreateInstance替换为CoCreateInstanceEx,以实例化远程目标上的WaaSRemediation COM 对象并获取指向IDispatch接口的指针。
使用指向IDispatch的指针,我们调用GetTypeInfo成员方法来获取指向ITypeInfo接口的指针 ,该指针被困在服务器中。此后调用的成员方法在服务器端发生。在识别感兴趣的包含的类型库引用 ( stdole ) 并派生感兴趣的后续类对象引用 ( StdFont ) 后,我们最终使用ITypeInfo接口上的“可远程” CreateInstance方法来重定向StdFont对象链接流(通过之前的TreatAs操作)以实例化System.Object。
由于AllowDCOMReflection 已正确设置,因此我们可以通过 DCOM 执行 .NET 反射来访问Assembly.Load,从而将 .NET 程序集加载到 COM 服务器中。由于我们在 DCOM 上使用Assembly.Load,因此这种横向移动技术完全是无文件的,因为程序集字节传输由 DCOM 远程处理魔法处理。有关从对象实例化到反射的技术流程的深入解释,请参阅下图:
System.Object 类实例化流程
开发中的阵痛
我们的第一个也是主要问题是通过IDispatch->Invoke调用Assembly.Load_3。Invoke将一个对象参数数组传递给目标函数,而Load_3是Assembly.Load的重载,它接受一个字节数组。 因此,我们需要将字节的SAFEARRAY包装在另一个VARIANT的SAFEARRAY中——最初,我们一直尝试传递一个字节的SAFEARRAY。
创建 Object Byte 的非托管等效项
另一个问题是找到正确的Assembly.Load重载。辅助函数取自 Forshaw 的 CVE-2014-0257代码, 其中包括GetStaticMethod函数。此函数使用 DCOM 上的 .NET 反射来查找给定类型指针、方法名称及其参数计数的静态方法。Assembly.Load有两个静态重载,它们接受一个参数;因此,我们最终使用了一个黑客解决方案。我们注意到,带有一个参数的 Load 的第三个实例是我们正确的选择。
寻找正确的 Assembly.Load 重载
运营难题
我们观察到这种技术的最大缺点之一是,生成的信标的生命周期仅限于 COM 客户端;在本例中,我们的武器化二进制文件“ForsHops.exe”(当然,这个名字很优雅)的应用程序生命周期。因此,如果 ForsHops.exe 清理了它的 COM 引用或退出,那么在远程计算机的 svchost.exe 下运行的信标也会清理或退出。我们尝试了不同的解决方案,例如让我们的 .NET 程序集无限期地挂起其主线程,在另一个线程中执行 shellcode,并让 ForsHops.exe 让漏洞利用线程挂起,但没有什么优雅的方法。
.NET 加载程序主线程挂起,而 shellcode 在单独的线程中运行
在当前状态下,ForsHops.exe 会一直运行,直到信标退出,此时它会删除其注册表操作。还有改进的机会,但我们将其留给读者作为练习。
ForShops.exe 执行
Windows 2019 Server 上的信标成功
Beacon 在 PPL svchost 进程中运行
ForShops.exe 在信标退出后删除更改
防御建议
在 Mohamed Fakroud 发布其实施方案后,Samir Bousseaden( @SBousseaden )提出的检测指导也适用于这种横向移动技术:
-
检测WaaSMedicSvc的svchost.exe进程内的 CLR 加载事件
-
检测以下项的注册表操作(或创建):HKLMSOFTWAREClassesCLSID{0BE35203-8F91-11CE-9DE3-00AA004BB851}TreatAs (StandardFont CLSID 的TreatAs项)
此外,我们建议实施以下附加控制措施:
-
检测HKLMSOFTWAREClassesCLSID{0BE35203-8F91-11CE-9DE3-00AA004BB851}的 DACL 操作
-
在HKEY_LOCAL_MACHINESOFTWAREMicrosoft.NETFramework中查找启用的OnlyUseLatestCLR和AllowDCOMReflection值
-
启用基于主机的防火墙以尽可能限制 DCOM 临时端口访问
此外,利用以下概念验证 YARA 规则来检测标准 ForsHops.exe 可执行文件:
规则 Detect_Standard_ForsHops_PE_By_Hash
{meta: description = "Detects the standard ForShops PE file by strings" reference = "GitHub Project: https://github.com/xforcered/ForsHops/" strings:$s1 = "System.Reflection.Assembly, mscorlib" wide$s2 = "{72566E27-1ABB-4EB3-B4F0-EB431CB1CB32}" wide$s3 = "{34050212-8AEB-416D-AB76-1E45521DB615}" wide$s4 = "GetType" wide$s5 = "Load" wide condition: all of them}
结论
我们的实现稍微扩展了 Forshaw 博客中解释的 COM 滥用,利用捕获的 COM 对象进行横向移动,而不是本地执行来绕过 PPL。因此,它仍然容易受到与执行本地执行的实现相同的检测。
您可以在此处找到https://github.com/xforcered/ForsHops- ForsHops.exe 概念验证横向移动代码。
致谢
特别感谢 Dwight Hohnstein ( @djhohnstein ) 和 Sanjiv Kawa ( @sanjivkawa ) 对本研究提供反馈并提供博客文章内容审查。
资源
James Forshaw 的博客“Windows Bug 类:使用 IDispatch 访问被捕获的 COM 对象”:https://googleprojectzero.blogspot.com/2025/01/windows-bug-class-accessing-trapped-com.html
Mohamed Fakroud 的博客“滥用 IDispatch 来捕获 COM 对象访问并注入 PPL 进程”: https: //mohamed-fakroud.gitbook.io/red-teamings-dojo/abusing-idispatch-for-trapped-com-object-access-and-injecting-into-ppl-processes
Jimmy Bayne 的博客“滥用 COM 注册表结构(第 2 部分):劫持和加载技术”:https://bohops.com/2018/08/18/abusing-the-com-registry-structure-part-2-loading-techniques-for-evasion-and-persistence/
James Forshaw 的 CVE-2014-0257 概念验证:https://github.com/tyranid/IE11SandboxEscapes/blob/master/CVE-2014-0257/CVE-2014-0257.cpp
Samir Bousseaden 在 WaaSMedicSvc 检测中执行 .NET https://x.com/SBousseaden/status/1896527307130724759
原文始发于微信公众号(Ots安全):利用捕获的 COM 对象进行无文件横向移动
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论