概念
電腦程式一般都會使用到一些記憶體,這些記憶體或是程式內部使用,或是存放用戶的輸入數據,這樣的記憶體一般稱作緩衝區。溢出是指盛放的東西超出容器容量而溢出來了,在電腦程式中,就是數據使用到了被分配記憶體空間之外的記憶體空間。而緩衝區溢出,簡單的說就是計算機對接收的輸入數據沒有進行有效的檢測(理想的情況是程式檢查數據長度並不允許輸入超過緩衝區長度的字元),向緩衝區內填充數據時超過了緩衝區本身的容量,而導致數據溢出到被分配空間之外的記憶體空間,使得溢出的數據覆蓋了其他記憶體空間的數據。
危害
在計算機安全領域,緩衝區溢出就好比給自己的程式開了個後門,這種安全隱患是致命的。緩衝區溢出在各種作業系統、套用軟體中廣泛存在。而利用緩衝區溢出漏洞實施的攻擊就是緩衝區溢出攻擊。緩衝區溢出攻擊,可以導致程式運行失敗、系統關機、重新啟動,或者執行攻擊者的指令,比如非法提升許可權。
在當前網路與分散式系統安全中,被廣泛利用的50%以上都是緩衝區溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕蟲。而緩衝區溢出中,最為危險的是堆疊溢出,因為入侵者可以利用堆疊溢出,在函式返回時改變返回程式的地址,讓其跳轉到任意地址,帶來的危害一種是程式崩潰導致拒絕服務,另外一種就是跳轉並且執行一段惡意代碼,比如得到shell,然後為所欲為。
攻擊
原理
通過往程式的緩衝區寫超出其長度的內容,造成緩衝區的溢出,從而破壞程式的堆疊,造成程式崩潰或使程式轉而執行其它指令,以達到攻擊的目的。造成緩衝區溢出的原因是程式中沒有仔細檢查用戶輸入的參數。
下面通過一個示例來詳細看看什麼是緩衝區溢出。程式的緩衝區就像一個個格子,每個格子中存放不同的東西,有的是命令,有的是數據,當程式需要接收用戶數據,程式預先為之分配了4個格子(下圖1中黃色的0~3號格子)。按照程式設計,就是要求用戶輸入的數據不超過4個。而用戶在輸入數據時,假設輸入了16個數據,而且程式也沒有對用戶輸入數據的多少進行檢查,就往預先分配的格子中存放,這樣不僅4個分配的格子被使用了,其後相鄰的12個格子中的內容都被新數據覆蓋了。這樣原來12個格子中的內容就丟失了。這時就出現了緩衝區(0~3號格子)溢出了。
在上面示例的基礎上來看一個代碼實例,程式如下:
void function(char *str) {
char buffer[16];
strcpy(buffer,str);
}
上面的strcpy()將直接把str中的內容copy到buffer中。這樣只要str的長度大於16,就會造成buffer的溢出,使程式運行出錯。存在象strcpy這樣的問題的標準函式還有strcat(),sprintf(),vsprintf(),gets(),scanf()等。
當然,隨便往緩衝區中填東西造成它溢出一般只會出現“分段錯誤”(Segmentation fault),而不能達到攻擊的目的。最常見的手段是通過製造緩衝區溢出使程式運行一個用戶shell,再通過shell執行其它命令。如果該程式有root或者suid執行許可權的話,攻擊者就獲得了一個有root許可權的shell,可以對系統進行任意操作了。
緩衝區溢出攻擊之所以成為一種常見安全攻擊手段其原因在於緩衝區溢出漏洞太普遍了,並且易於實現。而且,緩衝區溢出成為遠程攻擊的主要手段其原因在於緩衝區溢出漏洞給予了攻擊者他所想要的一切:植入並且執行攻擊代碼。被植入的攻擊代碼以一定的許可權運行有緩衝區溢出漏洞的程式,從而得到被攻擊主機的控制權。
在1998年Lincoln實驗室用來評估入侵檢測的的5種遠程攻擊中,有2種是緩衝區溢出。而在1998年CERT的13份建議中,有9份是是與緩衝區溢出有關的,在1999年,至少有半數的建議是和緩衝區溢出有關的。在Bugtraq的調查中,有2/3的被調查者認為緩衝區溢出漏洞是一個很嚴重的安全問題。
緩衝區溢出漏洞和攻擊有很多種形式,會在第二節對他們進行描述和分類。相應地防衛手段也隨者攻擊方法的不同而不同,將在第四節描述,它的內容包括針對每種攻擊類型的有效的防衛手段。
漏洞
緩衝區溢出攻擊的目的在於擾亂具有某些特權運行的程式的功能,這樣可以使得攻擊者取得程式的控制權,如果該程式具有足夠的許可權,那么整個主機就被控制了。一般而言,攻擊者攻擊root程式,然後執行類似“exec(sh)”的執行代碼來獲得root許可權的shell。為了達到這個目的,攻擊者必須達到如下的兩個目標:
1. 在程式的地址空間裡安排適當的代碼。
2. 通過適當的初始化暫存器和記憶體,讓程式跳轉到入侵者安排的地址空間執行。
可以根據這兩個目標來對緩衝區溢出攻擊進行分類。
實驗分析
2000年1月,Cerberus 安全小組發布了微軟的IIS 4/5存在的一個緩衝區溢出漏洞。攻擊該漏洞,可以使Web伺服器崩潰,甚至獲取超級許可權執行任意的代碼。微軟的IIS 4/5 是一種Web伺服器程式;因而,該緩衝區溢出漏洞對於網站的安全構成了極大的威脅;它的描述如下:
瀏覽器向IIS提出一個HTTP請求,在域名(或IP位址)後,加上一個檔案名稱,該檔案名稱以“.htr”做後綴。於是IIS認為客戶端正在請求一個“.htr”檔案,“.htr”擴展檔案被映像成ISAPI(Internet Service API)應用程式,IIS會復位向所有針對“.htr”資源的請求到 ISM.DLL程式 ,ISM.DLL 打開這個檔案並執行之。
瀏覽器提交的請求中包含的檔案名稱存儲在局部變數緩衝區中,若它很長,超過600個字元時,會導致局部變數緩衝區溢出,覆蓋返回地址空間,使IIS崩潰。更進一步,在如圖1所示的2K緩衝區中植入一段精心設計的代碼,可以使之以系統超級許可權運行。
預防措施
緩衝區溢出是代碼中固有的漏洞,除了在開發階段要注意編寫正確的代碼之外,對於用戶而言,一般的防範措施為
1、關閉連線埠或服務。管理員應該知道自己的系統上安裝了什麼,並且哪些服務正在運行
2、安裝軟體廠商的補丁,漏洞一公布,大的廠商就會及時提供補丁
3、在防火牆上過濾特殊的流量,無法阻止內部人員的溢出攻擊
4、自己檢查關鍵的服務程式,看看是否有可怕的漏洞
5、以所需要的最小許可權運行軟體
防範方法
緩衝區溢出攻擊占了遠程網路攻擊的絕大多數,這種攻擊可以使得一個匿名的Internet用戶有機會獲得一台主機的部分或全部的控制權。如果能有效地消除緩衝區溢出的漏洞,則很大一部分的安全威脅可以得到緩解。
目前有四種基本的方法保護緩衝區免受緩衝區溢出的攻擊和影響。
保護
這個方法使得緩衝區溢出不可能出現,從而完全消除了緩衝區溢出的威脅,但是相對而言代價比較大。
完整性檢查
在程式指針失效前進行完整性檢查。雖然這種方法不能使得所有的緩衝區溢出失效,但它能阻止絕大多數的緩衝區溢出攻擊。
非執行的緩衝區
通過使被攻擊程式的數據段地址空間不可執行,從而使得攻擊者不可能執行被植入被攻擊程式輸入緩衝區的代碼,這種技術被稱為非執行的緩衝區技術。在早期的Unix系統設計中,只允許程式代碼在代碼段中執行。但是Unix和MS Windows系統由於要實現更好的性能和功能,往往在數據段中動態地放入可執行的代碼,這也是緩衝區溢出的根源。為了保持程式的兼容性,不可能使得所有程式的數據段不可執行。
但是可以設定堆疊數據段不可執行,這樣就可以保證程式的兼容性。Linux和Solaris都發布了有關這方面的核心補丁。因為幾乎沒有任何合法的程式會在堆疊中存放代碼,這種做法幾乎不產生任何兼容性問題,除了在Linux中的兩個特例,這時可執行的代碼必須被放入堆疊中:
信號傳遞
Linux通過向進程堆疊釋放代碼然後引發中斷來執行在堆疊中的代碼來實現向進程傳送Unix信號。非執行緩衝區的補丁在傳送信號的時候是允許緩衝區可執行的。
GCC的線上重用
研究發現gcc在堆疊區里放置了可執行的代碼作為線上重用之用。然而,關閉這個功能並不產生任何問題,只有部分功能似乎不能使用。
非執行堆疊的保護可以有效地對付把代碼植入自動變數的緩衝區溢出攻擊,而對於其它形式的攻擊則沒有效果。通過引用一個駐留的程式的指針,就可以跳過這種保護措施。其它的攻擊可以採用把代碼植入堆或者靜態數據段中來跳過保護。
編寫安全的代碼
編寫正確的代碼是一件非常有意義的工作,特別象編寫C語言那種風格自由而容易出錯的程式,這種風格是由於追求性能而忽視正確性的傳統引起的。儘管花了很長的時間使得人們知道了如何編寫安全的程式,具有安全漏洞的程式依舊出現。因此人們開發了一些工具和技術來幫助經驗不足的程式設計師編寫安全正確的程式。
最簡單的方法就是用grep來搜尋原始碼中容易產生漏洞的庫的調用,比如對strcpy和sprintf的調用,這兩個函式都沒有檢查輸入參數的長度。事實上,各個版本C的標準庫均有這樣的問題存在。
此外,人們還開發了一些高級的查錯工具,如fault injection等。這些工具的目的在於通過人為隨機地產生一些緩衝區溢出來尋找代碼的安全漏洞。還有一些靜態分析工具用於偵測緩衝區溢出的存在。
雖然這些工具幫助程式設計師開發更安全的程式,但是由於C語言的特點,這些工具不可能找出所有的緩衝區溢出漏洞。所以,偵錯技術只能用來減少緩衝區溢出的可能,並不能完全地消除它的存在。