The mechanism behind Internet Explorer CVE-2014-1776 exploits

Recently Microsoft patched an Internet Explorer use-after-free bug (CVE-2014-1776) that was being exploited in the wild. Since then I’ve seen several reports of new variants based on the original exploit appearing ITW. Let’s look deep inside the exploitation mechanism to see how it works to make a use-after-free execute shellcode.

The whole exploit is made up of three parts. (Figure 1) The entry HTML is where the victim first visits when he receives a phishing email and clicks the link. The HTML loads a SWF file that performs a heap spraying operation. It calls back a Javascript function with de-obfuscated exploit Javascript code and the code manipulates the DOM structure of the main HTML file to trigger a use-after-free bug.

 

 fig01.png

Figure 1 Exploit components

 

Heap-spray

First things first, before the exploit actually triggers the vulnerability, it sprays the array of the heap with a Vector of uint type. The Vector is filled with various uint values. (Figure 2)

 

fig02.png

Figure 2 Heap spray code from SWF component

 

You can see the memory structure in Figure 3. The “this.s” array has 90466 members. Each array member is a type of Vector<uint> and each Vector size is 1022. The memory layout of this Vector<uint> data is simply represented by the following:

 

       Vector size (4 bytes) + extra header (4bytes)  + uint (4 bytes) array of 1022

 

So, basically by using an ActionScript data type of Vector<uint> and its assignment operations, you can interact directly with the actual memory content.

 

fig03.png

Figure 3 Heap spray structure

 

When I debugged the Internet Explorer process with this heap spray code running, I observed huge memory blocks allocated after the heap spray operation. (Figure 4) I ran the “!address” Windbg command before and after the heap spray operation and ran windiff.exe to diff the output from the command. The areas with a yellow background are all heap sprayed areas. As the heap spray elements are aligned by 0x1000 and the whole heap spray area is relatively big, it is usually assumed that the target memory block that the exploit uses (0x18090000~0x18290000) is almost always allocated. The other parts of the exploit assume that this area is allocated and use fixed addresses. As each array member has exactly the same contents and is aligned by 0x1000 bytes, you can assume that the exploit code later always sees the same data from this specific memory area if you access 0x1000 bytes aligned addresses.

 

fig04.png

Figure 4 Heap spray area diff

 

 

Use-after-free

The vulnerability is a use-after-free bug. The CMarkup object is passed to CMarkup::OnCssChange method. (Figure 5)

 

 fig05.png

Figure 5 Call stack of CMarkup method call

 

The CMarkup object passed to this method looks like Figure 6. The first DWORD from a normal C++ object usually points to vftable and the figure shows the valid one.

 

fig06.png

Figure 6 CMarkup object

 

This object is later freed when the destructor is called. (Figure 7)  This is related to how the object is referenced from other objects and how it is managed when the object is not used anymore. Note that the stack trace is actually overly long and we removed the parts that were nonessential.

 

 fig07.png

Figure 7 Part of call stack when free of the CMarkup object happens

 

Just after the free operation is called upon the object, the memory is re-allocated as a string buffer when a Javascript string operation is called. (Figure 8) From the call stack, the original CMarkup::OnCssChange call has not yet returned. The exploit code carefully allocates multiple 0x190 bytes (which happens to be the size of the affected CMarkup object) of memory for string assignments.

 

fig08.png

Figure 8 Part of call stack when memory overwrite occurs on freed object

 

After the string assignments, the freed object memory area is filled with data under the attacker’s control. (Figure 9)

 

fig09.png

Figure 9 Overwritten data of freed CMarkup object location

 

 

2 bytes memory overwrite

So now the attacker has made a fake CMarkup object. The method the attacker chooses next is interesting. From upon the member of the fake object, a house-keeping method (ClearRunCaches) is called later from the CMarkup::OnCssChange method. (Figure 10) This method accepts data of CElement pointer type as its 2nd argument (eax at 0x639DD46A in Figure 10).

 

 

fig10.png

Figure 10 eax is passed to ClearRunCaches method

 

 

From Figure 11, the eax (CElement *) actually comes from an edi pointer, which pointing to “this” object which is a type of CMarkup and points to the address already freed in this case. So as shown above, the attacker has full control of the memory location pointed to by edi. Figure 11 shows you how eax is calculated from this fake CMarkup object. Based on this code, the value of eax passed to ClearRunCaches is 0x18182124.

 

fig11.png

Figure 11 Calculating location of ClearRunCaches argument

 

The contents of this area are shown in Figure 12. This address is inside the heap spray area I mentioned previously. So the attacker also has full control of the data over there already.

 

fig12.png

Figure 12 CElement Object passed to ClearRunCaches

 

The fake CElement object is passed along multiple levels of call stack to reach the VoidCachedNodeInfo method. (Figure 13) The interesting instructions are located inside this method.

 

fig13.png

Figure 13 Call stack of VoidCachedNodeInfo

 

