About Us | Contact Us    



VUPEN Research

  VUPEN Research Team
  VUPEN Research Blog
  VUPEN Research Videos

VUPEN Vulnerability Research Team (VRT) Blog

Advanced Exploitation of Internet Explorer Heap Overflow Vulnerabilities (MS12-004)
Published on 2012-01-17 17:45:24 UTC by Nicolas Joly, Security Researcher @ VUPEN

Twitter LinkedIn Delicious Digg   

Hi everyone,

2012 has just begun with its bunch of very interesting CVEs. One of them is CVE-2012-0003, a critical vulnerability affecting Windows Multimedia Library and related to MIDI file handling. It was patched last week by Microsoft as part of the MS12-004 security bulletin.

Due to the criticality of this vulnerability, we highly recommend applying the patch as soon as possible!

From Wikipedia we can read: "MIDI is an industry-standard protocol, first defined in 1982". So basically, 30 years after its creation, this format is still causing troubles to software vendors such as Microsoft.

By itself, the vulnerability is quite common but exploitation is not trivial. When an application such as Windows Media Player or Internet Explorer parses a MIDI file, a static heap buffer is allocated but up to 0x440 bytes can be written to. Nevertheless, due to the large allocation size, common exploitation techniques for Internet Explorer might not work with this vulnerability which makes exploitation really challenging.

In this blog we will demonstrates the criticality of this vulnerability by showing how it can be reliably exploited via Internet Explorer 9/8/7/6 to achieve code execution by bypassing ASLR/DEP.

1. Technical Analysis of the Vulnerability

Here are the key points of this vulnerability. A MIDI file mainly contains two types of chunks, one named MThd and the other MTrk. Here follow the structures of both chunks:
Offset Field Size

Type = 'MThd'


Length = 6







Offset Field Size

Type = 'MTrk'








Before processing the file, Windows Media allocates two buffers in "mseOpen()" in winmm.dll:

.text:76B5CDB1 mov edi, [ebp+arg_4]
.text:76B5CDB4 mov eax, [edi+10h]
.text:76B5CDB7 lea eax, ds:94h[eax*8]
.text:76B5CDBE cmp eax, 10000h
.text:76B5CDC3 mov [ebp+var_4], 7
.text:76B5CDCA jnb loc_76B5CED7
.text:76B5CDD0 push ebx
.text:76B5CDD1 push esi
.text:76B5CDD2 push eax
.text:76B5CDD3 call winmmAlloc(x)              // allocate a buffer
.text:76B5CDD8 mov esi, eax
.text:76B5CDDA xor ebx, ebx
.text:76B5CDDC cmp esi, ebx
.text:76B5CDDE jz loc_76B5CED5
.text:76B5CDE4 push 400h
.text:76B5CDE9 call winmmAlloc(x)             // allocate a second buffer of size 0x400


The second buffer will be noted b1 in the following. This specific vulnerability lies in the way certain events from the MTrk chunk are parsed. These events are first read in "smfReadEvents()", defined in quartz.dll:

.text:74903483 loc_74903483:
.text:74903483 push [ebp+arg_C]
.text:74903486 lea eax, [ebp+var_14]
.text:74903489 push eax
.text:7490348A push esi
.text:7490348B call smfGetNextEvent(x,x,x) // read an event to var_8
.text:74903490 test eax, eax
.text:74903492 jnz loc_749035B1
.text:74903498 mov ecx, [ebp+var_8]
.text:7490349B cmp cl, 0F0h
.text:7490349E jnb short loc_749034EC


An event is identified by its first byte and noted e1 e2 e3 in the following, so that ECX = 0x00e3e2e1. Only events where e1 < 0xF0 are of interest. In the next piece of code, the event is written at offset 8 in an array previously allocated:

.text:749034B4 loc_749034B4:
.text:749034B4 mov eax, [esi+10h]
.text:749034B7 add eax, [ebp+var_14]
.text:749034BA movzx ecx, cl
.text:749034BD mov [edi], eax                    // write a first dword
.text:749034BF and dword ptr [esi+10h], 0
.text:749034C3 add edi, 4
.text:749034C6 and dword ptr [edi], 0         // write 0
.text:749034C9 movzx eax, byte ptr [ebp+var_8+2]
.text:749034CD movzx edx, byte ptr [ebp+var_8+1]
.text:749034D1 shl eax, 8
.text:749034D4 or eax, edx
.text:749034D6 shl eax, 8
.text:749034D9 add edi, 4
.text:749034DC or eax, ecx
.text:749034DE loc_749034DE:
.text:749034DE mov [edi], eax                    // write the event
.text:749034E0 add edi, 4
.text:749034E3 add dword ptr [ebx+8], 0Ch // increment the entry counter
.text:749034E7 jmp loc_749035A2


