FortiGuard Labs Threat Analysis Report
Earlier this year, Fortinet's FortiGuard Labs researcher Yonghui Han reported a Heap Corruption vulnerability in Office Outlook to Microsoft by following Fortinet’s responsible disclosure process. On Patch Tuesday of December 2018, Microsoft announced that they had fixed this vulnerability, released a corresponding advisory, and assigned it the vulnerability identifier CVE-2018-8587.
Microsoft Outlook is one of the components of the Microsoft Office suite that is widely used to send and receive emails, manage contacts, record and track schedules, and perform other tasks. The Heap Corruption vulnerability was found in multiple versions of Outlook running on Windows, covering all 32/64-bit versions of the software from Outlook 2010 to the latest Outlook 2019, as well as Office 365 ProPlus. The vulnerability can be triggered by a malformed RWZ (mail classification rules) file. When Outlook receives incorrect RWZ file content, it allocates too little heap memory and lacks appropriate boundary checks, resulting in Out of Bounds Writing of the heap.
In this blog I will share a detailed analysis of this vulnerability.
Reproduce the Vulnerability
To reproduce this vulnerability, we need to run Microsoft Outlook, then click "Rules => Manage Rules&Alerts => Options => Import Rules" and select the PoC file which causes Outlook to crash.
The following is the call stack when the crash occurs:
Analyze the Vulnerability
As we can see from the call stack, the crash occurs when the heap block is released. Since we cannot confirm what’s wrong with the freed heap block for now, we can open the Full Page Heap to trace the offending Heap block. The command is as follows:
YOUR_WINDBG_INSATALL_LOCATION\gflags.exe /p /enable outlook.exe /full
You can see the following returned result, indicating that it has been executed successfully.
After this operation we can open Outlook again and select the PoC file to monitor the new stack when the crash occurs:
Now we can see that the non-zero memory address that ECX points to is not readable, and an exception occurs when writing data to that memory address. There is a high probability that data is trying to be written to an unallocated (or freed) memory address. We can verify this prediction by checking the memory page allocation, where we can see that the memory still has the Reserve property. Here is the screenshot:
We now need to figure out why the program is writing data to an unused memory page. Through static analysis we can see that the value of ECX comes from EDI, and EDI seems to be being modified after calling MAPIAllocateBuffer, as shown in the screenshot below:
Through static analysis we learn that the function MAPIAllocateBuffer is a wrapper function of RtlAllocateHeap, which checks to ensure the requested heap size parameter is not greater than 0x7FFFFFF7. This means it’s not negative. In this case, however, it does not check whether 0 can be used as parameter. And because the actual allocated heap size is 8 bytes more than the requested heap size, the 8 bytes are filled with 0x0000000001000010. After that, the MAPIAllocateBuffer returns a heap address after these 8 bytes. Therefore, EDI’s value after calling MAPIAllocateBuffer is 8 + the allocated heap address received from RtlAllocateHeap. The screenshot is as follows:
From the above static analysis, we can roughly predict that the high probability of writing data in the Reserve heap is caused by an integer overflow. Combined with debugging, we find that, indeed, the heap size parameter of calling MAPIAllocateBuffer is 0. However, because MAPIAllocateBuffer requests allocation of the heap with size 0+8=8, RtlAllocateHeap will not return an error and successfully returns the correct heap address. However, MAPIAllocateBuffer uses those 8 bytes to write 0x0000000001000010 and then returns an invalid heap-tail address to the user. The screenshot is as follows:
Next, we need to figure out why the value of the requested heap size becomes 0. Combined with debugging and static analysis, we find that the value 0 results from a parameter of the current function: arg_4 (eax = arg_4 * 4 + 4). But, when the current function is called, the value of arg_4 is not the value of the incoming parameter, which means this function modifies the arg_4. Through debugging we can see that the change is done in child function sub_65F7DA. The screenshot is as follows:
Analyzing the sub-function sub_65F7DA, we find that it is another wrapper function. After a series of debugging, we finally know that the function called ReadFile—that is, the value of arg_4—actually comes from the PoC file. The screenshot is as follows:
Debugging shows that the content in the file read by arg_4 is 0xFFFFFFFF, so the allocation size of the heap passed on is 0xFFFFFFFF * 4 + 4 = 0 due to integer overflow. However, the program did not check this, resulting in the latter heap Out-of-Bounds Writing behavior. The screenshot is as follows:
Checking the PoC file, we can see that the 0xFFFFFFFF value does exist.
Modifying it to 0xAABBCCDD, we perform debugging again and set the same breakpoint to verify that the overflow is caused by these 4 bytes.
So we got it.
By comparing the program’s assembly code after the Patch release, we can see that the verification of the requested allocation heap size has now been added. See the screenshot below:
Applying this patch is critical since an attacker who successfully exploits this vulnerability could use a specially crafted file to perform actions in the security context of the current user.
All users of vulnerable Microsoft Outlook versions are encouraged to upgrade to the latest Outlook version or apply the latest patches immediately. Additionally, organizations that have deployed Fortinet IPS solutions are already protected from this vulnerability with the following signature:
Know your vulnerabilities – get the facts about your network security. A Fortinet Cyber Threat Assessment can help you better understand: Security and Threat Prevention, User Productivity, and Network Utilization and Performance.
Read about the FortiGuard Security Rating Service, which provides security audits and best practices.