1. 棧溢出的原因
棧溢出(stack-based buffer overflows)算是安全界常見的漏洞。一方面因爲程序員的疏忽,使用了
strcpy、sprintf 等不安全的函數,增加了棧溢出漏洞的可能。另一方面,因爲棧上保存了函數的返回地址等信息,因此如果攻擊者能任意覆蓋棧上的數據,通常情況下就意味着他能修改程序的執行流程,從而造成更大的破壞。這種攻擊方法就是棧溢出攻擊(stack
smashing attacks)。
棧是從高地址到低地址增長的。
棧溢出攻擊的原因是由於程序中缺少錯誤檢測,另外對緩衝區的潛在操作(比如字符串的複製)都是從內存低址到高址,而函數調用的返回地址往往就在緩衝區的上方(當前棧底),這爲我們覆蓋返回地址提供了條件。下面是stack smashing attacks示意圖:
#include <stdio.h>
#include <string.h>
int bof(FILE *badfile){
char buffer[20];
fread(buffer, sizeof(char), 100, badfile);
return 1;
}
int main(){
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile);
return 0;
}
DEMO的邏輯很簡單,就是從badfile文件中讀取最長100字節的數據,然而buffer的長度只有20字節,所以這裏是有可能發現棧溢出的。
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
call ___main
movl $LC0, 4(%esp)
movl $LC1, (%esp)
call _fopen
movl %eax, 28(%esp)
movl 28(%esp), %eax
movl %eax, (%esp)
call _bof
movl $LC2, (%esp)
call _puts
movl 28(%esp), %eax
movl %eax, (%esp)
call _fclose
movl $0, %eax
leave
ret
_bof:
pushl %ebp
movl %esp, %ebp
subl $56, %esp
movl 8(%ebp), %eax
movl %eax, 12(%esp)
movl $100, 8(%esp)
movl $1, 4(%esp)
leal -28(%ebp), %eax
movl %eax, (%esp)
call _fread
movl $1, %eax
leave
ret
我們只關注從main進入bof以及bof執行完畢後返回main這個過程。
- 在調用從call __fopen開始看,在進入__bof前,badfile地址已經入棧。
- call _bof語句的作用則是把下一條指令(movl $LC2, (%esp))入棧,也就是_bof執行完畢後的返回地址
- 在進入_bof後,第一時間把ebp入棧,ebp是當前棧底,用於恢復esp的。
2. 棧溢出漏洞的防護:
stack smashing attacks並不是無敵的,其對抗技術就是DEP(Data
Execution Prevention )和ASLR(Address Space Layout Radomization),通過這兩種技術的保護下Stack
smashing attacks一定程度上揭制。
DEP: 把stack 段設置爲 不可執行。
另一種針對stack smashing 的防護技術就是 : stack canary.
2.1 stack canary
要檢測對函數棧的破壞,需要修改函數棧的組織,在緩衝區和控制信息(如 EBP 等)間插入一個 canary word。這樣,當緩衝區被溢出時,在返回地址被覆蓋之前 canary word 會首先被覆蓋。通過檢查 canary word 的值是否被修改,就可以判斷是否發生了溢出攻擊。
3. GCC 中使用 stack canary 技術的堆棧保護:
Stack Guard 是第一個使用 Canaries 探測的堆棧保護實現,它於 1997 年作爲 GCC 的一個擴展發佈。最初版本的 Stack Guard 使用 0x00000000 作爲 canary word。儘管很多人建議把 Stack Guard 納入 GCC,作爲 GCC 的一部分來提供堆棧保護。但實際上,GCC 3.x 沒有實現任何的堆棧保護。直到 GCC 4.1 堆棧保護才被加入,並且 GCC4.1 所採用的堆棧保護實現並非 Stack Guard,而是 Stack-smashing Protection(SSP,又稱 ProPolice)。
SSP 在 Stack Guard 的基礎上進行了改進和提高。它是由 IBM 的工程師 Hiroaki Rtoh 開發並維護的。與 Stack Guard 相比,SSP 保護函數返回地址的同時還保護了棧中的 EBP 等信息。此外,SSP 還有意將局部變量中的數組放在函數棧的高地址,而將其他變量放在低地址。這樣就使得通過溢出一個數組來修改其他變量(比如一個函數指針)變得更爲困難。
GCC與 stack 保護相關的編譯選項:
-fstack-protector:
啓用堆棧保護,不過只爲局部變量中含有 char 數組的函數插入保護代碼。
-fstack-protector-all:
啓用堆棧保護,爲所有函數插入保護代碼。
-fno-stack-protector:
禁用堆棧保護。