Figure 14 shows the part where the interesting instructions are located. It just takes ecx and performs OR operation with 0xFFFFFFFF on it, which always makes the ecx value 0xFFFFFFFF. The cx, which is now 0xFFFF is overwritten to esi+0x42 address.

 

fig14.png

Figure 14 2 byte overwrite instructions

 

The register esi actually points to the CElement object – 0xe8 location, which is 0x18182fbe. So the overwritten memory location is 0x18183000 (0x18182fbe+0x42). (Figure 15)

 

fig15.png

Figure 15 2byte overwriting instruction

 

Address 0x18183000 is special as the area is inside heap sprayed memory and the location is 0x1000 bytes aligned. All the Vector<uint> objects are 0x1000 aligned here. So any address with 0x1000 alignment will point to the Vector<uint> header location where meta-information on the data structure is saved. The first 4 bytes of the Vector structure happen to contain Vector length information. Figure 16 shows the modification of the Vector length from 0x3fe to 0xffff. This enables the ActionScript code to access areas outside of the designated memory region.

 

 fig16.png

Figure 16 Vector length corruption

 

Additional vector length manipulation

By modifying the original Vector length from 0x3fe to 0xffff, it can access about 0xffff*4 bytes of memory. But, this is not enough to be utilized to perform code execution, especially when ASLR and DEP is in play. The exploit tries to modify an additional Vector object to make the length bigger. In this case, it carefully calculated the header location of an adjacent Vector object and modified the length value from 0x3fe to 0x3ffffff0. (Figure 17)  One uint type is 4 bytes long and the length is 0x3ffffff0, so it has read/write access to total of 0xffffffC0 bytes of memory. Now it almost has the full power to do whatever it wants to do on the process memory.

 

 

 fig17.png

Figure 17 Additional Vector object length modification

 

ROP

To defeat DEP, it uses a ROP technique. First, it sprays the heap with flash.media.Sound object pointers. From the ActionScript it looks for this object pointer pattern from the heap location. (Figure 18)

 

fig18.png

Figure 18 Searching flash.media.Sound object

 

After the exploit finds the location of the flash.media.Sound pointer, it overwrites the location with the pointer to a fake flash.media.Sound object (0x18184100). (Figure 19)

 

fig19.png

Figure 19 Overwritten flash.media.Sound object pointer

 

Figure 20 shows the fake object filled with ROP code and shellcode. When the toString method is called, code pointed to by object+0x70 location is called. (0x76fcb050 at 0x18184170)

 

fig20.png

Figure 20 ROP code

 

The code pointed to by the toString method is at 0x76fcb050, which is inside the ntdll.dll file. (Figure 21) The code is relatively simple and just exchanges eax with the esp register value and returns. Register eax points to the start of the flash.media.Sound object when this code is called (which is 0x18184100 in this case). When the ret instruction is called it retrieves a return value from the esp register location (0x18184100) and jumps there. It jumps to the ntdll!ZwProtectVirtualMemory function location in this case.

 

fig21.png

Figure 21 Fake toString function

 

Figure 22 shows the fake stack when this ZwProtectVirtualMemory function is called. The first value is the next return address and after that, arguments for the ZwProtectVirtualMemory function follow. Basically it gives PAGE_EXECUTE_READWRITE(0x40) protection level to the memory area from 0x181840ec with a size of 0x300 bytes.

 

 fig22.png

Figure 22 ZwProtectVirtualMemory arguments

 

The next step returns to the 0x1818411c location which is located after the ROP code. (Figure 23) The shellcode patches around the memory location it already corrupted and jumps to the next part of the shellcode.

 

fig23.png

Figure 23 Bootstrap shellcode

 

The next shellcode constructs a CONTEXT structure and runs the next shellcode by calling the SetThreadContext function. (Figure 23)

 

fig24.png

Figure 24 Next  shellcode with SetThreadContext

 

The main shellcode executed at this time has a more complicated structure and multiple subroutines. (Figure 25)

 

fig25.png

Figure 25 Main shellcode

 

Conclusion

The exploit found in the wild for CVE-2014-1776 looks very interesting. It exploits a use-after-free bug with very good control of code execution. ASLR and DEP can be easily defeated with a minor amount of heap spray (only around 16MB) and additional techniques. I tested multiple times, but the heap sprayed area always includes the target memory address region (address around 0x18180000). To defeat DEP, it used ROP and changed memory permissions with ZwProtectVirtualMemory. To construct the shellcode with proper API addresses, it used its ability to read full process memory. It parses the PE structure and finds main modules and function addresses from ActionScript code. Even though the full exploit package becomes convoluted in this way, the SWF component is highly portable and it can be easily used by other exploits. Not surprisingly, this exploitation method using Flash component has been used for a while as mentioned from Fireeye blog post.

 

Thanks to the following individuals for providing various samples for this research.

  • Bryan Burns & Brian Keefer from Proofpoint, Inc
  • Marion Marschalek from Cyphort
  • Matt Brooks

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author
Twitter: @ohjeongwook .
Featured


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation.