首页
社区
课程
招聘
[原创]从CVE-2015-1642到Office ActiveX控件堆喷探究
发表于: 2019-3-14 16:06 10737

[原创]从CVE-2015-1642到Office ActiveX控件堆喷探究

2019-3-14 16:06
10737

笔者最近重新研究了一下 officeActiveX 控件堆喷射的相关细节,在此过程中对遇到的一个cve-2015-1642 poc进行了复现,在本文的最后,笔者探索了 ActiveX 控件堆喷射的部分原理,提出了一种比较简单的 ActiveX 控件堆喷射检测思路,实践证明该方式可以准确检出这类堆喷射。

由于笔者能力有限,不足之处还请读者斧正。

cve-2015-1642 被公布时为在野利用状态(0day)

漏洞细节首先是由 @yongchuank2015.8.17 公布的,具体见:

https://labs.mwrinfosecurity.com/advisories/microsoft-office-ctasksymbol-use-after-free-vulnerability/

随后,NCC Group 公司的 @d0mzw2015.10.30 公开了这个漏洞的进一步细节,并在文章内详细讨论了 office 内存破坏漏洞借助堆喷射进行利用的具体细节,作者还仔细讨论了office堆喷射的许多细节和通用利用编写方式。

https://www.nccgroup.trust/uk/our-research/understanding-microsoft-word-ole-exploit-primitives/

接着,玄武实验室的 Danny__Wei2015.11.28 在自己的博客上公开了这个漏洞的poc,公开的poc是一段 C# 代码,poc构造思路基本遵循了 NCC Group 的文章。

http://www.cnblogs.com/Danny-Wei/p/5003302.html

笔者最近对 officeActiveX 控件堆喷射的细节进行了进一步研究,研究过程中再次遇到上述材料,因此决定复现一下相关poc。

office UAF 漏洞的利用是比较少见的,因为重用内存时占位的时机不好控制。这个漏洞触发后恰好有一段时机可以用来占位,所以值得研究一下。这篇文章还解决了一个问题:可以借助字符串申请申请任意大小的内存块,这在UAF利用时是非常有用的,一般产生UAF时,相应的对象大小往往比较小,所以如何在 office 内进行任意大小内存的申请也就显得尤其重要。

office 下的堆喷射技巧,历史上有若干漏洞样本都曾采用过。例如 cve-2013-3906/cve-2015-1641/cve-2015-1642/cve-2016-7193/cve-2017-11826,有资料表明 cve-2015-2424 这个0day也用到了堆喷射,这一点需要进一步考证。

上述几个借助堆喷射的漏洞,其漏洞类型分别如下:

cve-2013-3906 整数溢出

cve-2015-1641 类型混淆

cve-2015-1642 UAF

cve-2016-7193 数组越界写

cve-2017-11826 类型混淆

这里我们来研究一下 cve-2015-1642,笔者遇到的第一个问题是如何编译 Danny__Wei 的poc代码,这看着是一段 C# 代码,在经过若干探索后,笔者用 VS2010 新建了一个 C# 的窗体应用程序,如下:

随后在窗口上添加一个按钮控件:

双击按钮,即来到了 button1_Click 函数中,此时将 Danny__Wei 的代码全部拷贝进来即可,拷贝完代码后,会有些对象类型不认识,此时我们需要添加对 MSCOMCTL.OCX 动态库和 Microsoft.Office.Interop.Word.dll 动态库的引用,具体的方法如下:

笔者的电脑装有若干版本的VS,所以这两个动态库可以通过 Listary 等工具搜到,将其拷贝到工程目录下,选中后点击确定即可,成功引入后,可以看到引用列表里面多了如下两个库:

最后添加所需的头文件即可:

此时我们就可以编译生成该poc了(确保你的机器上装有 office,笔者的机器上装的是 office 2010)。

编译完成后,双击 .exe 文件,出现一个窗口,点击上面的按钮就可以生成poc了(test.docx):

此时这个 test.docx 还不是一个漏洞样本,它只带有堆喷射和 0x60 大小的内存占位功能。关于如何在此基础上构造一个 cve-2015-1642 的漏洞样本,@d0mzw 在他的文章里面写的很清楚:

