Anatomy of an exploit – inside the CVE-2013-3893 Internet Explorer zero-day – Part 2

GO TO PART:   ←Prev   1   2   

In Part One of this article, we looked at how the Internet Explorer (IE) exploit known as CVE-2013-3893 got its foot in the door of Windows, if you will pardon the pun.

In Part Two, we are going to follow the exploit as it takes over IE, suppresses Data Execution Prevention (DEP), and reaches a point where it can run pretty much any program code it likes just as if you had downloaded it yourself.

We think this exploit makes a good example for study, because:

  • It can theoretically be used against IE from version 6 to 11.
  • It works despite DEP and ASLR (Address Space Layout Randomisation).
  • It is already widely circulated, so we aren’t giving away secrets.
  • The vulnerability on which it relies is already patched.

If you haven’t yet read Part One, we suggest that you do so now, as it explains where we will be starting this time, and how we got here.

The story so far

Our test system is running IE 9 on Windows 7 32-bit, and at the end of Part One, our attackers had:

  • [1] Used malicious Javascript to fill known memory addresses with chosen binary data.
  • [2] Loaded a known DLL at a fixed memory address, despite ASLR.
  • [3] Crashed IE in such a way as to cause it to jump to a memory location of their choice from [1].

Visually, our attackers are here:

Controllable knowns

As we explained in Part One, the attackers used JavaScript to allocate and free up a series of text strings to provoke a use-after-free bug in IE.

The bug caused Microsoft’s HTML renderer to use untrusted text string data from the malicious JavaScript to tell IE where to jump in memory, leaving the crooks with three “controllable knowns”:

  • Execution is about to jump to the memory address stored in location 0x121212D6, shown in yellow.
  • The addresses shown in grey, from 0x12121212 onwards, are precisely controlled by a text string in the untrusted JavaScript.
  • The DLL hxds.dll, part of Office, is loaded into executable memory at the unrandomised address 0x51BD0000.

Deciding where to go next

The attackers are about to jump to the address specified in 0x121212D6, and they control that value directly from their JavaScript.

With the memory layout shown above, where 0x12121212 has been written repeatedly, the attackers can’t actually get control of IE.

That’s because the address stored in 0x121212D6 (shown in yellow above) is in heap memory that is protected by DEP, and provokes an access violation if it executed:

But if the crooks choose an address inside hxds.dll, they will reach memory marked a executable, and they will know what code is going to execute next, because the address space of that DLL isn’t randomised.

In the actual exploit, the address stored at location 0x121212D6 (the yellow bytes below) is 0x51BD28D4, as shown here:

→ Don’t forget that the x86 and x64 Intel CPUs used in Windows computers are little endian. That means that the least significant byte of a multi-byte value is stored at the lowest memory address, and so on. So the 32-bit value 0xC001D00D would actually appear in memory as the bytes 0D D0 01 0C), just as 0x51BD28D4 appears above as D4 28 BD 51.

If we disassemble the code at the address chosen by the criminals, we get this:

MOV  EAX, [ECX] ; Fetch the contents of the 
                ; memory address in ECX,
                ; where ECX is controlled
                ; by the attackers
CALL [EAX+8]    ; Call the location specified 
                ; in the address 8 bytes past that.

This time, the two lines of code above cause the following chain of execution:

The value of ECX above was forced to the value 0x12121202 by the attacker’s malicious JavaScript, and the contents of the memory block at and around the addresses shown above (from 0x1212111E0 to 0x12121310) were set up by the crooks in the same way.

At the moment, the attackers control the instruction pointer (EIP), but can’t yet aim it at their own machine code because of DEP.

The next best thing, then, is to control the stack pointer (ESP), because the stack lets you set up calls to system functions.

Pivoting the stack

The value chosen for the destination of the CALL [EAX+8], shown in green above, is critical to the rest of the exploit, and gives the attackers control of the stack by means of what is called a stack pivot.

The pivot for this exploit can be seen by disassembling at 0x51BE4A41:

XCHG EAX,ESP  ; Put EAX into ESP, and vice versa

A stack pivot is just a fancy name for any machine code instruction sequence that sets ESP to an attacker-controlled value: it could be a MOV, a PUSH followed by POP, or, as here, an XCHG instruction that swaps the values in EAX and ESP.

The attackers can now use a trick called Return Oriented Programming, or ROP, to control the flow of code execution indirectly.

That’s because the stack now consists of the bytes shown in grey here:

Converted from little endian notation and listed as a vertical stack of 32-bit addresses and their contents, we get this:

12121212:  51C3B376  <--ESP points here after pivot
12121216:  51C2046E
1212121A:  51BE4A41
1212121E:  51C2046E
12121222:  51BD10B8
12121226:  51C0E455
1212122A:  51C3B376
1212122E:  51BD71F4
12121232:  121212DA
12121236:  12121212
1212123A:  00001000
1212123E:  00000040
12121242:  12120A0C
12121246:  51C3B376
1212124A:  51C3B376
1212124E:  51C3B376
. . . . 

