溢出

溢出

程序設計者設計時的不足所帶來的錯誤
溢出是黑客利用操作系統的漏洞,專門開發了一種程序,加相應的參數運行後,就可以得到你電腦具有管理員資格的控制權,你在你自己電腦上能夠運行的東西他可以全部做到,等于你的電腦就是他的了。[1]溢出可分為緩沖區溢出、内存溢出、數據溢出等多類,使緩沖區溢出的任何嘗試通常都會被該語言本身自動檢測并阻止。
    中文名:溢出 外文名: 别名: buffers:長跳轉緩沖區 激活紀錄:Activation Records 分類:緩沖區溢出

基本介紹

溢出是在你自己電腦上能夠運行的東西他可以全部做到,等于你的電腦就是他的了。在黑客頻頻攻擊、在系統漏洞層出不窮的今天,作為網絡管理員、系統管理員的我們雖然在服務器的安全上都下了不少功夫:諸如,及時的打上系統安全補丁、進行一些常規的安全配置,但是仍然不太可能每台服務器都會在第一時間内給系統打上全新補丁。因此我們必需要在還未被入侵之前,通過一些系列安全設置,來将入侵者們擋在“安全門”之外。

溢出分類

1.1在程序的地址空間裡安排适當的代碼

1.1.1殖入法

攻擊者用被攻擊程序的緩沖區來存放攻擊代碼。攻擊者向被攻擊的程序輸入一個字符串,程序會把這個字符串放到緩沖區裡。這個字符串包含的數據是可以在這個被攻擊的硬件平台上運行的指令序列。

1.1.2利用已經存在的代碼

有時候,攻擊者想要的代碼已經在被攻擊的程序中了,攻擊者所要做的隻是對代碼傳遞一些參數,然後使程序跳轉到指定目标。比如,在C語言中,攻擊代碼要求執行“exec("/bin/sh")”,而在libc庫中的代碼執行“exec(arg)”,其中arg是指向一個字符串的指針參數,那麼攻擊者隻要把傳入的參數指針指向"/bin/sh",就可以調轉到libc庫中的相應的指令序列。

1.2控制程序轉移到攻擊代碼

這種方法旨在改變程序的執行流程,使之跳轉到攻擊代碼。最基本方法的就是溢出一個沒有邊界檢查或者其他弱點的緩沖區,這樣就擾亂了程序的正常的執行順序。通過溢出一個緩沖區,攻擊者可以用近乎暴力的方法改寫相鄰的程序空間而直接跳過了系統的檢查。

1.2.1激活紀錄(ActivationRecords)

每當一個函數調用發生時,調用者會在堆棧中留下一個激活紀錄,它包含了函數結束時返回的地址。攻擊者通過溢出這些自動變量,使這個返回地址指向攻擊代碼。通過改變程序的返回地址,當函數調用結束時,程序就跳轉到攻擊者設定的地址,而不是原先的地址。這類的緩沖區溢出被稱為“stacksmashingattack”,是目前常用的緩沖區溢出攻擊方式。

1.2.2函數指針(FunctionPointers)

C語言中,“void(*foo)()”聲明了一個返回值為void函數指針的變量foo。函數指針可以用來定位任何地址空間,所以攻擊者隻需在任何空間内的函數指針附近找到一個能夠溢出的緩沖區,然後溢出這個緩沖區來改變函數指針。在某一時刻,當程序通過函數指針調用函數時,程序的流程就按攻擊者的意圖實現了!它的一個攻擊範例就是在Linux系統下的superprobe程序。

1.2.3長跳轉緩沖區(Longjmpbuffers)

在C語言中包含了一個簡單的檢驗/恢複系統,稱為setjmp/longjmp。意思是在檢驗點設定“setjmp(buffer)”,用“longjmp(buffer)”來恢複檢驗點。然而,如果攻擊者能夠進入緩沖區的空間,那麼“longjmp(buffer)”實際上是跳轉到攻擊者的代碼。象函數指針一樣,longjmp緩沖區能夠指向任何地方,所以攻擊者所要做的就是找到一個可供溢出的緩沖區。一個典型的例子就是Perl5.003,攻擊者首先進入用來恢複緩沖區溢出的的longjmp緩沖區,然後誘導進入恢複模式,這樣就使Perl的解釋器跳轉到攻擊代碼上了!

