棧溢出漏洞及棧溢出攻擊

1. 棧溢出的原因

棧溢出(stack-based buffer overflows)算是安全界常見的漏洞。一方面因爲程序員的疏忽,使用了 strcpy、sprintf 等不安全的函數,增加了棧溢出漏洞的可能。另一方面,因爲棧上保存了函數的返回地址等信息,因此如果攻擊者能任意覆蓋棧上的數據,通常情況下就意味着他能修改程序的執行流程,從而造成更大的破壞。這種攻擊方法就是棧溢出攻擊(stack smashing attacks)
        棧是從高地址到低地址增長的。 

棧溢出攻擊的原因是由於程序中缺少錯誤檢測,另外對緩衝區的潛在操作(比如字符串的複製)都是從內存低址到高址,而函數調用的返回地址往往就在緩衝區的上方(當前棧底),這爲我們覆蓋返回地址提供了條件。下面是stack smashing attacks示意圖:


    下面是一個存在棧溢出的DEMO:

         #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字節,所以這裏是有可能發現棧溢出的。

下面是在cygwin的環境下編譯出來的彙編代碼(我已經把一些對邏輯理解無關的細節去掉):

_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的。
       整個堆棧的內存佈局如下所示:

       從分佈圖可以看到,系統實際分配給buffer的長度是28字節,接下來就是舊的棧底地址,bof返回地址和badfile地址。因此當badfile的內容長度是低於28字節的情況下,程序依然可以正常運行。但當badfile的內容長度超出28字節,就會直接把old EBP和ret address覆蓋掉,這就達到了修改返回地址的目的了。


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 保護相關的編譯選項:

CC 4.1 中三個與堆棧保護有關的編譯選項

-fstack-protector:

啓用堆棧保護,不過只爲局部變量中含有 char 數組的函數插入保護代碼。

-fstack-protector-all:

啓用堆棧保護,爲所有函數插入保護代碼。

-fno-stack-protector:

禁用堆棧保護。



         



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章