In September 2018, the Zero Day Initiative published a proof of concept for a vulnerability in Microsoft’s Jet Database Engine. Microsoft released a patch in October 2018. We investigated this flaw at that time to protect our customers. We were able to find some issues with the patch and reported that to Microsoft, which resulted in another vulnerability, CVE-2019-0576, which was fixed on 8-Jan-2018 (Microsoft Jan 2019 Patch Tuesday).
The vulnerability exploits the Microsoft Jet Database Engine, a component used in many Microsoft applications, including Access. The flaw allows an attacker to execute code to escalate privileges or to download malware. We do not know if the vulnerability is used in any attacks; however, the proof of concept code is widely available.
Although the available proof of concept causes a crash in wscript.exe, any application using this DLL is susceptible to the attack.
The following error message indicates the vulnerability was successfully triggered:
The message shows an access violation occurred in the vulnerable DLL. This vulnerability is an “out-of-bounds write,” which can be triggered via OLE DB, the API used to access data in many Microsoft applications. This type of vulnerability indicates that data can be written outside of the intended buffer, resulting in a crash. The cause of the crash is the maliciously crafted Jet database file. The file exploits an index field in the Jet database file format with an unexpectedly large number, resulting in an out-of-bounds write and, ultimately, the preceding crash.
The following diagram provides a high-level view of how the exploit works:
Exploit in Action
As we see in the preceding image, we can review debug information to determine the function that crashes is “msrd3x40!TblPage::CreateIndexes.” Furthermore, we can determine that the program is trying to write data and failing. Specifically, we can see that the program is using the “esi” register to write to the location [edx+ecx*4+574h], but that location is not accessible.
We need to understand how this location is constructed to provide clues to the root cause. The debug information shows that register ecx contains the value 0x00002300. Edx is a pointer to memory that we will see again later. Finally, they are added together with an offset of 574 hexadecimal bytes to reference the memory location. From this information, we can guess the type of data that is stored there. It appears to be an array in which each variable is 4 bytes long and starts at the location edx+574h. While tracking the program, we determined the value 0x00002300 comes from the proof-of-concept file group1.
We know that the program attempts to write out of bounds and we know where the attempt occurs. Now we need to determine why the program attempts to write at that location. We investigate the user-provided data of 0x00002300 to understand its purpose. To do this we must understand the Jet database file.
Analyzing the Jet Database File
Many researchers have extensively analyzed the Jet database file structure. Some of the details of previous work can be found at the following links:
To summarize, a Jet database file is organized as a collection of pages, as shown in the following image:
The header page contains various information related to the file:
After the header come 126 bytes, RC4 encrypted, with the specific key 0x6b39dac7, which is the same for every JetDB file. Comparing the key value with the proof-of-concept file, we can identify that group1 is a Jet Version 3 file.
Further examination leads to a Table Definition Pages section, which describes various data structures for a table. (Click here for details.)
The table definition data has various fields, including two of note: Index Count and Real Index Count.
We can determine the value of these in our proof-of-concept file. When we check this with the group1 file, we see following:
There are total of two indexes in the Index Count. When we parse both indexes we see the familiar value of 0x00002300:
Our offending value 0x00230000 is the index number for index2 in the table. This index seems rather large and leads to the crash. Why does it crash the program? Further parsing the file, we find the names of the two indexes:
With a debugger attached, we can see that first program calls the function “msrd3x40!operator new.” This allocates memory that stores the memory pointer address in eax:
After the memory is allocated, the program creates the new index:
This index number is used later in the execution. The function msrd3x40!Index::Restore copies that index number to the index address + 24h. This process is repeated in a loop for all indexes. First it calls the “new” operator, which allocates the memory. It then creates an index on that address and moves the index number to the base address of the index +24h. We see this move in the following code, which shows the malicious index value copied to newly created index:
Once successfully moved, the function msrd3x40!NamedObject::Rename is called and copies the index name value to the index address +40h:
If we look at the esi register, we see it points to the address of the index. The ecx register has a value of [esi+24h], which is the index number:
After a few more instructions, we can observe the original crash instructions. Edx points to the memory location. Ecx contains a very large number from the file group1. The program tries to access memory at location [edx+ecx*4+574h], which will cause the out-of-bounds write and the program crashes:
What is happening with the data the program tries to write? If we watch the instructions, we see that program tries to write the value of esi to [edx+ecx*4+574]. If we print esi or the previous value, we see that it contains the index name ParentIdName, which we saw in group1:
Ultimately, the program crashes while trying to process ParentIDName with a very large index number. The logic:
- Allocate the memory and get the pointer to the start of the memory location.
- From the start of memory location +574h, the program saves pointers to index names with each occupying 4 bytes multiplied by the index number mentioned in the file.
If the index number is very large, as in this case, and no validation is done, then the program will try to write out of bounds and crash.
This is a logic error and such errors are sometimes hard to catch. Many developers take extra precautions to avoid these types of bugs in their code. It is even more unfortunate when these bugs lead to serious security issues such as with CVE-2018-8423. When these issues are discovered and patched, we recommend applying the vendor patch as soon as possible to reduce your security risks.
Microsoft patches can be downloaded and installed from the following locations for respective CVEs:
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 malicious file as BackDoor-DKI.dr .
McAfee HIPS, GBOP (Generic Buffer Overflow Protection) feature might cover this, depending on the process used to exploit the vulnerability.
We thank Steve Povolny of McAfee’s Advanced Threat Research team, and Bing Sun and Imran Ebrahim of McAfee’s Hybrid Gateway Security team for their support and guidance with this analysis.