新聞中心
本文假設(shè)你對(duì)漏洞挖掘,漏洞分析,匯編這些不太了解,但是對(duì)C,C++有一定了解。首先我們找一個(gè)具體的公開(kāi)的例子——CVE-2011-1260,釋放后重用漏洞。首先我們上網(wǎng)找一下這個(gè)漏洞的POC,得到如下(運(yùn)行環(huán)境XP SP3,IE8,開(kāi)啟DEP(稍后解釋什么是DEP)):

0×1 漏洞分析
我們雙擊打開(kāi),發(fā)現(xiàn)IE崩潰了,到底是什么原因呢?
我們用windbg附加進(jìn)程(調(diào)試IE最好還是用微軟的調(diào)試器,畢竟微軟主場(chǎng)):附加后,按g,回車,如果限制了activeX控件運(yùn)行,就按允許,然后windbg在崩潰的時(shí)候停止了:
從eax+70的地址取出數(shù)據(jù),顯然這個(gè)地址不合法,所以導(dǎo)致錯(cuò)誤。我們按knL回車,從棧查看函數(shù)的調(diào)用情況:
不清楚棧的看這里,清楚的跳過(guò):函數(shù)調(diào)用有很多種,這里指stdcall,在每一次調(diào)用之前,都會(huì)將參數(shù)壓棧,從右到左,比如Void A(int a,int b),匯編就類似push,push,call,Call指令會(huì)將call指令的下一條指令的地址壓棧,所以最后??雌饋?lái)是這樣的:(注意,壓棧是從高往低壓)
對(duì)應(yīng)源代碼:
所以我們從棧中可以看出崩毀之前調(diào)用過(guò)什么函數(shù),以及現(xiàn)在在什么函數(shù)里面,函數(shù)返回地址等等很多信息。
這里我們可以看到是在CElement::Doc函數(shù)里面崩毀,看看附近的匯編指令:
取ecx地址的內(nèi)容返回給eax,但是eax是0,導(dǎo)致后來(lái)的內(nèi)存讀取錯(cuò)誤,顯然ecx出了問(wèn)題。一般情況下,在thiscall中,第一個(gè)參數(shù)是this指針(用ecx傳遞),而對(duì)象+0x0h的地方儲(chǔ)存的是虛函數(shù)表的地址,所以可得這里是取虛函數(shù)表偏移0×70的地方的虛函數(shù)指針,再調(diào)用這個(gè)虛函數(shù)。
顯然這里的ecx就是CElement對(duì)象,這個(gè)對(duì)象是IE里面很多元素的父類,如(CObjectElement,對(duì)應(yīng)
取ebx地址的內(nèi)容給ecx,那么ebx哪里來(lái)?我們用ida反匯編這個(gè)函數(shù)看看(在mshtml.dll)
Ebx就是this指針,即ebx指向的地方就是CTreeNode,然后CTreeNode對(duì)象+0x0h的地方傳給ecx(即偏移0x0h保存了對(duì)應(yīng)的CElement指針),再傳入CElement::Doc。那么CTreeNode和CElement有什么關(guān)系呢?
我們重新運(yùn)行,然后定如下兩個(gè)斷點(diǎn):
- bu mshtml!CObjectElement::CreateElement+0x18 ".printf \"[%08x]\\n\",eax;g"
在mshtml!CObjectElement::CreateElement+0×18出斷下,并且打印eax的值,然后繼續(xù)運(yùn)行。從文字可以看出這個(gè)是構(gòu)造CObjectElement的函數(shù),里面會(huì)調(diào)用CObjectElement的構(gòu)造函數(shù),eax一般是函數(shù)返回的值,即這個(gè)對(duì)象的指針。
函數(shù)中分配了一個(gè)0xDC的堆,顯然這是這個(gè)對(duì)象的大小。
- bu mshtml!CTreeNode::CTreeNode+0x8c ".printf \"allocated CTreeNode at %08x, ref to CElement %08x of tbale %08x\\n\", eax, poi(eax), poi(poi(eax));dds poi(eax) L 1;g"
這個(gè)斷點(diǎn)是在CTreeNode+0x8c地方斷下,打印對(duì)象指針,對(duì)應(yīng)的CElement指針(CTreeNode+0×0處保存對(duì)應(yīng)CElement對(duì)象),以及對(duì)應(yīng)的CElement對(duì)象的虛函數(shù)表地址。
定好斷點(diǎn),GO!
從圖可以看出,最后創(chuàng)建了一個(gè)CObjectElement對(duì)象在00201c30,然后立馬又創(chuàng)建了與之對(duì)應(yīng)的CTreeNode對(duì)象0338aa18,到了程序崩毀的地方,ebx(即CTreeNode對(duì)象)還是0338aa18,但是與之對(duì)應(yīng)的CObjectElement對(duì)象(ecx)卻改變了。
我們看00201c30的地方:
這里已經(jīng)不是CObjectElement的虛函數(shù)表,說(shuō)明這個(gè)對(duì)象已經(jīng)被釋放,所以與之對(duì)應(yīng)的CTreeNode也在崩潰之前釋放了,所以這里CTreeNode+0×0的CObjectElement指針也是錯(cuò)誤的,所以指向的地方是00000000,然后讀取虛函數(shù)表出錯(cuò)。我們定下斷點(diǎn):
- bu mshtml!CTreeNode::Release+0x19 ".printf \"freeing CTreeNode at %08x, CElement at %08x, of table %08x\\n\", edx, poi(edx), poi(poi(edx)); g"
這里可以看出,在IE崩潰前,CTreeNode已經(jīng)釋放了,然后崩潰時(shí)又引用了這個(gè)對(duì)象對(duì)應(yīng)的CElement對(duì)象,然后call虛函數(shù),導(dǎo)致程序出錯(cuò)。為什么會(huì)釋放呢?因?yàn)?object>標(biāo)簽沒(méi)指明clsid值,所以IE會(huì)釋放這個(gè)標(biāo)簽,那么接下來(lái)我們可以干什么呢?#p#
0×2 漏洞利用
我們既然這個(gè)CTreeNode已經(jīng)釋放了,那么+0×0對(duì)應(yīng)的CElement對(duì)象指針?biāo)赶虻牡胤轿覀冇袩o(wú)辦法控制呢?我們發(fā)現(xiàn)最后崩潰時(shí)候ecx指向的地方很接近之前CObjectElement分配到的地方,而且這個(gè)地方已經(jīng)釋放了,我們知道,當(dāng)一個(gè)對(duì)象的內(nèi)存空間釋放后,如果我們大量申請(qǐng)內(nèi)存,我們遲早會(huì)用到這個(gè)之前釋放的內(nèi)存空間,因?yàn)楸緛?lái)內(nèi)存就那么多,要循環(huán)利用嘛。如果我們大量申請(qǐng)和CObjectElement相同大小的堆塊,我們會(huì)不會(huì)把ecx指向的地方覆蓋呢?我們?cè)囋?
果然,ecx指向的地方已經(jīng)被0x0c0c0c0c覆蓋,根據(jù)匯編代碼,之后會(huì)call [0x0c0c0c0c+0x70]
然后我們能把0x0c0c0c0c+0×70寫上某個(gè)地址,然后eip就會(huì)跳到這個(gè)地址執(zhí)行我們指定的代碼,進(jìn)而pwn IE 8。
那我們有什么辦法在0x0c0c0c0c這里寫上我們要寫的數(shù)據(jù)呢?也是老方法,通過(guò)大量申請(qǐng)堆塊,我們遲早會(huì)把0x0c0c0c0c這里覆蓋成我們的內(nèi)容。
這是如果沒(méi)有DEP的話,我們直接用大量的nops+shellcode覆蓋內(nèi)存,然后精確計(jì)算在0x0c0c0c0c+0×70的地方填上我們shellcode的地址,我們就能跳去shellcode運(yùn)行啦。但是這里有DEP。DEP就是如果這塊內(nèi)存沒(méi)有執(zhí)行權(quán)限的話,即使EIP跳到這里,它也不能執(zhí)行代碼。而我們通過(guò)大量申請(qǐng)堆塊而放置shellcode的地方,系統(tǒng)是不允許在這里運(yùn)行指令的。這時(shí)候,我們可以用ROP繞過(guò)DEP。ROP就是在堆棧中壓入若干個(gè)小程序的地址,不斷控制EIP運(yùn)行到這些小程序里,達(dá)到某種目的。因?yàn)椴恢苯釉诓豢蛇\(yùn)行的內(nèi)存中運(yùn)行代碼,所以可以繞過(guò)DEP。等下我會(huì)具體舉例子。
要繞過(guò)DEP,我們可以通過(guò)VirtualAlloc+memcpy的方法,前者可以分配一個(gè)內(nèi)存屬性為可讀可寫可執(zhí)行的內(nèi)存區(qū)域,然后用memcpy把我們的shellcode復(fù)制過(guò)去。然后EIP跳到這個(gè)區(qū)域運(yùn)行shellcode。(shellcode就是我們想要達(dá)到某種目的的代碼,比如惡作劇可以是彈出一個(gè)對(duì)話框,把這些代碼變成匯編的機(jī)器碼,然后復(fù)制入內(nèi)存里面,控制EIP跳至這里執(zhí)行。)
首先我們要在0x0c0c0c0c的地方偽造一個(gè)棧,
通過(guò)MSDN查看我們要用的VirtualAlloc與memcpy函數(shù)的使用方法,最終我們決定利用兩行代碼繞過(guò)DEP(XP3下沒(méi)有開(kāi)啟ASLR):
VirtualAlloc(分配的內(nèi)存地址,內(nèi)存大小,內(nèi)存種類,內(nèi)存屬性)
- VirtuallAlloc(0x7f002000,0x00004000,0x00003000,0x00000040)
- Memcpy(0x7f003000,0x0c0c0c80,0x00001000)
然后我們構(gòu)造棧結(jié)構(gòu)如下:
然后我們要想辦法把esp(指向棧頂)指向0x0c0c0c0c,由于我們已經(jīng)控制EIP(0x0c0c0c0c+0×70),所以如果我們?cè)谶@里寫入0x76a712ff,該處的指令為xchg eax,esp//ret。然后我們EIP指向0x76a712ff,交換eax和esp(eax這時(shí)指向0x0c0c0c0c),然后ret(ret指令就是將EIP變成[ESP],然后ESP+4),這時(shí)EIP會(huì)變成0x7C809AE1。前面我們已經(jīng)說(shuō)過(guò),剛進(jìn)入函數(shù)的時(shí)候,[ESP]是函數(shù)返回地址,即到最后ret會(huì)將EIP變成這個(gè)地址,然后下面的是參數(shù),從低到高(從上到下)分別對(duì)應(yīng)C語(yǔ)言中的從左到右,即0x7f002000,0×00004000,0×00003000,0×00000040。然后我們看VirtualAlloc返回的時(shí)候指令
Retn 10的意思是EIP變成[ESP],然后ESP+14,所以運(yùn)行完retn 10后,我們會(huì)跳入0x7c921db3(memcpy),然后[ESP]是函數(shù)返回地址(0x7f001000),然后下面三個(gè)是參數(shù),運(yùn)行完這個(gè)函數(shù)后,我們會(huì)跳入返回地址0x7f001000運(yùn)行shellcode。(函數(shù)調(diào)用的具體過(guò)程大家可以參考《C++反匯編與逆向分析》的第六章)
然后0x0c0c0c34-0x0c0c0c7c我們放入隨意的數(shù)據(jù),然后0x0c0c0c7c放入0x76a712ff,然后0x0c0c0c80我們放入我們的shellcode。
按理來(lái)說(shuō),只要我們把上面的數(shù)據(jù)我們組成一個(gè)塊(block1),然后申請(qǐng)大量?jī)?nèi)存填入這些塊,最終覆蓋0x0c0c0c0c就可以了。但是又有問(wèn)題來(lái)了,堆塊申請(qǐng)的起始地址是會(huì)變的!!!例如,我們堆塊開(kāi)始可能的地方可能是0x0c0c0000也可能是0x0c0c0010,我們知道,如果這個(gè)地址變了,我們最終就無(wú)法令我們這個(gè)塊一開(kāi)始的地方準(zhǔn)確地對(duì)準(zhǔn)0x0c0c0c0c,然后我們0x0c0c0c0c+0×70的地方的數(shù)據(jù)就會(huì)將EIP指向一個(gè)錯(cuò)誤的地方,然后bomb,IE又崩潰。
如果沒(méi)有DEP,我們可以覆蓋大量的NOP(1mb左右)+shellcode(幾百字節(jié)),然后只要0x0c0c0c0c的地方是NOP就可以了(很大幾率hit中nop),但是這里不行,我們要準(zhǔn)確的堆噴射。在這里,我陷入了深深的沉思,我確實(shí)在這里想了很久的辦法,網(wǎng)上找了資料,大部分說(shuō)的都是nop+shellcode,沒(méi)有準(zhǔn)確的堆噴射,即使有,也看得不明白。后來(lái)我突然發(fā)現(xiàn),我們塊的起始地址有個(gè)共同的特點(diǎn):
0c0a8040是堆塊的起始地址,前8字節(jié)是塊首,真正我們用的是0c0a8048開(kāi)始,我們?cè)贘S里面噴射用過(guò)的是string類型,前4字節(jié)是保存string的大小,后2字節(jié)是0000表示字符串解釋,所以我們這里發(fā)現(xiàn),我們字符串起始的位置都是04c結(jié)尾有木有?!! 如果我們創(chuàng)建一個(gè)塊大小是0×1000(block2),然后前面0xc0c-0×048=0xbc4字節(jié)放入nop,然后再放入我們上面的block1,然后后面全部放入nop。然后我們的內(nèi)存全部塞滿這樣的塊,我們是不是就保證了全部0xXXXXXc0c地址都對(duì)準(zhǔn)了我們block1的起始位置?(包括0x0c0c0c0c)(這是我自己想到的方法,不知道大家有無(wú)別的好方法?)
接下來(lái),我們就要寫shellcode,這里我們用kail或者BT5的msfpayload生成一個(gè)反彈命令行至本地1024端口的JS版shellcode,然后加入我們的exp中。最終我們的exp如下(heapLib.js是一個(gè)網(wǎng)上大家用來(lái)堆噴射的庫(kù),某個(gè)牛人寫的):
Nc.exe –l –p 1024就是監(jiān)聽(tīng)1024端口,等待反彈shell。
以上,就是一個(gè)經(jīng)典的利用IE UAF漏洞進(jìn)行遠(yuǎn)程代碼執(zhí)行的完整過(guò)程,有了遠(yuǎn)程代碼執(zhí)行,木馬還會(huì)遠(yuǎn)嗎?由于本人菜鳥(niǎo),難免會(huì)有錯(cuò)誤的地方,希望大家一起能共同探討學(xué)習(xí)。
所以大家知道上XX網(wǎng)的危害了嗎?不要以為沒(méi)下載病毒沒(méi)事哦,惡意JS都能有exe的功能。
本文出自:http://www.freebuf.com/vuls/66865.html
網(wǎng)站名稱:針對(duì)網(wǎng)頁(yè)木馬(CVE-2011-1260)的分析與實(shí)踐
網(wǎng)頁(yè)鏈接:http://m.5511xx.com/article/cdhiode.html


咨詢
建站咨詢
