Those of you that follow my blog know that I am not accustomed to publishing the exploit code for critical vulnerabilities. I'm only publishing this article because a long time has passed since the vulnerability used here was patched and because I believe that the techniques used in this case study (windows 7 exploitation without relying on non-ASLR module and without using a secondary vulnerability for memory disclosure) are sufficiently different to be of interest to the security community. It is my hope and intention that the code given below will be used for educational purposes only.
Introduction
Reliable exploitation of browser vulnerabilities has been made increasingly difficult by introducing protection mechanisms such as DEP and ASLR. While it can be shown that ASLR can easily be defeated without DEP (for example by heap spraying) and DEP can easily be defeated without ASLR (for example by return-oriented programming), the combination of the two makes reliable exploitation much harder. This is probably the reason why there hasn't been much work published about the exploiting vulnerabilities on Windows 7.
Two most common techniques for exploiting vulnerabilities on Windows 7 are:
1. Using a secondary vulnerability to perform memory disclosure and reveal the address of an executable module in memory prior to using the "main" vulnerability to execute the payload. For example, Peter Vreugdenhil used this techniques in his Internet Explorer 8 on Windows 7 exploit in the Pwn2Own 2010 contest [1].
2. Making the application load a non-ASLR-enabled module in the memory, for example msvcr71.dll [2].
In this case study, I'll describe the development of Internet Explorer 8 exploit on Windows 7 without relying on the techniques above. The main idea of my exploit is using the same vulnerability to achieve both memory disclosure and code execution.
While, at first, it may seem that the techniques described below can only be used with a select few vulnerabilities, in my experience, many browser vulnerabilities can be used to achieve both memory disclosure and code execution using the techniques described bwlow. Just to name a few vulnerabilities I discovered, besides CVE-2011-1999 which will be used as an example in this case study, CVE-2010-1883 and CVE-2008-3475 (and probably others) could also be used to accomplish both memory disclosure and code execution. In fact, the most critical condition a vulnerability must satisfy in order to be applicable is that the attacker must be able to trigger it multiple times without crashing the vulnerable application. Most of the vulnerabilities that satisfy this condition can be used both for memory disclosure and for code execution, at least in the web browser context.
The vulnerability
The vulnerability that I'm going to use in this case study affected Internet Explorer 8. It has been disclosed as CVE-2011-1999 (MS11-081) and patched in October 2011.
The bug that causes the vulnerability is in incorrectly validating an integer parameter passed to the options.add method of a Select element object. This method is used to add an Option element to the Select element and it accepts two parameters:
1. An Option object to be added
2. An integer, specifying the index of the new Option object
Among other things, the options.add method maintains an array of pointers to the Option objects inside a corresponding Select object. This array is called the option cache.
Under certain conditions, the options.add method incorrectly validates the second parameter. If it is a negative number, instead of causing an exception or changing it to zero, the method will attempt to add a pointer to the Option object (passed at the first parameter) in the option cache at a negative index (negative offset from the beginning of the option cache).
This can be demonstrated using the following sequence of JavaScript instructions:
var s = document.createElement("select");
var o = document.createElement("option");
s.options.add(o,-0x20000000);
This causes the browser to crash as shown in the following image
Basically, Internet Explorer attempts to add a pointer to the Option ('o' in the code above) at address
[address of option cache]+(-0x20000000)*4
In assembly, that corresponds to the instruction
MOV DWORD PTR DS:[EAX+EDI*4],ECX
where EAX is a pointer to the option cache, ECX is the pointer to the Option object, and EDI is the index (-0x20000000 aka 0xE0000000).
The call stack at the moment of crash looks like
CImplPtrAry::Insert(int, void *)
CSelectElement::AddOptionHelper(class COptionElement *, long, bool)
CSelectElement::ie8_add(IHTMLOptionElement *,tagVARIANT *)
From all of the above, it follows that, by manipulating the second parameter of the options.add method, we can overwrite an arbitrary (dword-aligned) memory location with an address of an Option object.
But what address should we overwrite in light of ASLR and other browser protection mechanisms? This will be discussed in the following sections.
Memory disclosure
In order to defeat ASLR we basically need the ability to read the memory of the current process in order to determine the address of some executable module. However, memory disclosure won't be used only to defeat ASLR, but also to increase the reliability of the exploit as we won't have to make many guesses regarding the memory layout. The technique I used to leverage this vulnerability into memory disclosure has been described in a separate blog post. You can read about it in detail here. In fact, this blog post should be considered the first part of this case study. The main idea of using this vulnerability for memory disclosure is to use it to overwrite a DWORD that contains the length of some JavaScript string. After this, we can use the substr method of this string to make memory read queries.
One important thing to note is that this memory disclosure technique will allow us to read a large portion of the memory after some heap spray, so we need to make sure to "plug" any memory holes before this heap spray, so that anything important to our exploit will be allocated after the heap spray.
Exploiting the vulnerability
In order to achieve reliable exploitation on Windows 7 I used the technique that will be described below. This technique relies on two heap sprays:
1. The first heap spray has two purposes. Its first purpose is to enable the memory disclosure as described in the previous Section. Its second purpose is to hold a NOP slide followed by a shelcode. Note that, in order to defeat DEP, before calling the shellcode, we'll need to make a memory block holding it executable.
2. The second heap spray will act as a fake stack after we get the control of EIP. Basically, we are going to make stack pointer point somewhere in this heap spray. We need to obtain the control of the stack in order to use return-oriented programming and defeat DEP.
Note that, if we were making the exploit for Windows XP (or some other system without ASLR), a single heap spray would be sufficient. However, in our case we won't know the values of the return addresses we need to put on the stack until after we make the first heap spray.
The exploit steps are roughly given below.
1. Do the first heap spray
2. Allocate some Option and Select objects. It is important that these objects are allocated after the first heap spray in order for them to become readable
3. Enable memory disclosure by overwriting length of a string in the first spray (see the previous Section for details)
4. Use memory disclosure to read a vtable pointer of some option object. The base address of mshtml.dll can be computed by subtracting a constant offset from the vtable pointer.
5. Do the second heap spray (details will be given later)
6. Create an Option object. Let's call it A
7. Find an address of some other Option object (let's call it B) in memory and overwrite its CTreeNode pointer with the address of A. The address of B can be determined via memory disclosure (option cache is a good place to look).
8. Delete A
9. Allocate a string of the same size as A. Hopefully, it will be allocated at the same address where A used to be.
10. Read the memory where A used to be to determine if we successfully wrote our string there. If not, go back to step 6. Note that at this time, CTreeNode pointer of object B points to a string whose content we control.
11. Do something with B that would access its CTreeNode pointer and eventually attempt to call a virtual method of some object in the CTreeNode hierarchy. (In the exploit, I used B.parentNode.Click() to accomplish this).
Now lets see what happens if we fill a string in step 7 with a pattern
0x41 0x41 0x41 0x41 ...
This is shown in the following image.
We see that we control EAX and we came to the following sequence of instructions
3D093A73 8078 08 52 CMP BYTE PTR DS:[EAX+8],52
...
3D093A84 8B00 MOV EAX,DWORD PTR DS:[EAX]
...
3D093A88 8BF0 MOV ESI,EAX
...
3D093A8E 8B06 MOV EAX,DWORD PTR DS:[ESI]
...
3D093A91 FF90 DC000000 CALL DWORD PTR DS:[EAX+DC]
As we control EAX, we will make it point somewhere in the second heap spray, where we can predict the content. We'll make the second heap spray organized in patterns of size 0x1000, so because we know that each memory block begins at address divisible by 0x1000, some address in the form k*0x1000+0x24 is likely to hold the beginning of the pattern (extra 0x24 is for memory block header and string length). So, for example, if EIP is 1C1C0024 it is likely that it will point to the beginning of a pattern in the second heap spray.
first, we need to resolve the following line
CMP BYTE PTR DS:[EAX+8],52
Here, we just need to make sure that EAX+8 is readable and the content there is not 52. Pretty simple, just make sure that pattern+8 is not 52.
Next, we come to
MOV EAX,DWORD PTR DS:[EAX]
Here, we'll just make sure [EAX] points back into the pattern
Specifically, we'll make [EAX] point to pattern_address+0x10, so EAX is again controlled after this instruction
Now, EAX points to pattern_address+0x10
Next, we come to
MOV ESI,EAX
MOV EAX,DWORD PTR DS:[ESI]
Again, we'll just make it so that, after this is executed, EAX again points somewhere in the pattern.
Specifically, we'll make pattern_address+0x10 point to pattern_address+0x14
Now, EAX points to pattern_address+0x14, and we arrived at the
CALL DWORD PTR DS:[EAX+DC]
Which obviously gives us the control over EIP.
Now, if DEP wasn't enabled, we could just make the jump for our nop slide followed by a shellcode (in the first heap spray). However, as DEP is enabled in Internet Explorer 8 we need to first gain control over the stack and use return-oriented programming to make shellcode executable.
In order to gain control of the stack we'll make [EAX+DC] point to the following sequence of instructions:
XCHG EAX,ESP
RETN
Such sequence is easily located in most modules because both instructions are only a single byte in size (and that means that there will be plenty such values, even if the author of the module didn't intend it). In light of the ASLR, the address of this sequence (and any other sequence of instructions) has to be determined dynamically as [base of mshtml.dll]+[offset]. Remember that we computed the base address of mshtml.dll in step 4 of the exploit.
Now we control the stack and also the next function we'll return into.
What we need to do now is make shellcode executable. In order to do so we'll call VirtualProtect. But we don't know the address of VirtualProtect as VirtualProtect is not a part of mshtml.dll. However, mshtml.dll does contain an address of VirtualProtect in its import section.
So, instead of a direct return into VirtualProtect, we'll first return into
POP EAX
RETN
Here, we'll put the address which contains the address of VirtualProtect into EAX (remember that we control the stack).
This code will then return into
CALL DWORD PTR DS:[EAX]
RETN
so VirtualProtect gets called here. The stack has to be constructed so that, at this point, arguments of VirtualProtect are correctly aligned.
Those are
1) Beginning address of the memory block (we'll choose some block in the first heap spray that we'll return into later)
2) Block size (0x100000)
3) New set of permissions (0x40 for PAGE_EXECUTE_READWRITE)
4) Address where old set of permissions will be stored (some address in the first heap spray we don't need)
Stack also has to be constructed so that after the call to VirtualProtect, we return into a nop slide that we just made executable.
So, finally the pattern used in the second heap spray for will look like
[pattern_address+0x10][0xAA]*12[pattern_address+0x14][address of POP EAX;RETN][address of address of VirtualProtect][address of CALL [EAX];RETN][address of block in the 1st heap spray][block size][0x40][some address in the 1st heap spray][address of a nop slide on the 1st heap spray][0xAA]*196[address of XCHG EAX,ESP; RETN]
Using this pattern we can make shellcode executable and return into it as well.
One additional thing to note when developing Windows 7 exploits is that many shellcode won't work on Windows 7 and cause a crash instead. For my exploit I used SkyLined's Windows 7 compatible 'calc' shellcode, that can be found at http://code.google.com/p/w32-exec-calc-shellcode/
PoC code
PoC code can be seen at http://seclists.org/bugtraq/2012/Feb/178.
References
[1] Peter Vreugdenhil, Pwn2Own 2010 Windows 7 Internet Explorer 8 exploit, http://vreugdenhilresearch.nl/Pwn2Own-2010-Windows7-InternetExplorer8.pdf