CVE-2014-1761 RTF漏洞分析

By Zing - 2014-08-24

原文链接(翻译:Zing)

漏洞的基本情况

发布日期: 2014-03-24

影响系统:
Microsoft Word Viewer
Microsoft Word 2013
Microsoft Word 2010
Microsoft Word 2007
Microsoft Word 2003

产生原因:
因Microsoft Word在解析畸形的RTF格式数据时存在错误导致内存破坏,使得攻击者能够执行任意代码。当用户使用Microsoft Word受影响的版本打开恶意RTF文件,或者Microsoft Word是Microsoft Outlook的Email Viewer时,用户预览或打开恶意的RTF邮件信息,攻击者都可能成功利用此漏洞,从而获得当前用户的权限。值得注意的是,Microsoft Outlook 2007/2010/2013默认的Email Viewer都是Microsoft Word。

相关的文章链接http://www.bigsea.com.cn/archives/1358/

这篇文章的测试环境是Windows XP SP3 + Office 2010 SP2

译文:

最近,微软宣布CVE-2014-1761 RTF出现野外版本,这个样本才为人所知。这篇博客讲了我分析这个漏洞得到的发现。分析样本的SHA1值为200f7930de8d44fc2b00516f79033408ca39d610。分析过程中用到的主要模块是wwlib.dll,版本为14.0.7113.5001,用于Microsoft Office 2010。

主要解析器位于wwlib.dll中,地址0x31D0BAFF处。


图1 主要RTF解析器

这个函数比较复杂,包含几个大的switch语句。图2展示了函数的鸟瞰图,提供了比较直观的信息。大多数和漏洞有关的代码存在于这个函数中。



图2 主要RTF解析器鸟瞰图

越界数组覆写

这个漏洞产生的主要原因是内存越界覆写。使用控制字listoverridecount时会创建一个数组。在这个例子中,控制字的参数数量为25,也就是为之后分配内存存放内存指针的数组大小为25(见图3).每一个成员拥有8字节的空间。


图3 listoverridecount

图4展示了分配这个数组的代码。0x31D0CC67F处的指令将数组大小(25)压入栈中。0x31D0C690处的指令保存指向内存中结构体实例的指针(偏移量为0x80FC).寄存器ecx指向记录RTF信息的结构体。从偏移量(0x80FC)可以得知这个结构体很大。这个结构体在整个代码中都会被用到。从现在开始,我把ecx指向的地址称为RTF parse object。


图4 分配数组的代码

图5展示了实际数组分配时复制内存的过程。0x31D131FF处寄存器edi保存了数组的下标。0x31D131F9处指令,表示从eax+0x80FC处获取数组基址。寄存器eax指向RTF解析对象。


图5 数组复制过程

问题是当内存复制发生时,没有检查下标值(0x31D131FF处的edi)是否超出数组大小。所以一个数组越界覆写漏洞产生了。接下来的问题是,什么情况会导致越界内存分配?我发现每一个lfolevel控制字在数组分配时增大整个数组下标值。
图6展示了这个控制字的确触发了这种内存操作。这里有34个lfolevel控制字,超出了原来数组所能容纳的数量。


图6 控制字(lfolevel)引发数组分配

数组覆写内容
复制到每个数组成员中数据的前4个字节实际上是堆的内存地址。有时候只复制了一个空值。图7展示了其中一个内存地址(0x067d8870)被复制到下标为0x1d的数组成员中。
下标为0x1d的数组成员现在位于原始数组的外面,GFX.dll中OART操作对象里面。


图7 数组内容复制
图8展示了内存破坏是怎么发生的。你可以看到GFX对象相关的内存字节被覆写了。每个数组成员8字节中前四字节原本指向GFX对象的虚函数表,现在被覆写了。导致了之后GFX对象被调用时代码的执行。


图8 GFX 对象虚函数表破坏

图9中展示了0x67d8870处内存内容。在内存破坏后,这些内容被当作了一个虚函数表。