Thanks to the stack pivot, the attackers are about to execute a RET instruction with the stack pointer aimed at the topmost value in the list above.

Since RET, or “return from subroutine”, pops the value off the top of the stack and jumps to it, the attackers will now leap back into a carefully chosen instruction sequence inside hxds.dll.

In fact, you’ll notice that the topmost eight values on the stack are all addresses inside hxds.dll, so if each of the instruction sequences pointed to by those addresses ends with a RET, the attackers will execute a stitched-together series of instructions of their choice.

That’s not as convenient as simply putting the machine code they want right in their exploit data, but it’s the next best thing, and it’s where ROP gets its name.

→ In exploit literature, each instruction-snippet-plus-RET pointed to by a list of ROP addresses is known as a gadget. A string of ROP gadgets makes a ROP chain or program. ROP programs typically end up following a Byzantine execution sequence, leaping hither and thither in a DLL that hasn’t had its location randomised. This apparent complexity is irrelevant to the CPU, of course, which simply goes where it is told, and does what it is instructed.

The ROP gadget chain

Here’s what we get if we disassemble the gadgets at each of the addresses on the stack:

The chart looks rather complex, but the results are surprisingly straightforward:

  • [1] The first step simply returns to the next ROP gadget, like a NOP (no-operation) instruction.
  • [2] The POP EDI in step [2] serves merely to skip over the next gadget address (the already-used stack pivot); the value stored in EDI is irrelevant.
  • [3] This time the POP instruction loads EDI with the data value 0x51BD10B8 off the stack, and that value is important.
  • [4] Now EAX is loaded with the value stored at 0x51BD10B8. The POP EDI is redundant, but couldn’t be avoided by the attackers, who have to work with the gadget sequences available in hxds.dll.
  • [5] The address loaded into EAX is used as a function pointer, and called by the ROP program by PUSHing it on the stack and then jumping to it with a RET instruction.

Notice that when the final RET in step [5] is processed, the top five values on the stack, denoted [P] in the chart above, are as follows:

12121232:  121212DA
12121236:  12121212
1212123A:  00001000
1212123E:  00000040
12121242:  12120A0C

That leaves three vital questions: what is the memory address stored in location 0x51BD10B8, why did the attackers choose it, and what are the [P] values for?

Neutralising DEP

On our test system, the address stored at location 0x51BD10B8 was 0x759F50AB; when disassembled, it turns out to be the entry point of the function VirtualProtect() in the core system library kernel32.dll:

Even though Windows randomises where this function is loaded, in order to make it hard to find (for reasons which are about to become obvious), the attackers can nevertheless locate it.

That’s because the variable location of the randomised entry point is saved at a fixed location in the unrandomised library hxds.dll.

Understanding system calls

Under 32-bit Windows, system calls are made with the stack set up as follows:

[ESP]    -> Return address in calling program
[ESP+04] -> Parameter 1 passed to system call
[ESP+08] -> Parameter 2
[ESP+0A] -> Parameter 3
[ESP+0C] -> Parameter 4 
. . . .  etc.

→ When preparing for a system call, the parameters are PUSHed onto the stack in reverse order. (The stack grows upwards, towards lower memory addresses, in the diagram above.) That makes it easier to support functions with a variable number of arguments, since the first parameter is always 4 bytes down the stack; the second 8, and so on, regardless of how many arguments there are altogether.

Now the [P] values in the bottom-most section of the ROP chart above can be decoded, because they are the four parameters passed into, and the return address from, the function VirtualProtect():

This means our attackers are on the point of changing the memory protection for their exploit data, like this:

The memory area they will re-protect is the 4KB block starting at 0x12121212, which will end up with the protection permissions PAGE_EXECUTE_READWRITE.

The memory address 0x12120A0C is used to save the previous protection setting; the crooks don’t have any use for this information (the exploit doesn’t tidy up after itself), but the VirtualProtect() function won’t work without it.

And the return address, 0x121212DA, is the beginning of the memory block shown in blue below, immediately following the yellow value at 0x121212D6, where the exploit started off:

Launching the shellcode

When our attackers return from VirtualProtect(), they will effectively have regressed the protections in Internet Explorer to be much like they were under IE 6 on Windows XP2 and earlier.

They will no longer need ROP gadgets to execute code snippets inside hxds.dll: their own shellcode, shown in blue above, will run directly out of heap memory once DEP protection is removed.

And because their malicious executable code will run without triggering any dialog boxes or “are you sure” warnings that would tip off a well-informed user, they’re all set for arbitrary Remote Code Execution.

GO TO PART:   ←Prev   1   2