Today, more and more exploit developers are using Return-Oriented-Programming (ROP) techniques to bypass the Data Execution Prevention (DEP) feature in recent versions of Windows. In order to successfully launch an attack using ROP, one must know the fixed base address of the targeted module. However, Address Space Layout Randomization (ASLR), another security feature, makes it more difficult for an attacker to predict target addresses by randomly arranging the locations of key data areas.
There are several methods to bypass ASLR. For example, we have seen the use of just-in-time (JIT) spray attack wherein the JIT compiler of Adobe Flash Player was used to place large amounts of code in a system’s memory. We’ve also seen DLLs that did not have ASLR enabled (such as those associated with Java or .NET) targeting Internet Explorer (IE) 8 vulnerabilities in Windows 7. Information leakage can also give the attacker some useful information about the system’s memory layout, which can be used to bypass ASLR. Today, let’s take a look at a recent proof-of-concept (POC) exploit that uses both information leakage and ROP to bypass DEP+ASLR.
The original POC has already been published on the Exploit Database and has been used to attack a known vulnerability in Windows. This vulnerability was fixed with the January 2011 Patch Tuesday (see the Microsoft bulletin MS11-002). It was designated as CVE-2011-0027 and was discovered by Peter Vreugdenhil. Together with a separate use-after-free vulnerability, it defeated a fully patched Windows 7 system running IE 8, which allowed Vreugdenhil to win “Pwn2Own 2010.”
Although the exploit code is not 100% reliable, Vreugdenhil’s idea of using the vulnerability to leak information is still worth looking at.
Analysis of the Vulnerability
Let’s first look at the vulnerability itself. It’s a heap overflow in Microsoft Data Access Components. The vulnerability can be exploited via the use of a malicious site. The simplest code to do so would look like this:
If you open the file with IE on a system without the patches mentioned in MS11-002, your system will crash.
The .HTML file accesses what is called an XML Data Island. This actually acts as a database interface. The underlying object is an MSAdo object. A RecordSet object can be used to manipulate the XML data. RecordSet objects have a property called CacheSize, which indicates the size of a cache that stores the old rows that have been visited. For example, if the code visits the eighth row first then visits the first row, 8 will be stored in this cache.
Changing the CacheSize of a RecordSet has one effect. New memory for the cache buffer will be allocated with the allocation size calculated based on CacheSize: Allocation Size = (CacheSize * 4) + 4. Both are stored as 32-bit unsigned numbers. Therefore, if the CacheSize is very large, the resulting value will be larger than the variable can store. Therefore, the amount of allocated space will be much smaller than intended. The system will, however, continue to think it has allocated enough memory for the new CacheSize.
Let’s examine the code above in a debugger to make it clearer. The command localxmlid1.CacheSize = 0x40000358 changes the allocated cache size. To the debugger, we are now here:
esi=0x40000358 is the new cache size. It is stored in the memory location offset 0x10C from the CRecordSet object. However, the actual allocation occurs here:
Here, eax=0x40000358, which is the new CacheSize, is first multiplied by four, after which, four is added to the result. The final result is used as the actual allocation size and passed to MpHeapAlloc.
Let’s do the math. (0x40000358*4) + 4 = 0x100000D64. This is more than a 32-bit unsigned integer can store so only the least significant digits are stored: 0xD64, which is too small for the purpose.
Let’s continue the trace. The base address allocated for the cache buffer is 0x021112a8. Remember that it’s very small, only 0xD64 bytes. However, each execution of the for-do loop causes a 4-byte row number to be written to the cache buffer. This rapidly causes a buffer overflow, as we can see below. Note how the overflow consists of continuous row numbers:
Using the Vulnerability to Read Memory
If attackers can directly read key memory blocks, it will be possible for them to figure out the current layout of system’s memory (e.g., via VTables or OS data structures). This is what ASLR is supposed to prevent.
Most of the time, remote attackers do not have the capability to examine the contents of the user system’s memory as much as they would like. However, Vreugdenhil used the heap overflow vulnerability discussed above to do exactly that. While we won’t discuss the full details of the exploit we can explain the key idea behind it.
The ‘B’s here represent additional code in memory. Normally accessing the value of the first string gets only the intended value ‘AA.’ However, if we can overflow the terminator of the first string with row numbers, the memory will look like this. For the purposes of this example, the row number starts at 0x10120:
Careful examination of the code after the original string can be used to discover information that can be used for further exploitation.
Update: Clarified the circumstances of string storage.