图9 0x67d8870处内存

图10展示的代码将这些内容复制到0x67d8870。从RTF parse object+0x8104开始,复制了0x30个字节。执行memcpy(0x31D12F78)时,寄存器ecx指向全局的RTF parse object+0x8104。


图10 memcpy将内容复制到0x67d8870

内存分配发生后,最后与利用漏洞有关的内存如下图。ArrayData代表每次从RTF parse object+0x8104处复制的数据。
时的代码执行。


图11 ArrayData

RTF parse object+0x8104区域怎样构造

利用特定内存区域(RTF parse object+0x8104)的内容,样本可以触发内存破坏。接下来的两个问题是,这个区域应该怎样构造,攻击者是否能控制。第二个问题的答案是肯定的。前面已经说了RTF解析对象包含了解析RTF流的信息。RTF parse object+0x8104周围的内存区域已经被不同控制字使用。接下来解释每个控制字负责哪一块。
当新的控制字被解析时,RTF parse object+0x8104处的内容改变。我使用分配数组0x1d时的内存内容。

RTF parse object+0x8104(双字)
图12中的指令用于在这个位置存放值。


图12 RTF parse object+0x8104 修改指令
实际上相关的控制字是levelstartat。图13展示了负责写字节的部分。31611即十六进制数0x7b7b


图13 相关控制字
图14展示了RTF parse object+0x8104处的4字节


图14 RTF parse object+0x8104(双字)

RTF parse object+0x8108(字节)

图15展示了填充RTF parse object+8108的指令


图15 RTF parse object+0x8108修改指令

控制字levelnfcn触发代码执行。图16中,232即0xe8,就是往levelnfcn+0x8108写入的内容。


图16 相关控制字


图17 RTF parse object+0x8108(字节)

RTF parse object+0x81099(字节)

控制字levelnorestart,levelold,jclisttab,leveljcn影响这块内存。图18展示了修改内存字节的代码。


图18 RTF parse object+0x8109修改指令

偏移地址处的内存可以在图19中看到。


图19 RTF parse object+0x8109

图20展示了控制字levelnorestart1(8)和levelold1(分配40)用来异或得到值0x48


图20 相关控制字

RTF parse object+0x810A

RTF parse object+0x810A用控制字levelnumbers的参数填充。图21中,前5个字节(\'5A')被转义作为0x5A执行。


图21 相关控制字
图22中可以看到十六进制形式


图22 相关控制字(十六进制形式)

图23展示的代码将字节复制到RTF parse object+0x810A。


图23 RTF parse object+0x810A修改指令
图24展示了解析的字节


图24 RTF parse object+0x810A

RTF parse object+0x8113(字节)

由图25展示了控制下一个单字节的指令。


图25 RTF parse object+0x8113修改指令。

控制字levelfollow触发这段代码。图26中展示了实际RTF字符串。


图26 相关控制字

图27中的参数39即0x27。


图27 RTF parse object+0x8113(字节)

RTF parse object+0x8114(双字)

图28中展示了RTF parse object+0x8114的填充指令。


图28 RTF parse object+0x8114修改指令

控制字levelspace触发图29中的代码和参数.


图29 相关控制字

图30中22873即0x5959。


图30 RTF parse object+0x8114(双字)

RTF parse object+0x8118(双字)

图31展示了修改RTF parse object+0x8118处4个字节的指令。


图31 RTF parse object+0x8118修改指令

控制字levelindent触发这些指令。图32中可以看到。


图32 相关控制字
图33中23130即0x5a5a。


图33 RTF parse object+0x8118(双字)

总结

这个漏洞利用非常有趣。攻击者能够很好的控制漏洞利用中的大多数字节。通过稳定的方式,使用伪造的虚函数表代替原有的,使得漏洞利用非常稳定。通过使用整套控制字中的几个来控制关键的字节,体现了攻击者对wwlib.dll中RTF内部处理流程有很深的了解。

From Z1ng'Blog