This array is next handled by "midiOutPlayNextPolyEvent()", in winmm.dll:

.text:76B5D0B2 mov eax, [ebp+wParam]
.text:76B5D0B5 mov ecx, [ebx+eax]          // read an event to ecx
.text:76B5D0B8 add ebx, 4
.text:76B5D0BB mov eax, ecx
.text:76B5D0BD mov [esi+24h], ebx
.text:76B5D0C0 shr eax, 18h
.text:76B5D0C3 and ecx, 0FFFFFFh             // ecx = e3 e2 e1


At this moment, the application distinguishes whether or not  e1 > 7Fh:

.text:76B5D1B6 loc_76B5D1B6:
.text:76B5D1B6 cmp [ebp+hmo], ebx
.text:76B5D1B9 mov esi, [edi+84h]
.text:76B5D1BF jz loc_76B5D276
.text:76B5D1C5 test cl, cl                         // cl = e1
.text:76B5D1C7 mov al, cl                        // al = e1
.text:76B5D1C9 mov ebx, ecx
.text:76B5D1CB js short loc_76B5D1E3     // jump if 80h <= e1 <= FFh
.text:76B5D1E3 loc_76B5D1E3:
.text:76B5D1E3 mov edx, ecx
.text:76B5D1E5 shr edx, 8                       // dl = e2
.text:76B5D1E8 mov [edi+54h], cl
.text:76B5D1EB mov byte ptr [ebp+wParam+3], dl
.text:76B5D1EE shr ebx, 10h                   // bl = e3


As we can see in the following lines, Windows Media specifically processes events where e1 & F0h = 80h or 90h:

.text:76B5D1F1 loc_76B5D1F1:
.text:76B5D1F1 mov dl, al                      // dl = e1
.text:76B5D1F3 and dl, 0F0h
.text:76B5D1F6 cmp dl, 90h
.text:76B5D1F9 mov [ebp+var_1], dl
.text:76B5D1FC jz short loc_76B5D203
.text:76B5D1FE cmp dl, 80h
.text:76B5D201 jnz short loc_76B5D25F


For such cases, an offset is computed according to e1 and e2 to write data to the buffer b1 allocated above:

.text:76B5D203 loc_76B5D203:
.text:76B5D203 movzx edx, byte ptr [ebp+wParam+3] // edx = e2
.text:76B5D207 and eax, 0Fh                                      // eax = e1 & 0Fh
.text:76B5D20A shl eax, 7
.text:76B5D20D add eax, edx
.text:76B5D20F cdq
.text:76B5D210 sub eax, edx
.text:76B5D212 sar eax, 1
.text:76B5D214 cmp [ebp+var_1], 80h
.text:76B5D218 jz short loc_76B5D244


At the end, EAX = ((e1 & 0Fh) * 2^7 + e2) / 2.

This is where the data in b1 is going to be altered. Note that for particular values of e1 and e2, it is possible to get EAX > 400h. For example, e1 = 9Fh => 0Fh * 2^7 = 780h. Then if e2 > 7Fh, e1 + e2 > 800h which makes EAX >= 400h and the program writes past the bounds of the allocated buffer.

Depending on whether e1 & F0h = 90h or 80h, or even e3 = 1, it is possible to increment or decrement an arbitrary byte. For example with e1 & F0h = 90h:

.text:76B5D21E add esi, eax
.text:76B5D220 test byte ptr [ebp+wParam+3], 1
.text:76B5D224 mov al, [esi]                                   // read a byte in b1
.text:76B5D23E inc al
.text:76B5D240 mov [esi], al                                   // increment that byte


If e1 & F0h = 80h:

.text:76B5D248 lea edx, [eax+esi]
.text:76B5D24B mov al, [edx]                                 // read a byte in b1
.text:76B5D25B dec al
.text:76B5D25D mov [edx], al                                // decrement that byte


Since b1 is 0x400 bytes long, a heap overflow occurs when e1 = 8Fh or 9Fh and when e2 > 7Fh. In practice, it becomes possible to corrupt the 0x40 bytes following b1, which is enough to achieve arbitrary code execution.

Advanced Exploitation With ASLR/DEP Bypass

Since the vulnerable module can be loaded in Internet Explorer, it is possible to achieve a reliable exploitation of this flaw.

As we can see in "DllProcessAttach()" in winmm.dll and "_DllMainStartup()" in mshtml.dll, both libraries use the same heap for their allocations:

In "DllProcessAttach()":

.text:76B43F8F mov eax, large fs:18h                   // GetProcessHeap inlined
.text:76B43F95 mov eax, [eax+30h]
.text:76B43F98 mov eax, [eax+18h]
.text:76B43F9B mov _hHeap, eax


