今年早些时候,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
留言与评论(共有 0 条评论) |