2016-09-28 106 views
3

我想要使用此功能採用了經典的溢出與strcpy的重新堆棧緩衝區溢出:禁用堆棧保護工作不

#include <stdio.h> 
#include <string.h> 
void main(int argc, char **argv) { 
    char buf[100]; 
    strcpy(buf,argv[1]); 
    printf("Done!\n"); 
} 

我已經嘗試了所有的各種標誌,以消除編譯堆棧保護gcc -o vuln vuln.c -fno-stack-protector -g -z execstack以及使用sudo echo 0 > /proc/sys/kernel/randomize_va_space刪除ASLR。

我可以讓我的nop-shellcode地址覆蓋保存的EIP,但它在RET上崩潰。我想通過HANDLE SIGSEGV ignore等來禁用gdb中的SIGSEGV,但是在GDB外部運行時,無論我設置返回地址的位置如何,我都會繼續收到Segmentation Fault。

除了獲得舊版本的gcc以外的任何想法?目前使用gcc版本6.1.1。

回答

4

Linux遵循W^X原則:它將內存頁面標記爲非可執行文件,除非它們是代碼部分的一部分。這超出了你編譯的應用程序的範圍,並有很好的理由。操作系統承擔這一責任,以防止來自系統上執行的任何程序的緩衝區溢出攻擊;尤其是那些積極嘗試執行緩衝區溢出攻擊的程序,就像你的程序一樣。

您試圖通過buf在堆棧上編寫代碼,並覆蓋該函數的返回地址以將執行跳轉到新注入的代碼中。當函數返回時,程序計數器被設置爲重寫的返回地址,該地址現在指向堆棧內存。當程序計數器由於堆棧內存頁面的撤銷執行權限而嘗試執行下一條指令時,將拋出SIGSEGV

要從堆棧執行,必須禁用OS堆棧保護。

幸運的是,Linux提供了execstack這種情況下,用戶可以自行決定使用。

請參閱this Unix & Linux Stack Exchange發佈更多一般信息。


調試插入的數據

如果您收到廣發行SIGSEGV,它可能意味着在你的緩衝區溢出嘗試一些錯誤。爲了避免在main末清理搞亂,我建議讓你從主打電話做你的緩衝區溢出的函數:

#include <stdio.h> 
#include <string.h> 

char injection_code[] = ""; // buffer overrun data here 


void foo() { 
    char buf[100]; 

    // memcpy used to copy the full injection string, including any nested 0s 
    memcpy(buf, injection_code, 108); // +8 here to handle 64-bit system RAs 
} 

int main (int argc, char** argv) { 
    foo(); 
    printf("Done!\n"); 
    return 0; 
} 

使用GDB調試。我建議在foo的末尾加上一個斷點,然後檢查寄存器與info registers的期望值一致。你可能對指令指針最感興趣,你可以用info registers rip(64位)或info registers eip(32位)來檢查它。

您可以在GDB中使用printx/x來探索堆棧的外觀。例如,您可以檢查$rbp+8以查看堆棧上的返回地址(RA)。

GDB會SIGSEGVret指令,如果RA指向無效的位置:

Program received signal SIGSEGV, Segmentation fault. 
0x00000000004005bc in foo() at bufferoverflow.c:27 

您可以檢查,看它是否故障的ret指令通過在故障時檢查指令指針地址(在上面的例子中,它將是0x00000000004005bc)針對程序的程序集(在GDB中使用disassemble function_name)。

如果返回地址堆棧中的點背,它可能會拋出一個SIGILL的非法指令,這意味着你的寄信人地址可能不正確對齊,或者您的注入指令格式有誤:

Program received signal SIGILL, Illegal instruction. 
0x00007fffffffdc13 in ??() 

再次,您可以使用GDB來探索您的堆棧以瞭解其原因。

GDB對於獲得正確的緩衝區溢出結構很有用。 但是,一旦按預期工作,請記住,在GDB之外執行時可能會移動內存地址,特別是如果您編譯時沒有調試標誌(-g)。你將不得不隨身帶回地址,以便在'現場'環境中獲得它想要的位置。


旁白

一般情況下,直接運行注入的代碼緩衝區溢出攻擊是一般不能與今天的堆棧保護機制是可行的。通常情況下,需要額外的漏洞才能執行任意代碼。

+2

當然,這個問題的關鍵是準確地揭示OP所打算的那種攻擊。此功能的廣泛使用使得此類攻擊比以前更加可行。 –

+0

我使用execstack和gcc'-z execstack'並使用'sudo execstack -s vuln',並驗證GNU堆棧被設置爲RWE(讀,寫執行)'readelf -l vuln',我仍然得到類似的錯誤。關於我在做什麼錯的想法?我注意到我需要溢出緩衝區的數量超過了108個字節,而是116個字節。 – nrabbit

+0

@nrabbit我現在沒有時間去做,但會在我的機器上做實驗,然後回到你身邊。 – sdsmith

1

我終於明白了這個問題。 GCC將我的指令和變量對齊到16字節的邊界。因此,它在序言部分添加了彙編指令,即lea ecx,[esp+0x4]等,並且在函數結尾處類似地將堆棧指針移動了一些,並且與堆棧的佈局有關。我重新編譯了-mpreferred-stack-boundary=2,這個問題在GDB中解決了。現在我只需要像你提到的那樣使用返回地址。謝謝!!