[摘要]首先解釋了緩沖區溢出的概念和溢出原理,并在VC++環境下比較了緩沖區非溢出、下標越界溢出、堆溢出、綜合代碼和激活記錄溢出四種情況,由此總結出了避免緩沖區溢出的基本方法;其次通過一個導致緩沖區溢出的小程序對緩沖區溢出攻擊的產生進行了實例分析,總結出緩沖區溢出攻擊的類型;最后,從靜態防范和動態防范兩個方面提出了溢出的防范策略,緩沖區溢出攻擊的分析與防范對網絡信息安全具有非常重要的意義。
[關鍵詞]緩沖區;堆溢出;靜態防范:攻擊
一、緩沖區溢出概念
緩沖區是用戶為程序運行時在計算機中申請的一段連續的內存,它保存了給定類型的數據。緩沖區溢出指的是一種常見且危害很大的系統攻擊手段,通過向程序的緩沖區寫入超出其長度的內容,造成緩沖區的溢出,破壞程序的堆棧,使程序轉而執行其他的指令,以達到攻擊的目的。為了達到這個目的,攻擊者必須達到如下的兩個目標:(1)程序的地址空間里安排適當的代碼。(2)通過適當的初始化寄存器和內存,讓程序跳轉到入侵者安排的地址空間執行。
二、緩沖區溢出概念危害
在當前網絡與分布式系統安全中,被廣泛利用的50%以上都是緩沖區溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕蟲。而緩沖區溢出中,最為危險的是堆棧溢出,因為入侵者可以利用堆棧溢出,在函數返回時改變返回程序的地址,讓其跳轉到任意地址,帶來的危害一種是程序崩潰導致拒絕服務;另外一種是跳轉并且執行一段惡意代碼,比如得到shell,然后為所欲為。
三、緩沖區溢出的原理
通過往程序的緩沖區寫超出其長度的內容,造成緩沖區的溢出,破壞程序的堆棧,造成程序崩潰或使程序轉而執行其它指令,以達到攻擊的目的。造成緩沖區溢出的原因是程序中沒有仔細檢查用戶輸入的參數。例如下面程序:
void function(char*str){
char buffer[16];
strcpy(buffer,str);}
上面的strcpy()將直接把str中的內容copy到buffer中。這樣只要str的長度大于16,就會造成buffer的溢出,使程序運行出錯。存在像strcpy這樣的問題的標準函數還有strcat(),sprintf(),vsprintf(),gets(),scanf()等。當然,隨便往緩沖區中填東西造成它溢出只會出現“分段錯誤”(Seg-mentation fault),而不能達到攻擊的目的。最常見的手段是通過制造緩沖區溢出使程序運行一個用戶shell,再通過shell執行其它命令。如果該程序屬于root且有suid權限的話,攻擊者就獲得了一個有root權限的shell,可以對系統進行任意操作了。
緩沖區溢出攻擊之所以成為一種常見安全攻擊手段其原因在于緩沖區溢出漏洞太普遍,并且易于實現。緩沖區溢出成為遠程攻擊的主要手段其原因在于緩沖區溢出漏洞給予了攻擊者所想要的一切,植入并且執行攻擊代碼。被植入的攻擊代碼以一定的權限運行有緩沖區溢出漏洞的程序,而得到被攻擊主機的控制權。在1998年Lincoln實驗室用來評估入侵檢測的的5種遠程攻擊中,有2種是緩沖區溢出。在1998年CERT的13份建議中,有9份與緩沖區溢出有關,在1999年,至少有半數的建議和緩沖區溢出有關。在Bugtraq的調查中,有2/3的被調查者認為緩沖區溢出漏洞是一個很嚴重的安全問題。
緩沖區溢出漏洞和攻擊有很多種形式,會在第二節對他們進行描述和分類。相應地防衛手段也隨著攻擊方法的不同而不同,將在第四節描述,它的內容包括針對每種攻擊類型制定有效的防衛手段。
四、緩沖區溢出分類
1.堆棧溢出。破壞系統堆棧中被調用函數的返回地址是緩沖區溢出攻擊者最常用的方法。首先通過輸入大量的數據,以此造成緩沖區溢出,如果溢出改變了堆棧中保存的函數返回地址,則函數返回時,可使程序轉向執行其它指令,以達到攻擊的目的。攻擊代碼一般是類似于exce(“sh”)的這樣能獲得系統控制權的程序。通常情況下攻擊者輸入的超長字符串實際上是含一個二進制機器語言的小程序,這就是攻擊代碼。因此,改變的函數返回地址將指向溢出數據,也就是這段攻擊代碼。
2.修改函數指針溢出。如果程序中使用了函數指針變量,例如類似于“void(*foo)()”這樣的語句。函數的指針可能位于堆棧靜態數據區中等,如果被攻擊者攻擊的緩沖區點恰好就在某個指針的附近,攻擊者就有機會修改函數指針,當程序通過指針變量調用該函數時就會轉去執行攻擊者事先放置的代碼。
3.長跳轉溢出。在c語言中包含了一個簡單的檢驗/恢復系統,稱為“set jmp/long jmp”,意思是在檢驗點設定“setjmp(buffer)”,用“long jmp(buffer)”來恢復檢驗點。如果攻擊時能夠進入緩沖區的空間,“long jmp(buffer)”實際上是跳轉到攻擊者的代碼。像函數指針一樣,long jmp緩沖區能夠指向任何地方,所以攻擊者所要做的就是找到一個可供溢出的緩沖區。例如Perl 5.003,攻擊者首先進入用來恢復緩沖區溢出的long jmp緩沖區,然后誘導進入恢復模式,這樣就使Perl的解釋器跳轉到攻擊代碼上了。
4.綜合代碼植入和激活記錄溢出。最簡單和常見的溢出緩沖區攻擊類型就是在一個字符串里綜合了代碼植入和激活記錄。攻擊者定位一個可供溢出的自動變量,然后向程序傳遞一個很大的字符串,在引發緩沖區溢出改變激活記錄的同時植入代碼(C語言程序員通常在習慣上只為用戶和參數開辟很小的緩沖區)。
代碼植入和緩沖區溢出不一定要在一次動作內完成,攻擊者可以在一個緩沖區內放置代碼(這個時候并不能溢出緩沖區),然后攻擊者通過溢出另一個緩沖區來轉移程序的指針。這樣的方法一般用來解決可供溢出的緩沖區不夠大(不能放下全部的代碼)。如果攻擊者試圖使用已經常駐的代碼而不是從外部植入代碼,他們通常必須把代碼作為參數。例如在libc(幾乎所有的C程序都用它來連接)中的一部分代碼段會執“exec(something)”,其中something就是參數,攻擊者使用緩沖區溢出改變程序的參數,利用另一個緩沖區溢出,使程序指針指向libc中的特定的代碼段。
五、緩沖區溢出攻擊的防范策略
1.下標越界溢出防范。只要在所有拷貝數據的地方進行數據長度和有效性的檢查,確保目標緩沖區中數據不越界并有效,就可避免緩沖區溢出,更不可能使程序跳轉到惡意代碼上。但諸如C/C++自身是一種不進行強類型和長度檢查的一種程序設計語言,而程序員在編寫代碼時由于開發速度和代碼的簡潔性,往往忽視了程序的健壯性,導致緩沖區溢出,因此,我們必須從程序語言和系統結構方面加強防范。很多不安全程序的出現是由于調用了一些不安全的庫函數,這些庫函數往往沒有對數組邊界進行檢查。這些函數有strcpy()、sprintf()、str-cat()等,所以一種簡單的方法是利用grep搜索源程序,找出對這些函數的調用,然后代以更安全的函數,如strncpy()替換strcpy()。進一步的查找可以是檢查更廣范圍的不安全操作,如在一個不定循環中對數組的賦值等。
2.堆溢出的防范。緩沖區溢出的根本原因是沒有數組邊界檢查,當數組被溢出時,一些關鍵的數據就有可能被修改,攻擊代碼也可以被植入。因此,對數組進行邊界檢查,使超長代碼不可能植入,就完全沒有了緩沖區溢出攻擊產生的條件。只要數組不能被溢出,溢出攻擊就無從談起。
為了實現數組邊界檢查,則所有的對數組的讀寫操作都應當被檢查,以確保對數組的操作在正確的范圍內。最直接的方法是檢查所有的數組操作,但會使性能下降很多,通常可采用一些優化的技術來減少檢查次數。