最簡單和常見的緩沖區溢出攻擊類型就是在一個字符串裡綜合了代碼殖入和激活紀錄。攻擊者定位一個可供溢出的自動變量,然後向程序傳遞一個很大的字符串,在引發緩沖區溢出改變激活紀錄的同時殖入了代碼。這個是由Levy指出的攻擊的模闆。因為C語言在習慣上隻為用戶和參數開辟很小的緩沖區,因此這種漏洞攻擊的實例不在少數。

代碼殖入和緩沖區溢出不一定要在一次動作内完成。攻擊者可以在一個緩沖區内放置代碼,這是不能溢出緩沖區。然後,攻擊者通過溢出另外一個緩沖區來轉移程序的指針。這種方法一般用來解決可供溢出的緩沖區不夠大的情況。

如果攻擊者試圖使用已經常駐的代碼而不是從外部殖入代碼,他們通常有必須把代碼作為參數化。舉例來說,在libc中的部分代碼段會執行“exec(something)”,其中something就是參數。攻擊者然後使用緩沖區溢出改變程序的參數,利用另一個緩沖區溢出使程序指針指向libc中的特定的代碼段。

為什麼緩沖區溢出如此常見

在幾乎所有計算機語言中,不管是新的語言還是舊的語言,使緩沖區溢出的任何嘗試通常都會被該語言本身自動檢測并阻止(比如通過引發一個異常或根據需要給緩沖區添加更多空間)。但是有兩種語言不是這樣:C和C++語言。C和C++語言通常隻是讓額外的數據亂寫到其餘内存的任何位置,而這種情況可能被利用從而導緻恐怖的結果。更糟糕的是,用C和C++編寫正确的代碼來始終如一地處理緩沖區溢出則更為困難;很容易就會意外地導緻緩沖區溢出。除了C和C++使用得非常廣泛外,上述這些可能都是不相關的事實;例如,RedHatLinux7.1中86%的代碼行都是用C或C++編寫的。因此,大量的代碼對這個問題都是脆弱的,因為實現語言無法保護代碼避免這個問題。

在C和C++語言本身中,這個問題是不容易解決的。該問題基于C語言的根本設計決定(特别是C語言中指針和數組的處理方式)。由于C++是最兼容的C語言超集,它也具有相同的問題。存在一些能防止這個問題的C/C++兼容版本,但是它們存在極其嚴重的性能問題。而且一旦改變C語言來防止這個問題,它就不再是C語言了。許多語言(比如Java和C#)在語法上類似C,但它們實際上是不同的語言,将現有C或C++程序改為使用那些語言是一項艱巨的任務。

然而,其他語言的用戶也不應該沾沾自喜。有些語言存在允許緩沖區溢出發生的“轉義”子句。Ada一般會檢測和防止緩沖區溢出(即針對這樣的嘗試引發一個異常),但是不同的程序可能會禁用這個特性。C#一般會檢測和防止緩沖區溢出,但是它允許程序員将某些例程定義為“不安全的”,而這樣的代碼可能會導緻緩沖區溢出。因此如果您使用那些轉義機制,就需要使用C/C++程序所必須使用的相同種類的保護機制。許多語言都是用C語言來實現的(至少部分是用C語言來實現的),并且用任何語言編寫的所有程序本質上都依賴用C或C++編寫的庫。因此,所有程序都會繼承那些問題,所以了解這些問題是很重要的。

防止緩沖區溢出的新技術

當然,要讓程序員不犯常見錯誤是很難的,而讓程序(以及程序員)改為使用另一種語言通常更為困難。那麼為何不讓底層系統自動保護程序避免這些問題呢?最起碼,避免stack-smashing攻擊是一件好事,因為stack-smashing攻擊是特别容易做到的。

一般來說,更改底層系統以避免常見的安全問題是一個極好的想法,我們在本文後面也會遇到這個主題。事實證明存在許多可用的防禦措施,而一些最受歡迎的措施可分組為以下類别:

基于探測方法(canary)的防禦。這包括StackGuard(由Immunix所使用)、ProPolice(由OpenBSD所使用)和Microsoft的/GS選項。

非執行的堆棧防禦。這包括SolarDesigner的non-exec補丁(由OpenWall所使用)和execshield(由RedHat/Fedora所使用)。

其他方法。這包括libsafe(由Mandrake所使用)和堆棧分割方法。

遺憾的是,迄今所見的所有方法都具有弱點,因此它們不是萬能藥,但是它們會提供一些幫助。

相關詞條

相關搜索

其它詞條