CVE-2014-1761(ms14-017)漏洞分析与利用
本文已发表于“安全客”,转载请注明出处。
在上一期的office漏洞分享中,介绍了传奇漏洞cve-2013-3906的技术框架,其中涵盖了不少溢出类漏洞的经典。这一期给大家带来的分享是CVE-2014-1761,这个漏洞严格的说来主要影响的是office套件中的word程序,不像前面两期的漏洞可以在office套件中的多种程序里利用。当然每个漏洞都有自己的特色,只要运用得当,每个漏洞终会出彩。下面本人还是遵循老套路,讲完原理谈利用,最后再给大家演示一下如何编写此漏洞的木马程序捆绑器(通过逆向一个生成器)。
漏洞原理
这次的漏洞发生在WWLIB.DLL这个动态库里面,从其版本信息也能看出它主要是服务于word程序的。事实上,这个漏洞是由于word程序在解析rtf这类文档格式的文件时发生的溢出,而word程序提供对rtf这类文档的支持正是通过WWLIB.DLL这个解析库来实现的。
值得一提的是,这个解析库的代码异常庞大,文件本身就将近20MB的SIZE,通过IDA对其初步分析后保存的idb文件SIZE更是达到惊人的200多MB,从这点足可见这个解析库的强大之处了。回到漏洞本身来说,这个解析库虽然提供了对RTF文件这类比较复杂的文档格式的支持,但是百密总有一疏,虽然微软的格式文档中明确规定了listoverridecount这个rtf控制字的合法参数是0,1或9,不过在WWLIB.DLL中的实现并没有那么严格的校验,所以才导致这次的漏洞。在word程序中,rtf文档的格式(样式)信息都存在编目表里,其中一种编目表叫“编目覆盖表”(overridetable),用来索引多个“编目覆盖项”(listoverride),而listoverridecount是在“编目覆盖项”中描述格式要被覆盖的层次(即“编目覆盖层”lfolevel)数目。正常情况下,word程序会先根据listoverridecount的参数来分配一块内存以便后面索引lfolevel,如下图:
可以看出,根据listoverridecount给每个lfolevel分配8字节的内存,一共分配了8*listoverridecount字节的内存。后面,程序会继续解析每个lfolevel,并将其索引到这块内存区域里:
只是,这里索引的时候并“没有严格”检查索引值edi是否超过listoverridecount规定的最大值,就直接不断的递增其值并拷贝新的数据到分配的内存中去。而由于这块内存是事先根据listoverridecount的值分配出来的,如果实际lfolevel的数量多于listoverridecount,则会发生内存拷贝溢出。
构造触发漏洞的POC
经过上面的分析认识到这个漏洞的成因,由于word程序对rtf文件解析的实现不严谨导致内存拷贝溢出。为了验证这个原理来触发漏洞,首先我们需要参考rtf文件格式的规范手册去了解一些相关的(参考上文提及)rtf控制字的含义,才能进一步根据需求去构造能引导程序行为的rtf文件。接着,根据漏洞的原理构造出一个最简单的rtf文件:
直接双击文件用word打开,发现并没有想象中的异常,word解析后显示如下:
上文提到索引lfolevel的时候没有进行严格的检查,实际上是有检查的,只不过检查的逻辑有种偷工减料的感觉,在调试器下原形毕露:
可以看到,在每个索引之前的检查,都是进行了两次重复的按位异或和按位与的运算,其中最后一次比较索引值和最大值之前的运算是将当前索引值与0x0F值按位与,得到的其实只是索引值的低4位值,并不能代表索引值的位置,这个所谓的检查也就不具备完整性。从这也可以看出上面构造的POC为什么lfolevel的数目(3个)明明超过listoverridecount(2),却没有溢出,正是在这里被“校验”了。换个参数再来,比如listoverridecount的值给一个大于0x0F的值17(0x11),lfolevel的数目调成大于22个,重新走一遍上面的流程就能发现,预期的崩溃果然发生了:
这样成功的原因有两个:第一就是上述的校验过程把索引0x11和0x0F进行与运算后,得到的0x1再来与最大值0x11比较判断有没有越界,当然就会陷入了死循环里,后面待索引的lfolevel数目再多,最终判定越界的条件也成立不了。第二就是之所以选取listoverridecount的值为17而不是16的原因,则和堆内存的分配策略有关,选取17分配出来的内存比较容易挨着其他对象堆块,这样后面可以节省不少用于覆盖内存对象的lfolevel数量(本人实测参数为16的话,lfolevel数量要300多个以上才能溢出崩溃)。
漏洞利用
到这里其实还是和上一期差不多,现在相当于得到了一个堆内存越界拷贝漏洞,所以后面的利用思路实际上在大方向上也差不多(覆盖内存对象拿eip+布局代码)。当然,由于利用的环境有所不同,实现的方式也就有所区别。从上文构造POC的过程中可以看出,listoverridecount的参数会影响内存分配的位置,所以就会间接的影响我们想要某个覆盖对象虚表指针的需求;而lfolevel数量则影响内存对象的覆盖,过少的话会因为小于listoverridecount而触发不了漏洞,过多则可能触发其他异常检查代码直接崩溃。因此,对下面的利用来说这组参数的数值设定尤其重要。
由于这个漏洞具有特殊性,本次漏洞利用不需要自己额外构造覆盖的对象,而是可以覆盖已经存在的内存对象,这里直接给出一组在xp sp3下可用的参数值25和34(即listoverridecount为25,lfolevel的数量为34)。至于如何找到这组参数值得思考,其中既有运行环境的因素也有内存分配算法的因素,这里不作细解,只需要知道参数设定好后就能直接引起一个内存对象虚表指针被覆盖,最终得以劫持eip:
这样,就可以通过控制索引区覆盖的数据来劫持eip了。紧接着两个面临的问题,一个是覆盖的数据为lfolevel的索引数据,如何修改成我们需要的eip数据;第二就是普遍的shellcode代码布置问题,也是eip值的设定问题。很幸运,这两个问题都可以通过一种方式同时解决,那就是通过rtf控制字功能来精确控制特定区域的内存数据。这里需要说明的是,上述每个lfolevel索引最终得到的8字节数据中由于lfolevel没有子对象所以前4字节均为0;而如果lfolevel存在子对象,前4字节位置就可能是其一个子对象的地址如:
这个子对象的地址会指向一块可以通过控制字精确控制其数据的内存,于是,只要lfolevel子对象第二个双字的数据能够被精确构造,eip的值就能成功的被混淆而劫持到任意地址了。同理,由于子对象可以通过控制字写入内存数据,布置shellcode也就不成问题,再加上寄存器eax可以找到子对象的地址,也就有办法找到shellcode的地址,而构造ROP链所需的未开启aslr模块实际上也可以通过rtf控制字引入(如添加一个activeX控件类型的对象引入mscomctl.ocx模块)或者直接使用word程序本身。这样,除了如何准确构造lfolevel子对象的数据需要费一番调试以及查阅文档的功夫外,其他的都可以参考上期的方法去进行了。下面,直接给出一组子对象的构造示例(非EXP)以及对应的内存结构示意图仅供参考:
捆绑木马程序
前两期的漏洞分享中,漏洞利用的部分都是最后直接执行一个具有特定功能的(弹计算器)shellcode,其内部实现是解码需要的api然后调用system函数来打开外部程序。在实际环境中,payload部分经常是联网下载一个加密或未加密pe程序的downloader,或者直接将pe程序嵌入攻击文档中后面再分离出来运行。这样的好处就是灵活性比较高,不用费劲将所有的功能代码都在汇编级别的shellcode中实现。下面,当作本文漏洞分析的一个拓展,通过逆向一个不知名的木马捆绑工具来演示一下其实现的原理,了解本漏洞在实际场景中的运用。
以上就是这个exp工具的界面,虽然看起来比较简陋,却也勉强可用,能够将木马程序与正常文档绑定生成攻击文件,达到利用漏洞运行木马程序后再打开正常文档的效果。通过调试器打开后下个按钮点击事件的消息断点,就能慢慢窥探其中的实现细节了。该工具会从自身资源里取出一个模版文件new.doc,该文件其实就是根据上文分析的漏洞原理构造出来的一个exp框架,其文件结构大概如下图所示:
可以看出,其构造基本和上述一致,为了调用rop,前后共嵌入了两个activeX控件对象,确保mscomctrl.ocx模块会被加载。实际上,这个工具的操作过程,就是根据输入参数对模版文件的上述两个标颜色的区域进行修改,以配置不同的功能需求。以下介绍此工具精准构建动态shellcode和payload的一些细节。
首先,shellcode部分,代码应该是lfolevel子对象内的数据,具体地此exp是使用leveltext这个控制字来布置内存数据,所有shellcode在文件里看起来将是这样:
而这个工具使用的shellcode是以内联汇编代码的形式放在程序代码中,然后动态获取代码的位置后对其进行组合改造和编码,这样增加了程序的灵动性和可塑性:
组织完后再进行unicode编码输出到leveltext中:
然后是payload部分,程序将会根据用户指定的木马路径(以系统记事本程序为例),将木马文件读进内存与加载木马的shellcode代码进行拼接组装,并且对他们进行简单的异或加密:
当然,还有用于迷惑受害者的正常文档以及伪装的程序名参数,这些都是会一起加入shellcode的组装过程且进行异或加密后存放,这里限于篇幅就不作展示了。最后,象征性的上一个效果图吧:
总结
本文通过分析认识了rtf格式解析漏洞的机制,了解了word程序解析rtf文件格式在实现上存在的不足之处。整体上看,这个漏洞的成因还是缺乏校验的问题,导致内存数据可以被越界覆盖,而一旦可以越界,就总有办法控制对象的分布达到劫持程序控制权的目的。从另外一个角度来看,发现和利用了这个漏洞的人,能够在这么繁杂的代码中找到并巧妙的通过rtf的各类控制字精确的实现漏洞的稳定利用,不得不令人心生敬佩。当然,攻防无止境,剖析攻击的细节,是为了更好的防御。就本文的漏洞来说,其影响了office word程序2003到2013之间的版本,根本原因还是由于这类溢出漏洞存在的通病:内存的分配和使用不够严格和规范。所以,规范的使用以及严格的检验才是相对根本的解决方式。
目前没有反馈
表单载入中...