cve-2014-1776 분석보고서
시스템 2014. 7. 8. 15:37 |http://h30499.www3.hp.com/t5/HP-Security-Research-Blog/The-mechanism-behind-Internet-Explorer-CVE-2014-1776-exploits/ba-p/6476220#.U7t6K_l_vKg
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.
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)
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.
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.
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)
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.
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.
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.
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)
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::OnCssChangemethod. (Figure 10) This method accepts data of CElement pointer type as its 2nd argument (eax at 0x639DD46A in Figure 10).
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 ofCMarkup 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.
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.
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.
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 toesi+0x42 address.
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)
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.
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.
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)
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 fakeflash.media.Sound object (0x18184100). (Figure 19)
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)
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 theflash.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.
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.
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.
Figure 23 Bootstrap shellcode
The next shellcode constructs a CONTEXT structure and runs the next shellcode by calling the SetThreadContext function. (Figure 23)
Figure 24 Next shellcode with SetThreadContext
The main shellcode executed at this time has a more complicated structure and multiple subroutines. (Figure 25)
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