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中使用print
或x/x
來探索堆棧的外觀。例如,您可以檢查$rbp+8
以查看堆棧上的返回地址(RA)。
GDB會SIGSEGV
在ret
指令,如果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
)。你將不得不隨身帶回地址,以便在'現場'環境中獲得它想要的位置。
旁白
一般情況下,直接運行注入的代碼緩衝區溢出攻擊是一般不能與今天的堆棧保護機制是可行的。通常情況下,需要額外的漏洞才能執行任意代碼。
當然,這個問題的關鍵是準確地揭示OP所打算的那種攻擊。此功能的廣泛使用使得此類攻擊比以前更加可行。 –
我使用execstack和gcc'-z execstack'並使用'sudo execstack -s vuln',並驗證GNU堆棧被設置爲RWE(讀,寫執行)'readelf -l vuln',我仍然得到類似的錯誤。關於我在做什麼錯的想法?我注意到我需要溢出緩衝區的數量超過了108個字節,而是116個字節。 – nrabbit
@nrabbit我現在沒有時間去做,但會在我的機器上做實驗,然後回到你身邊。 – sdsmith