CVE-2017-0283:Windows Uniscribe远程代码执行漏洞分析

上个“补丁星期二”,修复了一个名为“USP10!MergeLigRecords中的Windows Uniscrible 字体处理堆破环”的RCE漏洞。多天后,谷歌Project Zero团队的Mateusz Jurczyk发布了一个带有PoC的报告。在那个Windows库中同时存在了8个漏洞中,我选了这个最严重的漏洞来分析。
0x01 重现
在Windows字体查看器中打开压缩包Poc.zip中的字体文件,显示"quick brown fox text" ,但是没有崩溃。我是在Windows 7 x64中测试:
1. 解压PoC
2. 在系统中安装signal_sigsegv_313372b5_210_42111ccffd2e10aba8b5e6ba45289ef3.ttf(或者包中任何其他的TTF)
3. 运行notepad
4. 选择格式->字体...>[字体] 4000 Tall 和 [脚本] Arabic
立即会崩溃:

调用堆栈:

0x02 PoC的分析
通常应该浏览PoC,并且试图在深入调试之前理解它的payload,通过了解数据的布局将节约大量时间。因此我在网上搜索了原始的TTF文件。我观察两个文件之间的不同,以便定位到恶意构造的字体属性。比较显示了一个常见的结构体。然而,使用一个自动化fuzzer,得到太多可疑的属性。这非常耗时,我不得不放弃。
0x03 分析官方补丁
提取和分析官方补丁是下一步该做的事。因此让我们看一下。
漏洞版本是1.626.7601.23688版本的usp10.dll,被补丁1.626.7601.23807代替。文件大小大约一致(788KB),使用BingDiff找到了733个函数。其中有个是崩溃调用堆栈中的一个。首先检查这些函数中的不同,因为补丁经常会在漏洞调用之前检查输入数据。第一个(USP10!MergeLigRecords)来自立即崩溃的上下文,它是一样的。下一个(USP10!LoadTTOArabicShapeTables)显示了修改。下图左边的是老的(漏洞),右边是新的(补丁)。

我们能看见有漏洞的调用MergeLigRecords在一个循环中。



在循环中,补丁只有一个重要的修改,是上图灰色的cmp-jnz代码块。这就是补丁吗?目前为止,分析有限,还无法确定(rsp+var_9C)的比较是什么意思,但是很明显,通过新增的代码块能比老版本判断更精确。
现在唯一的方式就是调试了。因此我们启动WinDbg,并附加到notepad进程。我们设置3个断点。一个是有漏洞的MergeLigRecords,一个在ttoGetTableInfo调用之前,还有个在其之后。ttoGetTableInfo调用是有趣的,因为它有两个结构体参数TTOOutput和TTOInput。灰色中if块检查TTOOutput的属性(rsp+var_9C)是否为4,如果不匹配退出循环。因此我们感兴趣的是ttoGetTableInfo中rsp+var_9C是否改变了并且和MergeLigRecords中的崩溃有什么关系。在F5和点击PoC之前,我们还需要定位补丁版本中的rsp+var_9C属性。

我们看到,在老的LoadTTOArabicShapeTables中有个名为rsp+var_A4的引用(在下面的代码段中解析为@rsp+34)。
现在我们能观察我们的断点

在头两个中,var_A4的值是0004,没有改变。USP10!MergeLigRecords调用执行没有崩溃。

在第3个中,var_A4通过ttoGetTableInfo调用改变了,从0004变为了0001,并且MergeLigRecords崩溃了。

此时,我们得出结论,灰色的cmp-jnz块能阻止崩溃,因此我们找到了补丁。
0x04 0patch补丁
目前为止,我们合起来看,所有的内容都在上述灰色部分,下面是我制作的.0pp文件。

当把官方补丁转成0patch补丁时,我使用了之前文中描述的"jump condition piggy-backing"技术。很方便,官方补丁是jnz退出有问题的循环,我们的0patch也是这个位置。我将它放在原始jnz前面。由于缺少空格(jnz只有2个字节,我们需要5字节跳转到我们的补丁代码),我不得不将它放于ttoGetTableInfo调用之前,0patch Agent将使用5字节跳转代替。补丁的第一条指令是替换为原始的ttoGetTableInfo。接下来是来自官方补丁的检查。首先是var_A4和4比较以检查循环是否要继续,否则保存ttoGetTableInfo的结果的eax置为1,因此test eax, eax会设置跳转标志(zf=0),jnz将退出循环。
在使用0patch Builder编译了.0pp文件之后,补丁就可用了,PoC将不再崩溃。我们的团队测试了PoC.zip中的其他ttf文件,所有的都不会崩溃。然而,需要注意的是这个补丁只覆盖了在重现场景中的执行过程。LoadTTOArabicShapeTablesthere的比较中,有另一个jnz灰色块被添加到usp10.dll中,存在类似的循环。但是因为没有公开的PoC可以触发,在0patch中我们不会覆盖它。0patch的安全使用方式是覆盖测试完全的补丁。