IE 11瀏覽器0day漏洞(CVE-2015-2425)UAF分析
前言
CVE-2015-2425
是Hacking team
洩露出來的一個IE11
的0day
漏洞,影響了IE11及之前的版本。在一封Hacking Team
高層收到的來自Vectra Networks
安全公司的信件中被發現。Vectra Networks
公司的研究者在信中向Hacking Team
提供了對於Windows 7/8.1最新版的IE11的poc
程式碼。但Hacking Team
並沒有購買,所以只洩露了poc
,並沒有攻擊程式碼。
環境
測試環境是win8.1
32位,IE版本是IE11。
poc
poc.html
:
把IE11附加到windbg
上,然後執行poc.html
,IE11崩潰到一個無法讀取的地址:
windbg+IDA分析
由於崩潰在一個不可訪問的地址,不好確定之前的指令,這時需要用到與棧回溯相關的命令,就是windbg
中的k
一系列命令:
每一行描述當前的一個棧幀,最上面的一行描述的是當前指令的返回地址:
由此可知崩潰的返回地址是63dfcf3c
,在JavascriptFunction::CallFunction<1>
中,看當前的esp
也可以得出同樣的結論:
為了看到函式是怎麼呼叫的,需要用到.frame
命令,使用.frame /c 1
回到崩潰棧的第一層,也就是上層函式呼叫時的狀態:
看到回到了上層函式中,eip
的值為崩潰處的返回地址,在反彙編視窗可以看到上層函式,也可以用u
產生崩潰的地方是63dfcf39
的call
函式,呼叫的是[ebp-1Ch]
處的函式指標,在IDA
中看一下CallFunction<1>
的定義:
這個函式是__fastcall
方式呼叫的,__fastcall
是一種快速呼叫方式,規定將前兩個引數由暫存器ecx
和edx
來傳遞,其餘引數還是通過堆疊傳遞(從右到左),不同編譯器編譯的程式規定的暫存器不同。在Intel 386
平臺上,使用ECX
和EDX
暫存器。
往前找更改ebp-1Ch
內容的指令,只有63dfcec6
處的mov
指令:
edx
是函式的第二個引數,也就是一個函式指標,所以63dfcf39
處的指令是呼叫了函式指標所指向的函式,這樣的話,還需要向上層看,到底是什麼樣的函式指標,回到上一層函式:
看到edx
來源是[eax+0Ch]
,而eax
的來源是[ebx+4]
,ebx
來源於下面一句:
看一下這個函式的定義:
__thiscall
為了解決類成員呼叫中this指標傳遞而規定的,__thiscall
要求把this
指標放在特定暫存器中,該暫存器由編譯器決定。VC
使用ecx
。所以這裡ecx
裡的指標就是this
指標。梳理一下這裡的過程:
推測這裡呼叫了某個物件的成員函式,而這個物件是JavascriptFunction
物件。
弄清楚後我們回到崩潰點所在的函式內,選擇在call [ebp-1Ch]
處下斷點,斷點會多次觸發,觸發5次後,停下,步入函式看:
在63e0631b
處的call
後,eax
就會變成一段已經釋放記憶體的地址,看到這句的jmp
指令跳到了eax
指向的值,把這段函式彙編看一下:
eax
裡存放的應該是NativeCodeGenerator::CheckCodeGen
的返回值,這次在這個函式上下斷點再次除錯,會觸發六次斷點,在返回前呼叫了NativeCodeGenerator::CheckCodeGenDone
將eax
置為了不可訪問的地址:
看看NativeCodeGenerator::CheckCodeGenDone
,這次還在呼叫NativeCodeGenerator::CheckCodeGen
上下斷點,六次斷點後進入CheckCodeGenDone
,發現裡面還有一個call
改變了eax
的值,然後還有mov eax,edi
:
再次用相同的方式除錯,這次進入ScriptFunction::UpdateThunkEntryPoint
看看,這個函式如下:
63e06604
的mov
語句改變了eax
:
這個函式返回後有一個mov eax,esi
的指令,但這裡edi
值也已經是返回後eax
的值了,看來還要追蹤edi
,看一下CheckCodeGenDone
函式:
edi
是作為引數傳入UpdateThunkEntryPoint
的,並且在函式內沒有被改變,那麼還要往回追蹤,經過回溯,從CheckCodeGenDone
函式開頭開始跟,由於中間還有跳轉,所以edi
的值還會改變,跟到edi
變成崩潰時的返回地址時可以得出:
那麼還需要跟蹤一下esi
,可以看到來自eax
,而eax
來自ecx
:
所以ecx
中是CheckCodeGen
的第一個引數:
是ScriptFunction
物件,說明在最後一次呼叫CheckCodeGen
前這個物件就已經被釋放了,用poi
看看指標引用的過程,通過三次引用找到了那塊已經釋放的記憶體:
重啟後在第五次斷在63e0631b
時,ebx
指向了ScriptFunction
物件,這時看一下觸發漏洞的指標,然後在第六次時看一下指標:
兩次指標並不相同,說明指標被改寫了一次,重新執行一樣在第五次斷點被觸發時看一下這裡的記憶體,此時還沒有被改寫:
在這裡可以下一個記憶體寫入斷點,看看什麼時候被改寫了:
真正的寫入發生在上一句mov
語句,這時的esi
為05a49120
,eax
為05a50000
:
而eax
裡的是上一個call
的返回值,也就是InterpreterThunkEmitter::GetNextThunk
的返回值,這次在63e06cd7
下斷點,會觸發五次,最後一次時步入函式:
由於這個函式是__thiscall
,所以ecx
就是this
指標,63e06df5
就是把類的一個成員賦給了eax
。
為IE開啟堆頁,命令是:
再次下斷點除錯,看看這裡堆的情況:
這時eax
指向的內容還沒有被釋放,是一段函式程式碼:
那我們要找到這段記憶體是如何釋放的,還是回到63e06cd7
斷點處,這次不進入函式,步過後那段記憶體並沒有被釋放,為了弄清楚在哪裡被釋放,給this
指標和那段uaf
的記憶體下訪問斷點:
斷點觸發時,09f90000
還沒有被釋放,函式返回時已經被釋放了,記憶體就是用FreeAllocations
來釋放,在EmitBufferManager
類中還有NewAllocation
函式,應該是分配記憶體的函式。
總結
所以漏洞的成因應該是在記憶體被FreeAllocations
釋放後又在JavascriptFunction::CallFunction<1>
中使用而造成的UAF
。由於這個漏洞的返回地址不可控,所以要用堆噴的方法的話可能還需要結合其他方法來繞過DEP
和ASLR。