深入分析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_LOCATION\gflags.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文件。截图如下:
今年早些时候,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文件。
copyright 无奈人生
图1.重现漏洞
发生崩溃时,调用堆栈如下所示:
图2.崩溃发生时的堆栈
二、漏洞分析
正如从调用堆栈中看到的那样,崩溃发生在堆释放时。由于我们现在无法确认释放的堆块有什么问题,我们可以打开整页堆来跟踪有问题的堆块。命令如下:
YOUR_WINDBG_INSATALL_LOCATION\gflags.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中完成的。截图如下:
copyright 无奈人生
图10.堆大小为0的源头
分析子函数sub_65F7DA,我们发现它是另一个封装函数。经过一系列调试后,我们终于知道名为ReadFile的函数,即arg_4的值,实际上来自PoC文件。截图如下:
本文来自无奈人生安全网