笔者在此思路上用 7z 打开了生成的 docx 文件,并将 activeX1.xmlclsid 换成了漏洞的 clsid:44F9A03B-A3EC-4F3B-9364-08E0007F21DF。此时我们就有了一个“理论上”可以劫持 eip0xC0DEC0DEcve-2015-1642 样本。

为什么说“理论上”呢?因为读者如果自己实验的话,就会发现这个poc很不稳定,构造的样本在没有打补丁的环境中 crash 是没有问题的,但是笔者试了几次后发现我们并未劫持到 eip

接下来我们通过调试并改造poc代码来初步实现对eip的劫持,本文不讨论利用编写,利用编写请参考笔者的另一篇文章:

https://bbs.pediy.com/thread-221792.htm

我们先删除堆喷射部分,构造一个只触发 cve-2015-1642 的样本,看一下崩溃现场是否和相关文章里面描述的相同(当然,如果读者比较懒,那么VT也有一个现成的 cve-2015-1642 crash poc,有条件的可以自己下载,md5: 8b7d1680d8aeb1d0d822ee33777671ab)

笔者调试时某次的 crash 现场如下,可以看到这是一个典型的UAF,且 crash 现场和 @d0mzw 的基本一致(请注意 @yongchuank 的crash现场并不是这里,笔者在 office 2013 下开启页堆后的崩溃现场依然是下面这个,读者请以实际调试的情况为准)

现在笔者有一个问题:我不知道被释放前 eax 对象的内存大小为多少。我们来下个断点看一下:

到这里,我们已经知道被释放的对象大小为 0x60.

现在,让我们来整理一下思绪,这个UAF如果要成功利用,所构造的文档在执行是需要满足哪些最简条件?

我们先来检视一下 @d0mzw 文章中提供的思路:

我们再来看一下 Danny__Wei 代码中对上述思路的实现,可以发现如果实际执行顺序为从下到上的话,和上述文章的思路完全一样:

顾名思义,DefragmenHeap 函数的作用是整理内存,实际内存申请时可能申请 0x60 大小的内存,占用的是稍微大一点的内存块,这个函数的作用就是把那些内存块先使用完,从而让系统分配精准的 0x60 大小的内存块,以便提高占位的有效性。

到这里,笔者脑海里的问题是:

我们借助调试器来探究一下上述问题。

要回答第1个问题,我们需要清楚以下几点:

笔者先回答a:显然,tabArrayB[j].Buttons.Add().ToolTipText = objAllocB/objAllocB/chunk; 这几处代码执行时存在一个统一的内存申请点。其次,每个 ActiveX[x].bin 文件在被映射到 office 进程空间时,还有一次统一的内存申请点,笔者将这部分的讨论放到本文最后。

关于 objAllocB/objAllocB/chunk 的申请,我们可以在堆喷射完的 winword 进程找一个对应堆块的指针,在只开启堆分配用户态栈回溯( +ust )的情况下可以观察到如下输出:

再回答b:借助上面得到的信息,笔者下了如下断点进行观察:

从上面的日志可以观察到:实际执行时 chunk 块(fffe0)的内存是最先申请的,随后是 DefragmenHeapReplaceHeap 中对 objAllocA/objAllocB 的申请,在不考虑字符串内容的情况下,这两个函数的执行顺序笔者并不关心。实际测试发现把 DefragmenHeap 函数中的 objAllocB 内存申请数量进一步增加,将 ReplaceHeap 函数删除,也可以实现正常占位。

接下来我们通过调试器来回答第2个问题:这个UAF在释放内存和重用内存之间的时间差够大吗?利用有足够的时间在这个时间区间内占用被释放的 0x60 大小的内存吗?

既然是UAF,调试时必须定位到 Free 在哪里?Reuse 在哪里?被释放的 0x60 字节内存是否成功被利用代码中申请的对象成功占位。

Danny__Wei 提供的poc在笔者的环境中并不能顺利完成占位,我们来借助调试器看一下究竟发生了什么:

根据上述观察,笔者推测利用代码中对 0x60 大小的字符串申请次数太少,于是笔者将 DefragmenHeap 函数的代码做了略微修改,如下:

同时在 button1_Click 函数进行了如下更改:

再次生成样本,笔者这次替换 activeX34.xml 中的 clsid 为漏洞 clsid,再次打开样本,在调试器中观察如下:

最后回答第3个问题,根据上面的代码已经知道,笔者在 DefragmenHeap 函数中一共用了80个大小为0x60字节的字符串去占位被释放的对象。这个视环境不同而异,读者可以按需自行调整。

通过上述调试,笔者成功借助公开资料和调试器劫持了 cve-2015-1642 样本的控制流,在此基础上就可以写出这个漏洞的 RCE 利用。这个漏洞比较好的一点就是我们可以在UAF之间对内存进行成功占位,对 office 来说这类可以成功利用的 UAF 还是比较少的。

在本文的最后,笔者单独讨论一下 office ActiveX控件堆喷射时的内存申请细节,这个问题实际上涉及一个更一般的问题:如何准确检出 office 堆喷射样本?也即,如何在动态执行过程中对 cve-2013-3906/cve-2015-1641/cve-2015-1642/cve-2016-7193/cve-2017-11826 这类漏洞样本的堆喷射行为进行准确标定。

每个 ActiveX[x].bin 文件在被映射到 office 进程空间时,有一处统一的内存申请点。

这里笔者以手头的某个自己构造的 cve-2015-1641 样本为例,为方便起见,我将负责堆喷射的 docx 文档单独抽取出来,抽取的 docx 文档内用1个 activeX1.bin 文件外加 40activeX[x].xml 文件进行堆喷射。

关于这部分的更多细节可以参考这篇文章:

https://www.greyhathacker.net/?p=911

其中 activeX1.bin 的大小为 0x20500 字节:

以下调试环境仍为 windows7 sp1 x86 + office 2010 + windbg

笔者在调试器中( +ust )将文档打开,查找满足上述大小的堆块:

笔者在上述栈回溯信息中注意到了两个函数:ole32!CMemBytes::WriteAtole32!CMemBytes::SetSize。我们用 IDA 定位到 mso.dll(14.0.1063.1000) 中的相应调用点来看一下:

我们来看一下 ole32!CMemBytes::WriteAt 的伪代码,其中灰色调用处调用了 ole32!CMemBytes::SetSize 函数:

我们再来看一下 ole32!CMemBytes::SetSize 的伪代码,可以看到里面确实调用了 GlobalReAlloc 函数。

到这里,笔者有若干问题:

我们先来探索第1个问题。

我们对 mso.dll 的上述调用点下断点,看一下每次调用的具体函数是什么:

通过日志可以观察到这个地方每次调用的都是 ole32!CMemBytes::WriteAt 函数。

分析到此处,笔者突然记起自己之前写过的一篇文章,里面谈到了在 office 2010 下借助 msxml6.dll 的符号打印解析的xml标签的断点,相关文章见:

https://www.anquanke.com/post/id/103080

我们来看一下 ole32!CMemBytes::WriteAt 函数是在解析到哪个标签时被调用的:

可以看到正是解析到 ax:ocx 标签后,开始调用 ole32!CMemBytes::WriteAt 映射 ActiveX 控件的内存。

接下来看一下第2个和第3个问题,我们来看一下 ole32!CMemBytes::WriteAt 的声明:

再次下断点,在函数调用前输出参数看一下:

可以观察到每次调用 ole32!CMemBytes::WriteAt 函数时待写入数据的大小(cb)都为 0x1000,偏移(ulOffset)则按 0x1000 的顺序递增。由此笔者合理推断 ActiveX 控件在被映射到内存时,是按每次 0x1000 的大小被写入的,所以调用 ole32!CMemBytes::WriteAt 处应该位于一处循环内,我们看一下相关调用点的伪代码,果然如此:

现在让我们在第1次调用 ole32!CMemBytes::WriteAt 时断下,看一下写入的数据是什么?

可以看到写入的数据正是ole头部,并且在随后写入了 ActiveX[1].bin 控件的其余数据。