In "_DllMainStartup()":

.text:3CEAC930 call GetProcessHeap()
.text:3CEAC936 push eax
.text:3CEAC937 mov _g_hProcessHeap, eax


As a result, it is possible to exploit this vulnerability to corrupt an Internet Explorer object.

Usually, exploitating such vulnerabilities consists in finding an object whose size is equal to the vulnerable buffer's size. In that case, exploitation can be achieved in the following way: allocate contiguously the vulnerable buffer, a string and an object. Once done overwrite the string's length to disclose the vTable and deduce mshtml.dll's base address:

Allocate then a new buffer and trigger the vulnerability a second time to overwrite the object's vTable:

At that point, spray the heap with a dynamic ROP for mshtml.dll and call a function from the corrupted object to redirect the execution flow.

(Un)fortunately, this method requires the vulnerability to trigger twice and only works as long as it is possible to allocate an object of a given size. For sizes < 0x100 bytes, IE provides enough objects for that method to work but in the particular case of the MS12-004 vulnerability, the buffer is 0x400 bytes long, and doing an XREF on 3FCh or 400h in mshtml.dll does not provide any interesting object allocation.

A reliable exploit for this vulnerability can however be created by taking advantage of a particular CImplAry object. The idea consists in creating an array of size 0x400, and precisely altering its content to disclose an arbitrary vTable and redirect the execution flow.

Such an array can be created for instance when an element is cloned. As we can see, "CElement::Clone()" leads to call "CElement::CloneAttributes()" and eventually "CAttrArray::Clone()" to clone the attributes. The following lines belong to "CAttrArray::Clone()":

.text:3D06A356 call CAttrArray::operator new(uint) // allocate a new CAttrArray object
.text:3D06A35B cmp eax, edi
.text:3D06A35D jz short loc_3D06A368
.text:3D06A35F mov ecx, eax
.text:3D06A361 call CAttrArray::CAttrArray(void)   // initialize the object
.text:3D06A366 mov edi, eax
.text:3D06A368 loc_3D06A368:
.text:3D06A368 test edi, edi
.text:3D06A36A mov esi, [ebp+arg_4]
.text:3D06A36D mov [esi], edi
.text:3D06A36F jz loc_3D0F5DE5


At that point, the application checks whether the original element contains an attribute:

.text:3D06A375 mov eax, [ebx+4]
.text:3D06A378 shr eax, 2                                   // eax represents the number of attributes
                                                                         // associated with the original element

.text:3D06A37B js loc_3D053663
.text:3D06A381 cmp eax, [edi+8]
.text:3D06A384 jbe loc_3D0F5DF1
.text:3D06A38A push 10h
.text:3D06A38C call CImplAry::EnsureSizeWorker(uint,long)


If this number is not NULL, IE allocates 10h * #attributes in "CImplAry::EnsureSizeWorker()". As a result, if the original element has 0x40 attributes defined, IE allocates exactly 0x400 bytes for the new array. These attributes are next copied by "CAttrValue::Copy()":

.text:3D06A3D9 mov byte ptr [esi+1], 0
.text:3D06A3DD call CAttrValue::Copy


Consider now the following script, which creates a new Select element, associates various attributes and clones the node:

var test = document.createElement("select")
.obj1 = this
test.obj8 = alert
test.obj12 = new Date()

var cl0ne = test.cloneNode(true)

Once cloned, the CImplAry looks like this:

Actually an attribute is identified by three fields and uses 0x10 bytes in memory. Pay attention to the bytes in red on Figure 3. They represent the variant types as defined in MSDN: 0x08 indicates a string, 0x09 an object, 0x03 an integer etc.

The following figure shows obj0, obj8 and obj12 as represented in memory:

Since the application specifically relies on the variant type to determine the type of the attribute, it becomes possible with a heap overflow to corrupt the array and force the browser to confuse types. Figure 5 shows the CImplAry after incrementing and decrementing two bytes:

On Figure 5, obj0 is now an object while obj1 is a string. It is then possible to bypass ASLR/DEP by leaking the object's vTable using JavaScript. This gives the following result:

Then the corrupted string is used to trigger the vulnerability in "CAttrValue::GetIntoVariant()" and reach a CALL instruction leading to arbitrary code execution despite ASLR/DEP:

Two bytes need to be altered for that method to work. If you manage to do it with only one, please let us know! Note also that this method works reliably with all Internet Explorer versions including 9/8/7 and even IE6, since heapAlloc is used for sizes > 0x230.

As demonstrated in this blog, this vulnerability is really critical thus we highly recommend applying the patch as soon as possible!

Copyright VUPEN Security


VUPEN Solutions  











2004-2014 VUPEN Security - Copyright - Privacy Policy