12

深入分析 Microsoft Outlook 漏洞 CVE-2018-8587

今年早些时候,FortiGuard 实验室研究员 Yonghui Han 通过 Fortinet 的负责任披露流程,向微软报告了 Office Outlook 中的 Heap Corruption 漏洞(Heap Corruption vulnerability)。2018 年 12 月的周二补丁日,微软宣布他们已修复此漏洞,发布了相应的通报(corresponding advisory),并为其分配了漏洞 ID 号 CVE-2018-8587。

Microsoft Outlook 是 Microsoft Office 套装的组件之一,广泛用于发送和接收电子邮件、管理联系人、记录和跟踪日程安排以及执行其他任务。在 Windows 系统上的多个 Outlook 版本中都发现了 Heap Corruption 漏洞,涵盖了从 Outlook 2010 到最新的 Outlook 2019 以及 Office 365 ProPlus 的所有 32/64 位版本。该漏洞由格式错误的 RWZ(邮件分类规则)文件触发。当 Outlook 收到不正确的 RWZ 文件内容时,分配的堆内存不足并且缺少适当的边界检查,从而导致堆的越界写入。

在本博客中,我将分享对此漏洞的详细分析。

一、重现漏洞

要重现此漏洞,需要运行 Microsoft Outlook,然后单击 " 规则 =>管理规则和警报 =>选项 =>导入规则 ",并选择导致 Outlook 崩溃的 PoC 文件。

图 1. 重现漏洞

发生崩溃时,调用堆栈如下所示:

图 2. 崩溃发生时的堆栈

二、漏洞分析

正如从调用堆栈中看到的那样,崩溃发生在堆释放时。由于我们现在无法确认释放的堆块有什么问题,我们可以打开整页堆来跟踪有问题的堆块。命令如下:

YOUR_WINDBG_INSATALL_LOCATIONgflags.exe /p /enable outlook.exe /full

可以看到如下返回结果,表明它已成功执行。

图 3. 完整页面堆已成功打开

完成此操作后,我们可以再次打开 Outlook 并选择 PoC 文件以便在发生崩溃时监视新堆栈:

图 4. 打开 Full Page Heap 时的崩溃位置

现在我们可以看到 ECX 指向的非零内存地址是不可读的,并且在将数据写入该内存地址时会发生异常。因此将数据写入未分配(或释放)的内存的可能性很高。可以通过检查内存页面分配来验证这个预测,我们可以看到内存仍然具有 Reserve 属性。这是截图:

图 5. 保留的内存页面

我们现在需要弄清楚程序为什么要将数据写入未使用的内存页面。通过静态分析,我们可以看到 ECX 的值来自 EDI,并且在调用 MAPIAllocateBuffer 之后正在修改 EDI,如下面的屏幕截图所示:

图 6. ECX 值的来源

通过静态分析,我们了解到函数 MAPIAllocateBuffer 是 RtlAllocateHeap 的封装函数,它进行检查确保请求的堆大小参数不大于 0x7FFFFFF7。这意味着它不是负的。但是,在此情形之下,它不会检查 0 是否可以用作参数。并且因为实际分配的堆大小比请求的堆大小多 8 个字节,这 8 个字节用 0x0000000001000010 填充。之后,MAPIAllocateBuffer 在这 8 个字节后返回堆地址。因此,调用 MAPIAllocateBuffer 后的 EDI 值为 8 + 从 RtlAllocateHeap 接收的分配堆地址。截图如下:

图 7. 检查分配的堆的大小

图 8. 分配额外的 8 个字节

从上面的静态分析中,我们可以粗略地预测在 Reserve 堆中写入数据很大概率是由整数溢出引起的。结合调试,我们发现调用 MAPIAllocateBuffer 的堆大小参数确实为 0。但是,由于 MAPIAllocateBuffer 请求分配大小为 0 + 8 = 8 的堆,因此 RtlAllocateHeap 不会返回错误并成功返回正确的堆地址。但是,MAPIAllocateBuffer 使用这 8 个字节写入 0x0000000001000010,然后向用户返回无效的堆尾地址。截图如下:

图 9. 只减少一个字节,但堆是正确的

接下来,我们需要弄清楚为什么请求的堆大小的值会变为 0。结合调试和静态分析,我们发现值 0 来自当前函数的参数:arg_4(eax = arg_4 * 4 + 4)。但是,当调用当前函数时,arg_4 的值不是传入参数的值,这意味着此函数会修改 arg_4。通过调试我们可以看到更改是在子函数 sub_65F7DA 中完成的。截图如下:

图 10. 堆大小为 0 的源头

分析子函数 sub_65F7DA,我们发现它是另一个封装函数。经过一系列调试后,我们终于知道名为 ReadFile 的函数,即 arg_4 的值,实际上来自 PoC 文件。截图如下:

图 11. ReadFile 的封装函数

图 12. 调用 sub_65F7DA 之前 arg_4 的值

调试显示 arg_4 读取的文件中的内容为 0xFFFFFFFF,因此由于整数溢出,传递的堆的分配大小为 0xFFFFFFFF * 4 + 4 = 0。但是,程序没有检查这一点,导致后一个堆越界写入行为。截图如下:

图 13. 调用 sub_65F7DA 后 arg_4 的值

检查 PoC 文件,我们可以看到 0xFFFFFFFF 值确实存在。

图 14. PoC 文件中的 0xFFFFFFFF

将其修改为 0xAABBCCDD,我们再次执行调试并设置相同的断点以验证溢出是否由这 4 个字节引起。

图 15. 修改后的 PoC 文件

图 16. 测试修改后的 PoC 文件

所以我们找到了原因。

通过在 Patch 发布之后比较程序的汇编代码,我们可以看到现在已经添加了对所请求的分配堆大小的验证。请参见下面的截图:

图 17. 补丁之前和之后的比较

应用此修补程序至关重要,因为成功利用此漏洞的攻击者可以使用特制文件在当前用户的安全 context 中执行操作。

三、解决方案

鼓励存在漏洞的 Microsoft Outlook 版本的所有用户升级到最新的 Outlook 或立即应用最新的补丁。此外,已部署 Fortinet IPS 解决方案的机构已通过以下特征受到保护:

MS.Outlook.CVE-2018-8587.Remote.Code.Execution

13
发表评论
留言与评论(共有 0 条评论)
昵称:
匿名发表 登录账号
         
   
验证码:

相关文章

推荐文章

10
11
'); })();