0x00 前言
在ATT&CK框架中,持久性方面,攻击者使用计划任务,或者服务作为持久性的相关技术,但很少看到编号为T1546.015的COM组件劫持相关的利用。本文行文仓促,如有错误,请各位积极指正。
0x01 COM劫持
COM劫持是一种通过劫持COM引用和关系的一种持久性手段,COM劫持是通过修改注册表相关内容来实现替换合法COM组件的引用。
0x1.1 COM模型
Component Object Model 译为组件对象模型,是一种独立于语言的一种二进制操作模型,攻击者通常使用组件对象模型(即COM)来执行本地代码。由于COM组件是语言独立的。所以,这些接口经常通过各种语言调用来进行代码执行,以及无文件下载以及持久性等诸多隐秘的操作,而被滥用。通常通过指定CLSID(标识GUID)或ProgID(程序标识符)来获得COM对象。
在0x01 COM劫持一节中,简略的介绍了COM劫持的基本原理,即通过修改注册表相关内容来实现替换合法COM组件的引用。因为COM都通过CLSID标识,这些CLSID数据大多存储在注册表HKEY_CLASSES_ROOT\CLSID
,HKEY_CURRENT_USER\Software\Classes\CLSID
,以及HKEY_LOCAL_MACHINE\Software\Classes\CLSID
中。在CLSID中,和COM劫持相关的键主要有InprocServer32
和LocalServer32
。所以,从狭义上来说,COM劫持主要是通过修改InprocServer32
和LocalServer32
的值来实现的。
InprocServer32主要存储的是一个*.dll的路径,当实例化一个COM后,就会将InprocServer32存储对的Dll文件加载入实例化该COM的进程中,而LocalServer32存储的是一个.exe的路径,当实例化COM之后,便会以一个进程形式存在。
当查询CLSID的时候,windows首先会查找HKEY_CURRENT_USER\Software\Classes\CLSID
存储的CLSID,当该键不存在的情况下,也会依次查询HKEY_CLASSES_ROOT\CLSID
和HKEY_LOCAL_MACHINE\Software\Classes\CLSID
。
0x1.2 Hunting COM
在0x1.1中,简单的介绍了COM劫持的基本原理,现在的问题是如何寻找可以被利用的COM?一般的搜索COM有以下三个原则:
- 增加缺少的CLSID的路径
- 修改原有的CLSID加载的程序
- 修改原有的CLSID加载的路径
在Persistence – COM Hijacking这篇文章中,简略的介绍了如何搜寻可以被利用的COM组件。
通过Persistence–COM Hijacking的描述,首先将ProcMon设定为
- 操作是RegOpenKey
- 结果是NAME NOT FOUND
- 路径以 InprocServer32 结尾
通常,劫持一些系统组件/进程实例化的一些COM,能更好的持久化,也能增加被劫持的几率。这里我选择Explorer.EXE进程。Explorer.EXE是一个常见的系统进程,其作用是管理windows系统的相关资源,通过多次的尝试,对UI的一些操作可能是Explorer.EXE通过COM来实现的。
··
通过ProcMon筛选的结果,可以选择{B41DB860-8EE4-11D2-9906-E49FADC173CA}
这个COM服务器进行劫持。可以看到注册表HKCU\Software\Classes\CLSID\
下并不存在{B41DB860-8EE4-11D2-9906-E49FADC173CA}
这样的键
然后在HKCU\Software\Classes\CLSID\
下新建上述的CLSID,并设置InProcServer32的路径为COM注入的dll的路径,如下图。此处为了演示效果,仅仅进行一次弹窗而已。
在下一次重启之后,便可触发COM劫持实现驻留。
但是,COM劫持的另一个大原则是不能干扰原进程的正常运行,看一个例子,同样的针对Explorer.exe进程,当我打开我的计算机的时候,可以看到先是读取了HKCU\Software\Classes\CLSID\{11DBB47C-A525-400B-9E80-A54615A090C0}\InProcServer32
。同样的,新建一个有个CLSID键。虽然在打开我的电脑会被劫持。但是会出现这样的情况。所以这不是一个好的劫持对象。
在Persistence – COM Hijacking还提到了工具acCOMplice,可以寻找缺失COM服务器的CLSID,但是在验证这个的时候发现。在部分的CLSID的LocalServer32或者InProcServer32键中可以不存在相关的值。例如{00020812-0000-0000-C000-000000000046}
。
可以使用如下的脚本遍历注册表中不存在路径的CLSID
在Hunting COM 这篇文章中,展示了利用powershell实例化COM对象的例子。主要是[activator]::CreateInstance([type]::GetTypeFromCLSID($Clsid))
进行实例化的。当修改注册表的InprocServer32的路径之后,这种COM 是很难被触发的。但是可以通过上述手动触发。
当在win7(x64)或者win10下,按照上述的操作,发现并不能进行COM劫持。并抛出HRESULT:0x800700C1的异常。通过https://stackoverflow.com/questions/50519154/net-4-0-winform-could-not-load-file-or-assembly-or-one-of-its-dependencies-i发现,这是由于自身编译的.dll文件是32位的,但是系统却是64位的,当编译出x64的dll之后,便可触发。
0x1.3 如何利用 COM Hiject
0x1.4 远程COM Hiject
bohops在滥用 COM 注册表结构一文中,利用$inproc = gwmi Win32_COMSetting | ?{ $_.LocalServer32 -ne $null }
来枚举可能造成文件缺失的“LocalServer32”路径,这给我以启示,可以利用Get-WmiObject等WMI方法来枚举所有的COM。
如下,是Find-MissLibraryComByGet-WmiObject可以远程获取可能被劫持的LoacalServer32和InprocServer32。利用如下WQL语句Query = "Select Name From CIM_DataFile Where Name = '$RemoteFilePath'"
判断文件是否存在。由此可以劫持文件不存在的COM。
在CLSID下还有一个特殊的键,名为“TreatAs”,其作为CLSID的一个软连接,当实例化一个含有TreatAs键的COM时,首先会读取原CLSID的数据,如果存在TreatAs键的时候,便读取TreatAs键的值,然后读取第二个CLSID,根据情况加载第二个CLSID的exe或者dll。
如此的话,此时通常会有两个攻击面,第一,可以修改TreatAs的值,从而将其劫持到一个非法的CLSID。第二,可以劫持TreatAs所对应的CLSID。
当检索系统中可能存在劫持可能的CLSID之后,就可以通过修改注册表实现COM劫持。WMI也存在修改注册表的方法。WMI的StdRegProv类提供了多个读取,遍历,修改注册表数据的函数。
使用 GetStringValue
函数获取键值的值
使用CreateKey
函数创建键
使用SetStringValue
函数设置键的值
使用EnumKey
检索项下面所有键
如下通过远程设置注册表来COM劫持
最后一步,可能就是触发COM劫持,一般而言,实例化COM是加载CLSID的二进制文件的原因,所以在本地通常使用CreateInstance实例化CLSID,我以为可以使用[activator]::CreateInstance([type]::GetTypeFromCLSID($Clsid,$ComputerName))
进行远程实例化,但是,实际上这是不可以的。因为这些CLSID并不是DCOM。而是普通COM,其并不具有远程实例化的能力。
所以,我只能把其映射到远程计算机本地去处理,在WMImplant中,我曾经修改过其代码,以便其能远程执行代码。其原理是利用Win32_Process类的Create函数创建带参数的powershell进行远程执行代码。
0x02 COR_PROFILE
COR_PROFILER是.NET Framework提供的一项监视或者调试CRL托管代码,这是一个.dll文件,并在运行的时候由CLR进行加载。具体技术细节可以参考微软的相关技术文章。https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/profiling/profiling-overview
COR_PROFILE通常使用环境变量注册COR_PROFILE,在.NET Framework 4之前,将COR_PROFILER设置一个CLSID,当创建.NET进程后,就会读取该CLSID下设置的COM服务器,并加载对应的二进制文件,在.NET Framework 4之后,只需要设置COR_PROFILER_PATH为需要加载dll的路径,那么就会屏蔽COR_PROFILER设置的COM。
因为使用powershell设置环境变量,仅仅修改的是副本,也就是说,修改的环境变量会随着这次powershell进程的退出而消失。并且,通过powershell设置环境变量而注册的dll,必须设置上述三个变量才可以生效。