作者:银雁冰
预估稿费:600RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
简介
北京时间2017年10月11日,微软在10月的安全公告中公开致谢奇虎360,后者在9月底向其秘密报告了一个在野office 0day并积极协助修复。该漏洞的成因是</w:font>标签没有正确闭合,造成用OLEObject的数据结构解析了font提供的数据。攻击者通过在font的name属性中提供精心构造的数据,覆写了一个函数指针,从而实现控制流劫持。该漏洞是和CVE-2015-1641一样经典的类型混淆漏洞。这篇文章中,我将分析该漏洞的触发原理,并在此基础上尝试构造该漏洞的一个简单利用,最后给出该漏洞的动态检测方案。
分析所用样本的MD5: b2ae500b7376044ae92976d9********
静态分析
原始样本为一个rtf文档,初步观察后发现该漏洞最后有一段乱码字符,初步判断是一段payload。进一步观察发现该样本为常见的rtf文档利用方式,于是在内容中搜索可能被嵌入的word对象,搜索“Word.Document.12”,得到如下结果:
证明该rtf文件里面嵌入有word文档对象,于是用rtfobj.py工具进行提取,结果如下:
可以发现该样本内嵌三个对象,其中两个为Word.Document.12对象,另外一个暂时未知。联想到之前分析过的CVE-2015-1641漏洞,于是猜测该样本由3部分构成:一个用于堆喷射文档,用于绕过ASLR的控制语句,一个漏洞触发文件。于是我把编号为1、2的两个对象的后缀名改为.zip,并用压缩软件打开,果不其然,看到了如下目录结构:
类比CVE-2015-1641,我在提取出来的2号文档的document.xml文件里面看到了如下语句:
可以看到<w:font>标签缺乏</w:font>这一闭合标签,而且中间存在特殊字符,猜测这就是漏洞触发点,等待后面验证。
到这里,还没有找到Bypass ASLR的关键字。我尝试在rtf文档中搜索类似如下的文本内容(该类型漏洞用来Bypass ASLR的常见方式,最终会导致加载msvcr71.dll从而Bypass ASLR):
有点意外,没有发现类似的内容,而且看rtfobj.py提取出的0号对象也不像。不过我注意到在Word.Document.12对象嵌入前存在下面的语句:
里面有一个CLSID:D5DE8D20-5BB8-11D1-A1E3-00A0C90F2731,查询得知该CLSID对应的模块为msvbvm60.dll,读过《Bypassing Windows ASLR in Microsoft Office using ActiveX controls》这篇文章的人应该都知道这个模块是可以用来Bypass ASLR的。分析到这里,前面猜测的3部分已经都定位到了,下面进行动态验证。
(工具为James Forshaw的OleViewDotNet)
动态分析
为了方便动态分析,需要构造一个crash样本。依据之前对CVE-2015-1641的调试经验,只要在rtf文档中把堆喷射部分移除,就可以造成可控的crash。具体来说,就是在原始样本中把上面提取出来的编号1的内容和最后的乱码部分(乱码部分一般是该类型漏洞利用方式的第二阶段payload)给移除,或者构造一个最简crash,如下:
我的调试环境为windows7_sp1_x86 + office2007,具体的文件版本如下:
将上述最简crash拖进虚拟机,用windbg打开winword.exe,然后打开该crash文件,发现在如下位置产生一个访问违例:
且此时的寄存器状态如下:
可以看到这是一个call调用,且eax寄存器的值为0x88888ec,在有堆喷射的情况下,该地址处应该为布控好的内存,因为现在没有进行堆喷射,所以此处是一个无效值。
计算得到崩溃点在wwlib中的偏移为3d30e9:
从IDA的反汇编视图中可以猜测出崩溃函数的第二个参数的+0x1c处的值为一个长度,+0x18处的值为一个宽字字符串:
对崩溃函数的首地址下断点,调试验证上面的猜测,输出如下:
对照静态分析得到的文件,可以发现,崩溃函数的第二参数中存有每次解析的标签,崩溃函数应该是在解析每一个标签的内容。从上图可以看到,当解析到idmap标签时发生了crash,并且在上图中并没有发现font标签。猜测font标签是在其他函数中进行解析的。
于是追溯到崩溃函数的父函数,父函数为sub_3D3FB,崩溃函数在父函数的调用处如下:
观察发现父函数的逻辑是对更大范围标签进行派发。现在来下断点验证一下父函数是否解析到了font标签。从IDA视图中可看出,崩溃函数的第一参数为父函数调用处[esi+b10]存的值,崩溃函数的第二参数即为父函数的第二参数,于是对父函数的首部下断点,得到如下的调试结果,可以看到父函数是可以解析到font的:
现在对崩溃函数和父函数首部同时下断点,看父函数把标签派发给崩溃函数的情况:
可以看到OLEObject和idmap标签父函数都派发给了崩溃函数处理,但font标签父函数没有派发给崩溃函数。
调试到这里,已经知道是wwlib在解析漏洞文件中的OLEObject及其子标签时引发的漏洞。接下来的问题是,漏洞文件中的数据是如何被传递到漏洞触发处的?
注意到崩溃点的上面不远处有一个call(sub_9DA0),对该call下断点,多次调试之后发现该call的返回值对应的[[ret_value+44]+44]处固定为0x88888ec,猜测该值为漏洞文件所提供。对sub_9DA0函数进行分析后,发现该函数的作用是计算返回一个地址,分析时我的IDA注释显示该函数在CVE-2015-1641的触发过程中也被用到(因为也要解析标签获取数据)。在IDA中发现该函数会通过ecx提供的值计算得到一个地址,如下:
地址计算公式为 edx × [ecx + 8] + [ecx + c] + ecx。
如下图所示:在崩溃函数中发现调用前ecx的值源自esi,edx的值也来自esi,而esi进一步源自arg0,所以地址计算公式可以表示为:([[[arg0+b14]]] – 2) × [[[arg0+b14]]+ 8] + [[[arg0+b14]]+ c] + [[arg0+b14]]:
此时对崩溃函数开始处的断点输出进行补充,使标签名和每个标签对应的公式数据一并输出。调试后得到如下结果(输出中第一个值并没有减2,后面在计算公式需要手动减2,其实后面会发现这个值在函数中是固定的):
从上图的输出中可猜到,紫色框圈出的输出应该为xml文件中标签解析的嵌套层级,可以看到漏洞触发时OLEObject对应的level为4,idmap对应的level为6。从上图中也可得知:在崩溃函数中,公式第一项的右乘数和公式第二项的值在每次计算时都固定保持不变,分别为0x4c和0x10,且公式的第四项在解析OLEObject标签及其子标签时保持不变。所以公式可以简化为:(current_level-2)× 0x4c + 0x10 + [[arg0+0xb14]]
由上面的分析可知崩溃前的sub_9DA0返回值的[[ret_value+44]+44]处的值固定为0x88888ec,且此时传入sub_9DA0的 edx = 6–2 = 4。重启windbg,在解析OLEObject标签时,用上面推导的公式计算得到崩溃点前sub_9DA0会返回的值calc_addr。先查看[[calc_addr+44]+44]处的值,发现访问违例,再退一步查看[calc_addr+44]处的值,发现全为0,于是对[calc_addr +44]处下一个内存写入断点,调试及输出如下:
6字节数据在内存中变成了4字节数据,猜测用到了utf-8编码,验证一下果然是这样。如下图:可以看到原文件中的非ASCII码字符通过utf-8编码后到达了内存中的指定地方,从而在漏洞触发后被获取:
由栈回溯发现,该拷贝过程发生在父函数的另一处逻辑里,如下。该逻辑应该就是解析font标签的逻辑,这里不再深入追踪:
到这里,已经分析清楚漏洞数据的传递过程。接下来的问题是,类型混淆究竟是如何发生的?到底是什么和什么之间产生了类型混淆?
既然上面怀疑漏洞是</w:font>标签没有正常闭合导致,现在就来构造一个带</w:font>的正常样本,方法很简单,创建一个新的docx文档,随便写入一些数据,保存后将后缀名改为.zip,用压缩软件将原文档中的document.xml替换为增加了</w:font>的漏洞文件,如下图:
打开后文件果然没有崩溃,看来就是这个问题导致的。接下来在windbg里面打开修复后的文档,再下一遍上面的断点,对比正常文档和漏洞文档调试输出的不同处。
首先对比两个文档在解析各标签时的公式计算结果,由下图对比发现两者在解析idmap时的level级别不同,正常文档解析idmap的level级别是5,而漏洞文档为6:
再检查两个文档在传递漏洞数据时的不同,windbg打开正常文档,对崩溃函数首部下断点。在解析到OLEObject标签时,用一样的公式计算出地址,并对该地址设定内存写入断点,发现正常文档在漏洞触发点前的某个地方再次触发了内存写入断点,而前面调试漏洞文档时并没有在此处触发该断点,如下:
发现该处地址位于漏洞函数内,在IDA的反汇编视图中看到该处正位于崩溃点之前:
进一步调试发现sub_3127F3FB(基址调为0后为sub_3F3FB)函数内部也调用了计算地址的sub_9DA0函数。有意思的是,和上图相比,这里调用时对同一个值减去了1,而上图中可以看到崩溃点是减2,且该值正是当前解析标签的level值。正常情况下,当解析OLEObject标签时,level_OLEObject = 4,level_idmap = 5,解析OLEObject时在上图中的修改点将4-1=3对应的值设定到指定地址处,在解析idmap时,崩溃点拿到的是5-2=3对应的值,这正是它的父标签OLEObject设定的值;而在漏洞触发时,解析OLEObject时同样将4-1=3对应的值设定到指定地址处,但在解析idmap时,崩溃点拿到的是6-2=4对应的值,那么4对应的值是谁设置的呢?应该是level_5对应的标签设置的,而level_5对应的标签正是font,在父函数对font的解析逻辑里也调用了sub_9DA0对5-1=4处的地址进行了设定,此过程还读入了font的name属性对应的数据。
在没有</w:font>闭合标签的情况下,在解析完font后索引值并没有减1,导致idmap在解析时,理应获取OLEObject设置的数据,却获取了font设置的数据,在后面解析数据时,用OLEObject对应的数据结构解析了font所提供的数据,从而造成类型混淆。只要精心构造font所提供的数据,就可以劫持特定的函数指针,达到控制执行流的目的。
利用编写
分析到这里,已经知道了漏洞的触发原理,下面尝试构造一个exploit,使用这个漏洞弹出一个计算器。
由于该漏洞的利用方式和CVE-2015-1641及CVE-2016-7193非常像,所以如果构造过前两个漏洞的exp的话,构造这个漏洞的exp几乎不需要多少时间。
步骤如下:
1. 写一段Python脚本修改原有的axtiveX1.bin文件,构造所需要的rop-gadgets和弹计算器的shellcode;
2. 再写一段Python脚本,利用生成的axtiveX1.bin文件作为输入生成堆喷射docx文档;
3. 用压缩软件打开生成的堆喷射docx文件,将里面的activeX1.bin手动删除,再手动放入,目的是为了减小文档体积(可以显著减小体积);
4. 新建一个空白rtf文档,将3中生成的堆喷射docx手动拖入文档内,保存;
5. 用notepad++打开4中生成的rtf文档,提取出{object…}闭合的部分;
6. 将bypass-aslr所需用到的内容拷贝到新的rtf文档中,原始样本加载的是一个新的模块,我这里为了实验直接用了otkloadr.WRAssembly,具体步骤也可参照维一零的文章;
7. 从原文档中提取出漏洞触发的部分,和5中类似;
8. 新建一个文本文档,按堆喷射在前、加载bypass aslr模块在中,漏洞触发在后的顺序构造exp文件,保存成一个rtf文档;
整个利用布局如下:
将构造好的exp在调试环境下打开,可以顺利弹出计算器(如果不想看到crash,可以把shellcode编写得优雅点)
这里给出一下生成activeX.bin的脚本和替换activeX控件的Python脚本:
动态检测
由上面的分析可知,该漏洞是个典型的类型混淆漏洞,所以合理的检测方案是比较正确的指针和混淆后的指针。在崩溃函数写入对象指针的位置做一个拦截,在解析标签等于"OLEObject"的时候,保存v15的值,即eax的值,供后面对比使用:
在混淆前的sub_9DA0(即上图中的sub_31249DA0,上图中的基址没有设为0)函数调用完成后也做一个拦截,取sub_9DA0函数的返回值,也即eax,将这个值与上面的值做比较,如果两者不同,则说明触发了漏洞。
结语
整个分析来看,这是一个典型的类型混淆漏洞,无论在漏洞原理上还是利用方式上都堪称CVE-2015-1641的姊妹漏洞,如果在原样本中将劫持地址和堆喷地址由0x88888f0稍微调高一点,利用的稳定性就会好很多。该漏洞触发非常稳定,可能会在不久的将来取代CVE-2015-1641,成为下一个被滥用的漏洞,需要引起高度警惕。
致谢
特别感谢《CVE-2017-11826 样本分析》这篇文章。
PS. 原始样本在利用成功后释放Payload以达到持久驻存的方式用到了《Persisting with Microsoft Office:Abusing Extensibility Options》这篇paper里面讲到的方法。
参考链接
《最新Office 0day漏洞(CVE-2017-11826)在野攻击通告》http://blogs.360.cn/blog/office_0day_cve-2017-11826_ch/
《CVE-2017-11826 样本分析》 https://bbs.pediy.com/thread-221995.htm
《结合一个野外样本构造一个cve-2016-7193弹计算器的利用》https://bbs.pediy.com/thread-221792.htm
《the-curious-case-of-the-document-exploiting-an-unknown-vulnerability-part-1》 http://blog.fortinet.com/2015/08/20/the-curious-case-of-the-document-exploiting-an-unknown-vulnerability-part-1
《Spraying the heap in seconds using ActiveX controls in Microsoft Office》 https://www.greyhathacker.net/?p=911
《Bypassing Windows ASLR in Microsoft Office using ActiveX controls》 https://www.greyhathacker.net/?p=894
《手把手教你如何构造office漏洞EXP(第四期)》http://bobao.360.cn/learning/detail/3246.html
《Attacking Interoperability》 https://www.blackhat.com/docs/us-15/materials/us-15-Li-Attacking-Interoperability-An-OLE-Edition.pdf
《Persisting with Microsoft Office:Abusing Extensibility Options》https://labs.mwrinfosecurity.com/assets/BlogFiles/WilliamKnowles-MWR-44con-PersistingWithMicrosoftOffice.pdf