分析到这里,笔者想知道目的地址在哪里,既然有一个 ulOffset,那么肯定有一个 base,我们能通过上述数据得到 base 吗?答案是肯定的。

ole32!CMemBytes::WriteAt 函数中,调用完 ole32!CMemBytes::SetSize 后,有一处 memcpy 操作,这里就是在往新扩大的 0x1000 内存拷贝数据,其中第一个参数就是通过 base[offset] 来寻址的,笔者在寻找的就是这个 v5, 可以看到 v5 是通过如下调用得到的:

看来 v5 是间接通过 CMemBytes 对象指针得到的,我们来看一下 GlobalLock 函数的实现:

可以看到在 GlobalLock 函数中,返回的值其实就是对传入的参数做了一次解引用,原来如此。

现在我们还需要搞清楚 CMemBytes 对象及其相关对象的结构体,幸运的是 IDA 已经给出了相关的结构体定义:

我们的目的就是通过 CMemBytes 对象指针去解析到 v5 对应的地址,所以我们可以将断点修正如下:

上面的日志是一次完整的从 offset=0offset=0x20500 的过程(中间省略了大量重复日志),笔者注意到 base_addr 地址在中间发生过改变,笔者猜测一开始的地址在内存增长过程中可能大小不过,于是将之前已拷贝的数据拷贝到了一块更大的内存,并继续增长,这应该和 GlobalReAlloc 函数的实现有关,这里不再深究。

分析到这里,原则上笔者可以写出一个监控 office ActiveX 堆喷射总内存申请大小的实时 windbg 脚本,但是实际使用时发现带有伪寄存器的脚本执行的速度太慢,该脚本仅供参考。

由于笔者的日常工作涉及到沙箱检测技术的开发,所以我试着将上述调试结论融入沙箱的检测框架。通过累加堆喷射内存大小,并与预先准备的阈值(以下为75MB)进行比较,从而判断一个 office 样本是否有 ActiveX 控件异常堆喷射行为。笔者对 cve-2013-3906/cve-2015-1641/cve-2015-1642/cve-2016-7193/cve-2017-11826 常见攻击样本进行检测,获得了非常好的检出效果。

这里笔者给出若干漏洞的样本以及对应的检出日志,供读者参考:

由于 office 中一定还存在其他内存破坏漏洞,笔者预计未来仍然会有类似的利用手法出现,这些利用在针对旧版本 office(office 2007/office 2010) 的攻击中还是非常稳定的。

由于微软 EMETHeapSpray 检测方式只是简单的占坑,在新样本的检测中并没有非常好的效果。因此笔者在本文最后提供了一种较为简单的检测 office 堆喷射检测方式,供读者参考。

《Microsoft Security Bulletin MS15-081 - Critical》
https://docs.microsoft.com/en-us/security-updates/securitybulletins/2015/ms15-081

《Microsoft Office CTaskSymbol Use-After-Free Vulnerability》
https://labs.mwrinfosecurity.com/advisories/microsoft-office-ctasksymbol-use-after-free-vulnerability/

《Understanding Microsoft Word OLE Exploit Primitives: Exploiting CVE-2015-1642 Microsoft Office CTaskSymbol Use-After-Free Vulnerability》
https://www.nccgroup.trust/uk/our-research/understanding-microsoft-word-ole-exploit-primitives/

《CVE-2015-1642 POC》
http://www.cnblogs.com/Danny-Wei/p/5003302.html

《Spraying the heap in seconds using ActiveX controls in Microsoft Office》
https://www.greyhathacker.net/?p=911

《结合一个野外样本构造一个cve-2016-7193弹计算器的利用》
https://bbs.pediy.com/thread-221792.htm

《Open XML标签解析类漏洞分析思路》
https://www.anquanke.com/post/id/103080

 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-3-26 13:07 被银雁冰编辑 ,原因:
上传的附件:
收藏
免费 6
支持
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  Editor   +2.00 2019/03/15 精品文章~
最新回复 (2)
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2
 厉害,感谢分享!
2019-3-15 09:37
0
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
前排
2019-3-15 13:51
0
游客
登录 | 注册 方可回帖
返回
//