CVE-2013-3906(ms13-096)漏洞分析与利用
本文已发表于“安全客”,转载请注明出处。
最近事情比较多,直到今天才有时间重新来安心的继续我的漏洞分析分享,这次的漏洞分享同时也是我的进入漏洞分析的处女作以及我在2014年的毕业设计,希望能够尽可能详细的展现出来(由于时间关系,直接拿投安全客的稿整上来)。
漏洞背景
该漏洞于2013年11月份发布,漏洞发生在微软的图像图形相关解析库OGL.DLL中,影响包括office2003/2007/2010,Basic2013等相关的软件产品,可以通过漏洞达到远程代码执行。该漏洞已知最早开发于2013年三月份,由于攻击样本中包含大量俄文,所以疑似出自俄罗斯,相关的时间发展线见下图。
这个漏洞最出名的地方除了上面提及的发现过程和特殊的技术框架外,还有漏洞的应用上大多指向巴基斯坦情报局和军方相关的APT攻击,大量的诱惑性攻击样本和木马程序服务于此目的,详见freebuf上的一篇资讯:http://www.freebuf.com/news/21632.html
漏洞原理
这个漏洞发生的位置是OGL.DLL模块,此模块是office用来解析图形图像的动态库,比如一个文档里面如果包含一些图表或者图片,当使用office打开这个文档的时候OGL.DLL就会自动被载入进程空间被调用来解析这些图形元素。而这个漏洞发生的原因是OGL.DLL在解析TIFF这类格式的图片数据时动态计算分配的内存大小,计算结果由于发生整数溢出且没有进行校验,从而导致堆内存缓冲区溢出。漏洞发生过程如下所示:
可以看出关键问题在于文档中包含的这张TIFF格式图片计算size数据的过程,这个过程可以从IDA中的这个子函数找到:
这里展示的是OGL.DLL在对经过JPEG算法压缩过的TIFF格式图片解析时,动态计算压缩数据大小的过程,看得出数据大小主要是由多个数组的数值循环累加而成,而其中每个数组的数值其实代表的是一个压缩块数据的大小。计算完总数据大小后,可以看到函数并没有进行任何检查就马上进行了内存分配,这也是导致此漏洞发生的重要原因,当然其根本原因还在于多个数值叠加后发生了32位整数溢出。此后,进行分配0字节的内存,这个调用将会成功并且返回内存堆块的句柄,只是其堆块内存的容量为0,所以当后面进行数据拷贝的时候,缓冲区就必然会发生溢出,破坏原始堆块的数据结构,这样就导致后面的程序轻易的会发生异常而崩溃。以下是对winword进程开启页堆后运行POC看到的堆内存越界访问,看得出拷贝的目标堆块size为0:
构造触发漏洞的POC
经过上面对漏洞成因的分析,知道漏洞是由于office解析压缩的TIFF图片时计算不严格导致的。为了验证以上的分析并触发漏洞,我们需要往office文档插入一张特殊构造的TIFF图片。这张特殊的图片当然不会是正常的TIFF图片,而是要满足我们前面所说的经过JPEG算法压缩且压缩块size数组叠加后整数溢出。具体地,我们需要参考TIFF文件格式手册,对正常的经JPEG压缩的TIFF图片进行改造,改造过程需要涉及的主要字段为:Compression(压缩标志字段)、StripOffsets(压缩数据偏移数组)和StripByteCounts(压缩数据size数组)、JPEGInterchangeFormat(JPEG压缩格式的数据头SOI)和JPEGInterchangeFormatLength(数据头的长度SOI_SIZE)、ImageLength(图像数据的长度)和RowsPerStrip(压缩块容量),其中,Compression=6(代表使用JPEG压缩),如下图:
而总的size要满足计算结果为32位整数0,总size的计算公式如下:
size=SOI_SIZE+strip_size[1]+strip_size[2]+…+strip_size[n]+n*2+8(SOI_SIZE=JPEGInterchangeFormatLength,n=ImageLength/RowsPerStrip,n > 1)
根据此公式对size数组数据进行修改,使其累加后能够发生32位整数溢出,如下图所示
构造好满足条件公式的图片后,将图片嵌入文档中(可以先嵌入一张正常的图片后再替换成构造的图片)保存,之后再通过office打开文档,文档即可华丽地崩溃:
漏洞利用
至此,我们得到的是一个由于整数溢出导致的堆内存越界拷贝漏洞,异常分配的0字节堆块原本是要用来拷贝SOI数据,现在由于越界拷贝破坏了堆块后面的内存数据导致程序出现异常。那么,为了能够利用这个漏洞来做一些事情,我们的首要目标就是接管程序的控制权,拿到eip。当然,这一步是仁者见仁智者见智,所谓条条道路通罗马,下面就介绍一种可行的方式进行利用。
由于SOI数据在TIFF图片中可控,所以间接的就可以控制越界拷贝的内存数据,而这部分数据是处于堆内存里面,不清楚拷贝数据后破坏了什么样的内存数据结构。因此,通过构造可以预测的内存对象,再借助对象的虚表指针进行跳转来争夺eip是一种比较能够预期的方法。具体地,我在插入TIFF图片之前,先插入一个图表,再插入正常待替换的图片,紧接着再插入一个复制的图表,最后再替换图片为构造好的TIFF图片。这样,相当于在解析TIFF图片的前后,都会解析插入的图表对象:
而图表对象的解析过程都会涉及到其他更多的子对象,比如GraphicsPath这种图像路径对象。构造TIFF图片并填充SOI数据为重复的内存地址值(如0x0a0a00a0),替换了构造的TIFF图片后打开文档,在调试器里跟踪会发现程序会在一个图像路径复制函数GdipClonePath中调用到GraphicsPath对象的虚函数,且GraphicsPath对象的虚表指针被构造的TIFF图片SOI数据覆盖为指定的内存地址值(以0x0a0a00a0为例),于是就成功劫持了程序eip到内存地址0x0a0a00a4:
上图的eax实际上就是GraphicsPath对象的虚表指针,其值存放的位置刚好是0字节堆块后面的堆内存,说明上述的越界拷贝覆盖了GraphicsPath内存对象,而此对象又在解析第二个图表对象时被复制,复制的过程中由于调用了对象的虚函数对对象本身进行检查,导致我们的利用目标得以实现。整个利用过程大概如下图所示(请自行脑补并通过实践去验证):
到了这里,终于从一个堆内存越界写漏洞成功拿到了程序的控制eip,但是漏洞利用过程还远没有结束,因为有了eip后就要跳转到我们的shellcode地址去,可是shellcode存放在哪里?此时,介绍一种office堆喷射的技术,利用office的activeX控件进行内存布局。首先,在文档插入第一个图表对象的前面先插入n多个(如n=40)activeX对象(如上期提到的ListView对象),保存文档为docx格式(office open xml文件格式)后对其进行zip解压缩,将里面的n多个ActiveX.bin文件(一模一样)进行改造。改造的过程也很简单,文件头不需要动,然后将我们的shellcode以一定的间隔进行重复填充,直到整个bin文件的大小为一个编排计算好的值(比如2mb)。改造完顺便替换TIFF图片后重新打包文档,然后office打开文档的过程中就会先映射这n多个bin文件到内存,由于这些内存正常是会连续申请的,所以比较方便我们对进程的内存进行编排,把shellcode布局到可以预测的地址,这个过程大概如下图所示:
通过这种方式,shellcode能够相对稳定的布局到特定的某个内存地址(当然这种方法其实是有一些缺点的,比如无法百分百每次都成功,以及喷射过程比较缓慢等),然后我们只要控制eip跳转到那个地址去执行shellcode就可以。不过,如果是WINXP+office 2007还好可以直接执行payload代码,因为默认没有开启dep保护,堆内存代码可以被直接执行;而如果是在office 2010以上环境, payload代码所在映射堆的内存由于没有可执行属性,执行的时候会抛出异常而停止执行。这时候就该ROP链出场了,众所周知它是dep的老伙伴。ROP是基于栈内存的编程,我们的思路也很简单,就是通过这种特殊的编程方式实现修改shellcode所在地址的内存具有可执行属性后再跳转到上面去执行。
首先由于shellcode存放在堆内存里,而ROP编程基于栈内存,所以我们先修改esp,进行堆和栈之间的转换。要实现这个很简单,就是在进程空间找到一段类似这样的指令:
xchg esp, eax
ret
然后控制eip指向该指令地址去执行后便能直接将shellcode翻转到栈内存,这里会遇到两个问题,一个是如何搜索这些指令的问题,另一个是如何确保这些指令的地址是固定不变的(必需)。第二个问题其实好说,就是根据运行环境找没有开启aslr保护的程序模块进行搜索,就比如winword.exe和上期提到的mscomctl.ocx这两个模块都没有开aslr。而第一个问题,搜索这些指令一般都是借助工具,工具的实现原理一般是对代码段数据进行反汇编后搜索匹配,将一些可用的指令集及其地址展现出来供编程者(我们)来使用。另外,也有一些网站提供在线上传搜索服务,比如通过ropshell.com这个网站上传winword程序文件进行搜索,返回一些易于使用的指令集:
翻转shellcode后,就可以基于栈来跳转调用内存属性修改函数了,这里我的思路是在winword程序的导入表里找到VirtualProtect函数的地址,在栈内存上构造好需要的参数后就跳转到该函数调用,完成修改后直接跳转到payload代码地址去执行,编写好的ROP链如下:
• 从call dword ptr ds:[eax+4]开始:
• 0x0a0a0000(eax):
– 0x3000143d ;pop esi ;ret
– 0x30002465 ;xchg esp,eax ;retn 0c28b
– payload
• 0x0a0a0000+0x0c28b+4(esp):
– 0x300010ed ;(pop esi ;ret)
– 0x30002071 ;jmp [esi + 0xffffff83] (virtualprotect)
– 0x0a0a00a8 ;ret payload start
– 0x0a0a00a8 ;lpAddress(payload start addr)
– 0x00010000 ;dwSize(payload size)
– 0x00000040 ;flNewProtect(RWE)
– 0x0a0a2210 ;lpFlOldProtect(mem can write)
最后,总算可以在office 2010弹一把计算器了:
总结
通过本文详细的分析,我们展示了如何通过一个简单的堆内存越界写漏洞在office程序里执行任意代码。虽然本文采取的利用方式略有繁琐且利用的效果稍有不足之处,但是整体的技术框架还是比较经典,具有一定的借鉴意义,很多地方值得好好的思考。这个漏洞的利用思路以及对应的检测方法具有显著的多样性,漏洞的本质是由于计算结果缺少校验导致内存可以越界写数据,从这一点也可以看出程序开发人员具备认真严谨的编程习惯与良好的安全意识是多么的重要。
目前没有反馈
表单载入中...