本帖最后由 sandalwood 于 2022-4-3 11:14 编辑
什么是COM什么是COM,说白了,就是一堆功能相关的interface,它是某种语言向另一种语言暴露功能的最大单位。COMcomponent(COM组件)是微软公司为了计算机工业的软件生产更加符合人类的行为方式开发的一种新的软件开发技术。在COM构架下,人们可以开发出各种各样的功能专一的组件,然后将它们按照需要组合起来,构成复杂的应用系统。由此带来的好处是多方面的:可以将系统中的组件用新的替换掉,以便随时进行系统的升级和定制;可以在多个应用系统中重复利用同一个组件;可以方便的将应用系统扩展到网络环境下;COM与语言,平台无关的特性使所有的程序员均可充分发挥自己的才智与专长编写组件模块。COM的最核心的思想,说白了就是要做个跨语言的 “class” “object” “function” 。 什么是CLSID(不重要)当初微软设计com规范的时候,有两种选择来保证用户的设计的com组件可以全球唯一(其实就相当于COM的唯一ID):第一种是采用和Internet地址一样的管理方式,成立一个管理机构,用户如果想开发一个COM组件的时候需要向该机构提出申请,并交一定的费用。 第二种是发明一种算法,每次都能产生一个全球唯一的COM组件标识符。第一种方法,用户使用起来太不方便,微软采用第二种方法,并发明了一种算法,这种算法用GUID(Globally Unique Identifiers)来标识COM组件,GUID是一个128位长的数字,一般用16进制表示。算法的核心思想是结合机器的网卡、当地时间、一个随即数来生成GUID。从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义上)3240年不重复。 GUID的例子:54BF6567–1007–11D1–B0AA–444553540000HKEY_CLASSES_ROOT\CLSID{002B9E07-2E10-438F-AF1E-40E6A96F1EE4}在微软的COM中GUID和UUID、CLSID、IID是一回事,只不过各自代表的意义不同: • UUID :代表COM • CLSID :代表COM组件中的类 • IID :代表COM组件中的接口 在程序中,实际对象数据对应的处理程序路径string往往不尽相同,比如有的放C盘有的D盘,微软想出了一个解决方案,那就是不使用直接的路径表示方法,而使用一个叫 CLSID的方式间接描述这些对象数据的处理程序路径。CLSID 其实就是一个号码,CLSID 的结构定义如下:
COM和注册表的关系 通常windows内建com已经在注册表内存储着相关信息,而自定义com需要创建注册表入口点告诉windows com组件服务器在上面位置,我们可以在HKEY_CLASSES_ROOT\CLSID{clsid}位置找到所有windows已注册的com组件。 注册后com通过GUID唯一标识符来寻找并使用这个com组件,理论上每一个GUID都是唯一的,GUID在标识不同的对象时会有不同的称呼,标识类对象时称之为CLSID(类标识符)、标识接口时被称为IID(接口标识符)。 在每一个注册的clsid表项中都包含一个名为InprocServer32或LocalServer的子项,该子项内存有映射到该com二进制文件的键值对,操作系统通过该键值对将com组件载入进程或另起进程。(进程内组件和进程外组件,二进制代码的表现形式为dll(内)和exe(外))。 简单的理解就是,我们编写好一个COM组件,都需要注册到注册表中(也可以设置不用注册的COM组件,但是一般都是使用的注册方法),这样当调用COM组件的这个功能的时候,程序会进注册表进行读取相应位置的DLL或者EXE,加载到进程还是线程中,供我们使用。 COM注册的位置一般在以下地方,执行时是通过CLSID从上往下找,找到就停止查找直接执行。 - HKEY_CURRENT_USER\Software\Classes\CLSIDHKEY_CLASSES_ROOT\CLSID ps:这个其实就是HKEY_LOCAL_MACHINE\SOFTWARE\Classes的映射HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\<font color="rgb(0, 0, 0)"><font face="" "=""><font style="font-size: 16px">而Inproc</font></font></font><font color="rgb(0, 0, 0)"><font face="" "=""><font style="font-size: 16px">Server32和LocalServer32(以及InprocServer和LocalServer)这两个键值是COM服务(例如DLL、CPL、EXE和OCX)的引用点,也就是COM具体调用的dll和exe的绝对路径</font></font></font>
复制代码
如图,这里就是通过注册表去看这个COM的具体信息。这里COM的CLSID为{BF87B6......},它具体的实现是在qcap.dll中。
同样的,我们也可也用powershell命令去看系统注册了的COM组件。 - Get-WmiObject Win32_COMSetting
复制代码
自己实现一个COM(理解COM到底是什么,有什么用) 为了更好的理解COM到底是个什么玩意,这里我们自己实现一个COM然后注册到注册表中,再跨语言调用它。 编写com这里用的是VS2019 新建一个ALT项目,按照下面的流程走。
这里我选择的dll文件,exe总感觉有点碍眼。
到这里的时候,我们就获得了两个项目,然后不需要管下面那个。
然后向主项目添加一个新的ATL对象。
短名称随便写,然后其他地方会自动填充,然后ProgID需要设置为项目名称.短名称,然后直接点完成即可。
此时在项目下会自动添加 Temp.h 以及 Temp.cpp以及生成一个CTemp类,接着我们就可以创建我们自己的方法了。 在Temp.h头文件中给Ctemp这个类加函数声明。
然后再到Temp.cpp文件中实现这个函数的具体功能。这里我另函数Number实现的功能是输入一个数字,返回其平方(这里随便加了个弹计算机操作)。 - STDMETHODIMP CTemp::Number(LONG __num, LONG* __result){*__result = __num * __num;system("calc");return S_OK;}
复制代码
然后定义好方法后去编辑comtest.idl,然后找到ITemp : IDispatch这个接口,在里面写入方法接口。
com的clsid也在这个文件中,如下,这个是随机生成的。
最后就是编译生成了。出现这个报错不用管,这里是前面我们在创建项目的时候取消了注册选项。(如果选择了这个选项,这个com就会注册在你本机中,这不是我们想要的)
注册COM
使用regsvr32去将编译得到的COM注册的注册表中。PS:如果报错的话,请提升权限运行。 - regsvr32.exe -i C:\Users\momo\Desktop\comtest.dll #这是注册regsvr32.exe
复制代码
然后我们的com就成功注册了,在注册表中也能找到它的存在。(这里我是通过COM name去找的,也可以通过CLSID去找)
多种方式调用COMvbs调用这里首先用COM Name去创建了一个com对象,然后再调用这个对象里面的Number方法,其实就是我们前面在编译时候写入的方法。返回输入的平方。 - set com=CreateObject("ComTest.Temp")dim numnum=com.Number(2)msgbox num
复制代码
powershell直接调用 powershell调用是最简单的,没什么好说的。 - [activator]::CreateInstance([type]::GetTypeFromCLSID("169fcde5-b131-4fa7-bd67-d738152cc4e5")).Number(2)
复制代码
java调用COMhttps://www.jianshu.com/p/bf6841ccd2d5 COM调用分析这里我们可以使用火绒剑去监听,动作设置为REG_openkey,路径设置为InprocServer32或者LocalServer32。然后调用这个组件,就能监听到它的调用过程,当然,傻傻的去翻也不是不行。 如图,这里我们就监听到了它open的键,进而追踪找到了它的InprocServer32值。
COM劫持的原理和实现 前面讲了COM和注册表直接的关系,而COM劫持也正是基于这个关系的,我们知道,系统在查询COM对象的时候是从注册表的三个位置从上往下找的,所以COM劫持就是在这个读取过程中进行劫持。 首先,这里编写一个用于劫持的dll,老样子,弹calc PS:需要注意的是,在以下所有过程中,如果想要上线cs的话,是不能直接用cs生成的那个dll的,需要自己去用shellcode重新编译生成一个,这里不会的话,可以看我以前发的那个CVE-2021-1675的文章中免杀dll的生成。
注册表查询顺序提前劫持原理一种常见的COM劫持方法是在程序读取注册表信息中的DLL或者EXE功能的路径上,做一个拦截,让程序提前读取我们的设置好的恶意DLL或者EXE。COM劫持原理在某种程度上近似于DLL劫持。(和dll劫持一样,读取的顺序有先后,我们在上游做个拦截) Windows系统中应用程序读取COM注册表信息的顺序如下: 通常情况下,com不会注册到这里的HKEY_CURRENT_USER\Software\Classes\CLSID,所以我们一般就是劫持做在这里。提前在这个注册表位置做跳转,这样当COM执行时,就会访问到这里不再往下,直接开始执行我们的恶意dll和exe。 - #这里需要注意的是,HKEY_CLASSES_ROO\下的键是从从HKLM_LOCAL_MACHINE\SOFTWARE\Classes\映射过去的
复制代码
所以当使用进程监控查看的时候,你可能会发现这个com调用的路径是是上述中的一种,但其实他们的本质都是一样的- HKEY_CURRENT_USER\Software\Classes\CLSID HKEY_CLASSES_ROOT\CLSID or HKLM_LOCAL_MACHINE\SOFTWARE\Classes\CLSID HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Objects\ <font color="rgb(0, 0, 0)"><font face="" "="">[size=1.125em]<b>实现</b></font></font>
复制代码
新建一个键 HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID{169fcde5-b131-4fa7-bd67-d738152cc4e5}\InprocServer32,然后修改值为我们的dll。 - reg add "HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{169fcde5-b131-4fa7-bd67-d738152cc4e5}\InprocServer32" /d "C:\Users\administrator\Desktop\新建文件夹\dll远程创建弹calc.dll" /t REG_SZ /f
复制代码
此时,通过火绒剑看到它的调用是直接走HKCU这个注册表的。
InprocServer32和LocalServer32键值修改或文件替换 原理因为系统是通过这两个键值将com组件载入进程或另起进程的。所以另一种劫持手段就是直接替换相应目录的文件或修改其原本的InprocServer32和LocalServer32键值,使其指向我们的恶意dll和exe。
实现直接改目标键值的指向或者覆盖目标dll即可。 - reg add "HKEY_CLASSES_ROOT\CLSID\{169fcde5-b131-4fa7-bd67-d738152cc4e5}\InprocServer32" /d "C:\Users\administrator\Desktop\新建文件夹\dll远程创建弹calc.dll" /t REG_SZ /f
复制代码
找到废弃的二进制引用(实用性不高) 原理随着系统在使用过程中的各种应用安装卸载,而有些应用如果卸载不全面,比如在安装的时候注册了自己的com,卸载的时候没卸载这个com,只把文件给删了,就可能遗留下注册了的COM组件,但其LocalServer32/InprocServer32指向缺是空的情况。我们就可以扫描出设备本身缺少的或者遗留的空COM组件路径,然后放置我们自己恶意的文件。 而缺少对二进制路径的引用的COM类的数量取决于一些因素,例如: 操作系统版本,家族和功能; 第三方软件(已安装/卸载后); 先前的DLL/COM注册。
实现但是其实这里有个很重要的问题,这种情况下的不好利用,因为这些被弃用的COM大概率是不会再被执行调用了,所以用来做权限维持不太理想,但万一呢,哪天用户又把组件下回来。 如图,执行了该powershell,我们可以看到,我们本机确实有不少被弃用的组件,但是其在注册表中的注册信息没被删除。
COM在权限维持中的应用设置计划任务通过powershell或vbs脚本来调用自己注册的恶意COM这种操作会比普通 IEX下载更迷惑,防守方如果不懂的话还可能没法进行下一步的排查分析。(缺点,还是会有蓝框弹出,需要注意,在这里不能用双引号,必须上单引号)
利用现有任务进行劫持(隐蔽)原理首先,这里我们需要了解计划任务的触发器执行操作。计划任务并非只能设置运行exe等可执行文件,它还能设置COM,如下图,在微软文档中声明了计划任务的Action存在四种操作,但后面两种弃用了。 这里我们就是劫持这些Action操作为COM的计划任务。 但通过taskschd.msc的管理控制器,只能新增一些EXEC的任务,其他任务要么弃用,要么就没显示在操作里面。 无法自己自定义一个Action为ComHandler的任务。 https://docs.microsoft.com/zh-cn/windows/win32/taskschd/taskschedulerschema-comhandler-actiongroup-element
首先,我们分析一个Action为ComHandler的任务,这里以Microsoft\Windows\CertificateServicesClient\UserTask为例子。 我们发现,在控制台这边看它的操作选项是一个自定义句柄,在这里完全无法分析。
所以,这里还要再讲一个知识点,计划任务其实是以xml文件存储在C:\Windows\System32\Tasks目录下的,所以UserTask这个计划任务的xml文件路径其是就是C:\Windows\System32\Tasks\Microsoft\Windows\CertificateServicesClient\ 然后我们可以查看该计划任务的具体XML来获取更多信息,使用schtasks query语句或者直接翻找指定路径的文件都可以,比如这里,我们就能看到该计划任务的Action不是EXEC,而是ComHandler,即调用COM组件。 从下面的XML中我们可以看到Triggers标签处就是触发器,其中有LogonTrigger,说明是存在登录触发的。而操作Action这个字段下的是ComHandler字段(如果是执行exe的计划任务,这里的字段显示是EXEC),说明它是调用COM组件的,并且下方给出了CLassId,即类ID。也就是说,它执行的COM组件是58FB....这个组件。 所以到这里,我们就已经有两种思路进行任务的劫持了。
枚举可用于COM劫持的计划任务这里我们可以通过大佬的脚本去枚举可被劫持利用的COM组件。 https://github.com/enigma0x3/Misc-PowerShell-Stuff/ Import-Module .\Get-ScheduledTaskComHandler.ps1Get-ScheduledTaskComHandler脚本可以检查主机上所有在用户登录时执行并且容易受到COM劫持的预定任务。 这里主要是找那种Action设置为Custom Handler的计划任务。
参数“PersistenceLocations”将检索易受COM劫持的计划任务,这些任务可用于持久性且不需要提升的特权。 Get-ScheduledTaskComHandler -PersistenceLocations
注册表提前劫持(懂com劫持的很容易被发现)这里做一个提前劫持,然后重新登录用户。如图,成功劫持!!(如果劫持了但是没执行dll,就要考虑下这个计划任务的启动权限是否有权限去读我们的dll所在的目录了)
直接修改目标的dll指向(隐蔽且很难排查,但需要权限)
因为提前劫持这种操作比较容易被发现,所以我们还可以直接修改目标的dll指向。但这里有个问题,这些Action为Comhandler的计划任务调用的全是系统内置COM,他们在注册表中的修改权限都是trustedinstaller,其他用户都只有读取权限。
这里有两种思路去修改。
①手动操作,修改注册表的所有者为当前用户。缺点:需要3389才能修改。
这里通过手动将目标注册表的权限改成administrator了,然后就可以修改指向dll,做我们想做的事了。
②利用备份还原特权去修改。没有缺点
如图,成功修改注册表,直接上线。
自己生成一个计划任务,调用自己注册的COM(还没实现)有时可能系统自带的服务,他的触发器不是我们想要的,所以我们可能需要自己去创建一个任务,
然后随便调用一个系统存在的COM,再进行劫持。(或者直接就是调用我们自己注册的恶意COM)
这里没实现,并且已经排除了权限问题和dll问题。
猜测是计划任务中要的COM是注册在其他地方的,或者是计划任务中对Comhandler中的值做了签名校验,非微软签名的com没法做计划任务。有空再研究研究。
其他想法1.这里如果要劫持系统自带的COM任务的话,可以在以下链接中可以找感兴趣的去劫持。
https://chentiangemalc.wordpress.com/2011/05/08/windows-7-default-scheduled-taskscomplete-overview/
2.可以劫持常用的程序,如微信、qq、火狐中调用的COM(这些软件可能自己注册了COM,就可以劫持他们)。
|