Introduction
As of July 2019, Microsoft has fixed around 43 bugs in the Jet Database Engine. McAfee has reported a couple of bugs and, so far, we have received 10 CVE’s from Microsoft. In our previous post, we discussed the root cause of CVE-2018-8423. While analyzing this CVE and patch from Microsoft, we found that there was a way to bypass it which resulted in another crash. We reported it to Microsoft and it fixed it in the January 19 patch Tuesday. This issue was assigned CVE-2019-0576. We recommend our users to install proper patches and follow proper patch management policy and keep their windows installations up to date.
In this post we will do the root cause analysis of CVE-2019-0576. To exploit this vulnerability, an attacker needs to use social engineering techniques to convince a victim to open a JavaScript file which uses an ADODB connection object to access a malicious Jet Database file. Once the malicious Jet Database file is accessed, it calls the vulnerable function in msrd3x40.dll which can lead to exploitation of this vulnerability.
Background
As mentioned in our previous post, CVE-2018-8423 can be triggered using a malicious Jet Database file and, as per the analysis, this issue was in the index number field. If the index number was too big the program would crash at the following location:
Here, ecx contains the malicious index number. On applying the Microsoft patch for CVE-2018-8423 we can see that, on opening this malicious file, we get the following error which denotes that the issue is fixed, and the crash does not occur anymore:
Analyzing the Patch
We decided to dig deeper and see exactly how this issue was patched. On analyzing the “msrd3x40!TblPage::CreateIndexes” function, we can see that there is a check to see if “IndexNumber” is greater than 0xFF, or 256, as can be seen below:
Here, the ecx which contains the index number has the malicious value of “00002300” and it is greater than 0xFF. If we see the code, there is a jump instruction. If we follow this jump instruction, we reach the following location:
We can see that there is a call to the “msrd3x40!Err::SetError” function, meaning the malicious file will not be parsed if the index value is greater than 0xFF and the program will give the error message “Unrecognized database format” and terminate.
Finding Another Issue with the Patch
By looking at the patch, it was obvious that program will terminate if the index value is greater than 0xFF, but we decided to try it with an index value “00 00 00 20” which is less than 0xFF, and we got another crash in the function “msrd3x40!Table::FindIndexFromName”, as can be seen below:
Finding the Root Cause of the New Issue
As we know, if we give any index value which is less then 0xFF, we get a crash in the function “msrd3x40!Table::FindIndexFromName”, so we decided to analyze it further to find out why that is happening.
The crash is at the following location:
It seems that program is trying to access location “[ebx+eax*4+574h]” but it is not accessible, meaning it is an Out of Bound Read issue.
This crash looks familiar as it was also seen in CVE-2018-8423, except that it was an Out of Bound Write, while this seems to be an Out of Bound Read. If we look at eax it contains “0055b7a8” which, when multiplied by 4, becomes a very large value.
If we look at the file it looks like this:
As can be seen in below image, if we parse this file, this value of “00 00 00 20” (in little endian from the above image), denotes the number of an index whose name is “ParentIDName”:
Looking at the debugger at the point of the crash, it seems that ebx+574h points to a memory location and eax contains an index value which is getting multiplied by 4. Now we need to figure out the following:
- What will be the value of eax that will cause the crash? We know that it should be less than 0xFF. But what would be the lowest value?
- What is the root cause of this issue?
On setting a breakpoint on “msrd3x40!Table::FindIndexFromName” and changing the index number to “0000001f”, (which does not cause a crash but helps with the debugging and understanding the program flow) we can see that edx contains the pointer to an index name which, in this case, is “ParentIdName”:
Debugging further we can see that the eax value comes from [ebp] and the ebp value comes from [ebx+5F4h] as can be seen below:
When we look at “ebx+5F4” we can see the following:
We can see that “ebx+5F4” contains the index number for all the indexes in the file. In our case the file has two indexes and their number are “00 00 00 01” and “00 00 00 1f”. If we carefully review the memory we can figure out that the maximum number of indices which can be stored here are 0x20, or 32:
Start location: 00718d54
Each index number is 4 bytes long. So 0x20*4 + 00718d54 = 00718DD4
After this, if we look at ebx+574+4, we can see that it contains the pointer to index names:
So, the overall memory structure is like this:
There are only 0x80 or 128 bytes available to save index name pointer at location EBX+574. Each pointer gets saved at an index number location, i.e. for index number 1 it will be saved at EBX+574+1*4, the location for index number 2 will be saved at EBX+574+2*4 and so on. (index number starts from 0).
In this case, if we give an index number which is more than 31, the program will overwrite data past 0x80 bytes, which will be at the start of the EBX+5F4 location, which is the index number from the malicious file. So, in this case, if we give the value “00 00 00 20” instead of “00 00 00 1f”, it will overwrite the index number at the EBX+5F4 location, as can be seen below:
Now the program tries to execute this instruction in “msrd3x40!Table::FindIndexFromName’
Mov ecx, dword ptr [ebx+eax*4+574h]
Here, eax contains the index number which should be “00 00 00 01” but, since it is overwritten by “0055b7a8” which is a memory address, on multiplying it with 4, it becomes a huge number and then 574h is getting added to it. So, if that memory area does not exist and the program tries to read from that memory, we get an access violation error.
So, to answer the questions we had:
- Any value which is less then 0xFF and greater then 0x31 will cause a crash if the resulting memory location from [ebx+eax*4+574h] is not accessible.
- The root cause is that an index number is getting overwritten by a memory location, causing invalid memory access in this case.
How is it Fixed by Microsoft in the Jan 19 Patch?
We again decided to analyze the patch to see how this issue was fixed. As is clear from the analysis, any value which is greater than or equal to 0x20 or 32 still causes a crash so, ideally, the patch should be checking this. Microsoft has added this check in the Jan 19 patch release, as can be seen below:
As can be seen in the above image, eax hold the index value here and it is compared with 0x20. If it is more than or equal to 0x20 the program jumps to location 72fe1c00. If we go to that location, we can see the following:
As can be seen in the above image, it calls the destructor and then calls msrd3x40!Err::SetError function and returns. So, the program will display a message saying, “Unrecognized database format” and then terminate.
Conclusion
We reported this issue to Microsoft in October 2018 and it fixed this issue in the Jan 19 patch Tuesday. It was assigned CVE-2019-0576 to this issue. We recommend our users keep their Windows installations up to date and install vendor patches on a regular basis.
McAfee Coverage:
McAfee Network Security Platform customers are protected from this vulnerability by Signature IDs 0x45251700 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2018-8423) and 0x4525890 – HTTP: Microsoft JET Database Engine Remote Code Execution Vulnerability (CVE-2019-0576).
McAfee AV detects the malicious file as BackDoor-DKI.dr .
McAfee HIPS, Generic Buffer Overflow Protection (GBOP) feature will often cover this, depending on the process used to exploit the vulnerability.
References