CVE-2013-0640的分析记录

CVE-2013-0640的分析记录_第1张图片

1. 概述

在Adobe Reader的XFA(XML Forms Architecture)处理模块AcroForm.api 中,存在一个用于得到任意代码执行权限的漏洞,编号为CVE-2013-0640。另一个是 CVE-2013-0641,该漏洞存在于 Adobe sandbox 的 broker 进程中,用于从sandbox 中逃逸,获取高权限,这两个漏洞同时被用在了一个样本中。CVE-2013-0640的POC可在古河的文章下获取( https://bbs.pediy.com/thread-163035.htm )。


2. 实验环境

版本为11.0.1的Aodbe Reader,XPSP3,Win7,OD,windbg,IDA


3. 漏洞触发原因

漏洞触发的基本过程是这样:AcroForm.api中的内存分配函数Alloc在分配 Adobe Reader的 XML表单中的新节点NewObject后,由一个存在缺陷的函数bugFun来调用这个新节点,一开始分配的时候 NewObject 的大小为40h字节,但是当缺陷函数bugFun再去引用这个节点NewObject的时候,访问的却是[NewObject+44h]的地方,多访问了4个字节,而这个位置也是一个对象的指针,这个漏洞的产生原因就是bugF函数在引用节点NewObject的时候没有对可引用的范围做限制,造成了越界访问。


4. 分析记录

这里先是分析了CVE-2013-0640的POC样本,这个POC用来演示CVE-2013-0640的触发和利用,之后再分析了真实样本。

首先将OD设置为实时调试器,接着打开POC,不久OD就会捕获异常,程序停在了“0x88888888”,由于这个函数地址"0x88888888"是伪造的,也是不存在的,于是函数没有正常返回,可以看到是“0x208a54d3”调用了函数,在上面的指令中可以看到取函数地址的过程,如下图所示:

CVE-2013-0640的分析记录_第2张图片

接着用windbg来分析,感觉用windbg下断点更方便。用Windbg附加Aodbe Reader后,直接g命令运行,crash的完整汇编代码如下:

CVE-2013-0640的分析记录_第3张图片

对应的伪代码:

CVE-2013-0640的分析记录_第4张图片

可以对比着看上图OD在crash的现场,在”0x208A54b9“的地址处获得了[NewObject+44h]的虚表指针,在这之前esi的值由”0x208A5488“处的mov esi,[eax]赋予,而eax的值最初由”0x208A5478“处的调用函数生成返回,就是这个函数”0x208A5514“生成了新节点NewObject,具体的分配函数是”0x208A5514“的一个子函数:这里可以看到新节点NewObject的大小为40h,但是上图中”0x208A54b9“处却引用了偏移44h的位置 ,多访问了四个字节:

而这个 [NewObject+44h]的地址存放的也是某个对象的指针,该对象的结构如下:

struct NewObject+44t {

+0 虚表

+4 referencecount //当引用计数为 0 时,对象将被析构

+30 unknown

+48 MemberArray //一个数组结构的指针 }

重新用windbg附加Adobe Reader,一开始有说到漏洞函数存在的模块为AcroForm.api,于是用模块断点”sxe ld AcroForm.api"对漏洞函数所在的模块下断点,成功之后根据上面的分析在”0x208a54b9“处下下断观察[NewObject+44h]的虚表指针被覆盖的过程:

CVE-2013-0640的分析记录_第5张图片

接着通过内存写断点"ba w4 0c0c0c20"来跟踪虚表地址被覆盖的过程,如下图所示:

CVE-2013-0640的分析记录_第6张图片

5. 样本构造

(这部分内容参考古河的分析: https://bbs.pediy.com/thread-163035.htm )

通过以上分析可知,只要能控制[NewObject+44h]处该对象指针的值,以及这个值所指向的内存区域的内容,就能控制 EIP,首先将[NewObject+0x44]处的值设为 0x0c0c0c20,于是 0x0c0c0c20 将被当 成一个 Member44_t 对象的指针进行后续操作,同时我们将 0x0c0c0c20 处的内存布局成如下形式:

1. [0x0c0c0c20] = 0x0c0c0c28, 在析构对象调用析构函数时将被使用。

2. [0x0c0c0c24] = 1, 该对象的引用次数,将其设置为 1,于是在 “dec dword ptr [ecx+4]” 的减操作完成后,析构动作立即发生。

3. [0x0c0c0c28] = 0x88888888,这个是虚表中第一个函数,最后 crash 时 EIP 会指向 0x88888888

现在的问题是我们如何精确地控制[NewObject+0x44]处的值,使其变成 0x0c0c0c20? 这个是可以办到的,关键在于 AcroForm.api 中自带的堆内存分配机制。 AcroForm.api 有自己的堆分配管理机制(可选),这个机制和 Windows 自身的很像,它针对大小为 0 ~ 0x100 的内存分配有专门的 0x100 个 freelist,用来 保存已经释放的内存块(当然没有真的调用 free 去释放,同时还有另外的一个 freelist 存放其它大小的 已释放内存块。

当一个内存分配请求到来时,先检查 freelist 中有没有合适的内存块,如果找到合适的 则直接返回这个块;如果没有正好合适的大小,则尝试将较大的已释放内存块切成小一点的内存块返 回,实在找不到了才会新分配内存。这里还有一个要点是,对于 freelist 中的内存块,在释放和重新分 配返回时,不会将原有的内容进行清理。

利用这个自定义的内存分配机制,我们现在可以精确地控制[NewObject+0x44]处的内容了。首先分 配一系列较大的内存块,将这些大内存块填充为 0x0c0c0c20。 然后分配一些和 NewObject 大小相同的块,这样做的目的是为了将 freelist 中已有的缓存用完。然后 我们释放所有的大内存块,紧接着触发漏洞。在漏洞触发时,NewObject 被分配,由于大小相同的 freelist 已经被清空,于是它会尝试从我们刚刚释放的某个大内存块中切割出一个 0x40 的小内存块并返 回,换句话说,返回的 NewObject 地址其实是落在我们刚刚释放的某个大内存块中间的。于是在越界 访问 NewObject+0x44 时,得到的值是我们一开始在大内存块中填充的值,即 0x0c0c0c20。

整个过程如下图:

CVE-2013-0640的分析记录_第7张图片

控制eip的示意图:

CVE-2013-0640的分析记录_第8张图片

6. POC中的javascript分析

 ”NewObject“的生成和越界访问“NewObject+44h”的过程可以参考POC样本中的javascript代码来理解,关键代码如下:

第一步:解析获取节点

xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field" + index.toString() + "[0].#ui[0]");

uiListNodes.push(node);

//解析并获取 XFA 表单中的一个UI节点,这个节点就是在汇编代码中“0x208A5478”处生成的节点对象“NewObject”

var node = xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field" + index.toString() + "[0].#ui[0].#choiceList[0]");

 choiceListNodes.push(node);

//解析并获取上面得到的UI节点下面紧邻的choiceList节点,这个节点就是“NewObject+44h”处的那个对象

}

第二步:再次解析引用的时候发生越界访问

var node = xfa.resolveNode("xfa[0].form[0].form1[0].#pageSet[0].page1[0].#subform[0].field0[0].#ui");

//引用之前已获取的节点”NewObject“

if ( node == undefined ) {

return false;//如果引用的节点对象未定义,就返回false,啥事没有

}

try {

node.oneOfChild = choiceListNodes.pop();//否则返回“NewObject+44h”对象的指针(被篡改后的),由于没有对对象的可引用范围做限制,会发生越界访问;

}

catch (e) {

return false;

}

return true;


7. 对真实样本的分析

之后再分析了完整样本,完整样本的分析是在win7中进行的,其中CVE-2013-0640的触发过程基本与上述分析过程一致,主要的问题就是相关漏洞函数的内存地址发生了变化,但是相对偏移地址是不变的,比如XP下的地址"0x208A54d3"在Win7下就会变为"0x67cd54d3",相对偏移地址没变。在Win7下尝试下断点的时候,一开始没有成功也是因为忽略了不同系统下内存地址的变化,之后先在xp下用“uf +地址”命令找到指令所在的真实地址,比如"0x208A54d3"和"0x67cd54d3"的真实地址就是:”AcroForm!PlugInMain+0xa31a5“,这在不同系统下都是一样的,之后通过真实地址成功下了断点。另外就是上面分析的POC并没有ROP链,POC只是用来crash,真实样本中这个漏洞是被实实在在的利用了。

触发过程跟上面的分析一致,就是地址不一样,对完整样本的分析主要是跟进了CVE-2013-0640触发后过程,通过真实地址”AcroForm!PlugInMain+0xa31a5“下断,可以看到在“0x67cd54d3“的位置调用了伪造的虚函数:

CVE-2013-0640的分析记录_第9张图片

如果直接F10执行,函数就会通过rop加载恶意dll,之后就是利用第二个漏洞CVE-2013-0641完成沙盒逃逸,整个逃离过程全是在第一个漏洞引入的dll中完成:

CVE-2013-0640的分析记录_第10张图片

可到对应目录下找到该DLL及其释放的木马:

CVE-2013-0640的分析记录_第11张图片

接着用windbg重新附加Adobe Reader,让程序继续断在“0x67cd54d3“,这次用F8单步跟进,会发现程序开始执行ROP,其中包括了大量的ret指令和pop等操作:

CVE-2013-0640的分析记录_第12张图片

ROP和加载DLL的shell code间是用NOP指令填充的:

CVE-2013-0640的分析记录_第13张图片

shellcode首先会通过GetTempPathW获得Temp文件夹的路径:

之后在该路径下通过fsopen()函数生成指定文件夹和指定的DLL文件"D.T",可通过fsopen()函数的参数来查看对应的路径和文件:

CVE-2013-0640的分析记录_第14张图片
CVE-2013-0640的分析记录_第15张图片

接着通过fwrite()函数来写入数据:

最用用fclose()函数结束写入:

CVE-2013-0640的分析记录_第16张图片
CVE-2013-0640的分析记录_第17张图片

写入之后通过loadlibrary来加载该文件:

CVE-2013-0640的分析记录_第18张图片

这个DLL加载完,这个漏洞的使命也达到了,往下就是第二个沙箱逃逸漏洞的触发和利用。


8. 总结

前期分析的时候过于陷入别人的分析思路,没有去独立思考,这样很不好,他人的思路也要辩证的接纳,保持独立思考,要尝试自己去勾勒出漏洞触发和利用的流程,这方面还缺乏很多经验,一点点来吧。


9. 参考文章

1、http://www.freebuf.com/vuls/33448.html

2、https://bbs.pediy.com/thread-163035.htm



原文作者:大晟

原文链接:https://bbs.pediy.com/thread-246656.htm

转载请注明:转自看雪论坛



看雪阅读推荐:

1、[原创] 打造Wi-Fi “DOS”攻击工具——Wi-Fi_deauther

2、[原创]8月29号网鼎杯pwn1-impossible简单调试过程

3、[原创]bindiff基本使用以及小试牛刀

4、[分享]【看雪安全开发者峰会2018】议题:自动逆向机器人

5、[翻译]ARM汇编简介(六)堆栈和函数

你可能感兴趣的:(CVE-2013-0640的分析记录)