摘要:分析緩沖區溢出及其溢出植入代碼的組成結構,指出相應的防御措施;通過分析溢出和植入代碼的組成結構,給出緩沖區溢出的一般規律和溢出攻擊植入代碼的結構特征;最后討論如何根據溢出規律和植入代碼的結構特征,采取有效措施來防御緩沖區溢出攻擊。
關鍵詞:緩沖區溢出; 植入代碼; 緩沖區溢出防御虛擬機
中圖法分類號:TP393.08文獻標識碼:A
文章編號:1001-3695(2007)01-0164-03
自“Morris Worm”[1]事件以來,緩沖區溢出攻擊已成為目前最主要、最具威脅性的網絡攻擊之一。據Cert統計,目前緩沖區溢出攻擊已經占到了整個網絡攻擊次數的一半以上,對全球的計算機系統造成了巨大的破壞。
緩沖區溢出攻擊利用了當前軟件中的緩沖區溢出漏洞,而軟件中的緩沖區溢出漏洞主要源于C/C++語言的不安全性。目前大部分操作系統和應用軟件都是采用C/C++語言編寫。從理論角度上講,采用其他安全語言(如Java,C#)取代C/C++可以從根本上杜絕緩沖區溢出問題,但實際應用中,許多因素決定了這樣做是非常困難的。因此,緩沖區溢出漏洞仍然會在今后相當長的時期內廣泛存在。
緩沖區溢出攻擊可以導致程序運行失敗、系統崩潰以及重新啟動甚至整個網絡癱瘓等結果。更為重要的是,它可以利用緩沖區溢出執行非授權指令,甚至取得系統特權。因此,深入研究緩沖區溢出的原理及其植入代碼的結構特征,對于探索如何防御溢出攻擊具有重要意義。
1緩沖區溢出原理
當程序運行時,計算機會在內存區域開辟一段連續的內存塊[1]。該內存塊即是緩沖區,包括文本區、數據區和堆棧區三部分。文本區存放機器碼和只讀數據,任何對該區的寫操作都會導致段錯誤;數據區包括已初始化的數據段和未初始化的數據(BSS)段,保存全局的和靜態的未初始化變量,在編譯時分配;堆棧區包括堆和棧。堆是一個為存儲變量而開辟的存儲空間,在程序運行時分配,堆的分配由malloc()類函數來實現;棧是一種用來存儲函數調用時的臨時信息的結構,在程序運行時分配。圖1是進程的內存組織形式示意圖。
當某個用戶/進程試圖往程序中一個固定長度的緩沖區放置比原來初分配的存儲空間還要多的數據時,通常會導致超越存儲邊界,影響相鄰內存空間的數據。當前存在以下幾種緩沖區溢出:棧溢出、堆溢出、BSS溢出。
1.1棧溢出
棧在函數調用時建立,包含了下面幾個信息:①函數的返回地址;②調用函數的棧幀信息,即棧頂和棧底;③為函數參數分配的空間;④為被調用函數的局部變量分配的空間。棧的分配如圖2所示。
棧的生長方向與內存的生長方向相反,是從低地址向高地址生長,其數據的壓入和彈出由PUSH和POP來完成,并且遵循先進后出的原則。當程序調用函數時,依次將函數參數、返回地址、棧幀指針和函數局部變量壓入棧中。
假如某函數中用到字符串復制函數strcpy(s,“AAA…”)(“AAA…”足夠長以覆蓋函數返回地址),則該函數執行后,棧中存放的數據可能變為如圖3所示。
可以看出,通過向函數的某個字符數組中復制超長的數據“AAAA…”,可以將其相鄰的框架指針、函數返回地址的值都改寫成“AAAA”。攻擊者通過這種方式改寫函數返回地址,轉而指向特定的內存地址并執行攻擊者指定的操作[1]。
1.2堆溢出
使用malloc()可以動態分配一段內存,并向用戶返回一個內存地址,而實際上這個地址前面通常還有8Bytes的內部結構,用來記錄分配的塊長度以及一些標志。圖4是一個已分配的堆的結構示意圖。
假設有如下的堆定義:
char *buf1 = (char *)malloc(16);
char *buf2 = (char *)malloc(16);
事先向buf2中寫入16個“A”,之后再往buf1寫入24個“B”。由圖5可知,通過溢出指針buf1,將buf1的內部保留的部分內容改寫成了“BBBBBBBB”,buf2的前8Bytes也被改寫成了“BBBBBBBB”。攻擊者可以通過改寫指針或者函數指針等方式[2],使其指向特定的內存地址并執行攻擊者指定的操作。
1.3BSS溢出
BSS段存放全局和靜態的未初始化變量,其分配比較簡單,變量與變量之間是連續的,沒有保留空間。如下定義的兩個字符數組即是位于BSS段:
static char buf1[16], buf2[16]。
假設事先向buf2寫入16個“A”,之后再往buf1寫入24個“B”。由圖6可看出,通過溢出靜態字符數組buf1,可改寫其相鄰區域字符數組buf2的值。利用這點,攻擊者可以通過改寫BSS中的指針或函數指針等方式,改變程序原先的執行流程,使指針跳轉到特定的內存地址并執行攻擊者指定的操作[2]。
1.4溢出的一般規律
上述三種溢出方式,無論是哪一種溢出,前提條件都是必須存在一個有漏洞的緩沖區,通過溢出該緩沖區,就可以改寫其相鄰區域的數據,如改寫諸如函數返回地址、指針、函數指針或者長跳轉緩沖區[3],這樣就可以使程序流程發生改變。如果攻擊者預先在目標機器的內存植入代碼,讓目標機器轉向該內存地址并執行該植入代碼,那么攻擊者就可能劫持程序的執行權;如果溢出的是有Root權限的程序,則意味著攻擊者可以控制目標機器。在圖7中,攻擊者利用目標系統的緩沖區溢出漏洞,向目標緩沖區輸入一段超長的數據——植入代碼(由填充數據N、Shellcode、Shellcode的入口地址R三部分組成),導致目標緩沖區溢出。如果植入代碼的R能正確地覆蓋相鄰區域的函數返回地址或者函數指針等,那么目標機器的執行流程就會發生跳轉,去執行攻擊者植入的Shellcode。
2植入代碼分析
溢出攻擊是否成功,關鍵在于構造的植入代碼是否合理。植入代碼一般由Shellcode、返回地址(用R表示)、填充數據(用N表示)三種元素按照一定構造類型構造組成[4]。
2.1Shellcode
Shellcode是植入代碼的核心組成部分,其功能主要是完成一定的系統調用。Shellcode實質是一串機器代碼,由于最初的Shellcode只是獲取一個基本的sh權限,所以稱為Shellcode。最初的Shellcode是利用exec()系統請求,執行“/bin/sh”,從而獲取Shell的。除了經典的利用exec()系統請求外,表1列出了在Shellcode中經常用到的其他系統調用[5]。
表1Shellcode中經常用到的其他系統調用
為了突破各種反入侵手段的防護和入侵檢測系統的檢測,目前已經有了許多高級的Shellcode[6],但其功能的實現依然是通過一定的系統調用。不過,也有的Shellcode干脆將其寫成木馬的形式,植入目標機器[7]。
2.2返回地址
返回地址是指Shellcode的入口地址。攻擊者如果希望目標機器改變其原來的執行流程,轉向執行Shellcode,則必須設法用Shellcode的入口地址覆蓋某個跳轉指令。但是,由于植入代碼是復制到目標機器的緩沖區中,至于緩沖區的確切內存地址,攻擊者是無法知道的。不過,內存的分配是有規律的,如Linux系統,當用戶程序運行時,棧是從0xbfffffff開始向內存低端生長的。如果攻擊者想通過改寫函數返回地址的方式使程序指令發生跳轉,則程序指令跳轉后的指向也應該在0xbfffffff附近。事實上,雖然不同的緩沖區溢出漏洞,其植入代碼的返回地址都不同,但均處于某個較小地址區間內[5]。
另外,為了提高覆蓋函數返回地址的成功率,往往在植入代碼中安排一段相同的返回地址,這對于入侵檢測來說是一個很重要的判斷特征。
2.3填充數據
由于攻擊者不能準確地判斷Shellcode的入口地址,因此為了提高Shellcode的命中率,往往在Shellcode的前面安排一定數量的填充數據。填充數據應該對植入代碼沒有影響,這樣只要返回地址指向填充數據中的任何一個位置,均可以確保Shellcode順利執行。另外,填充數據還可以起到一個作用,就是當植入代碼的長度還夠不著覆蓋目標,如函數返回地址時,可以增加填充數據的數量,使植入代碼的返回地址能夠覆蓋函數返回地址。
對于Intel CPU來說,填充數據實質上是一種單字節指令,使用得最多的是空操作指令NOP,其值為0x90,該指令什么也不做。除此之外還有其他的單字節指令可以作為填充數據使用,如調整計算結果的AAA和AAS、操作標志位的CLC和CLD等。在植入代碼中,往往安排比較長甚至幾百上千的填充數據,而一個有效的指令長度,其實際的最大長度也不過10Bytes左右[5],因此,也可以根據這一特點來判斷是否發生了緩沖區溢出攻擊。
2.4植入代碼的構造類型
植入代碼一般的構造形式是在Shellcode的后面安排一定數量的返回地址,在前面安排一定數量的填充數據,這種結構稱為NSR型,或前端同步型。這是一種經典結構,適合于溢出緩沖區較大的情況,這種構造類型是向前跳轉執行Shellcode的。其溢出原理如圖8所示。
但是,當目標的溢出緩沖區較小,使溢出緩沖區與溢出目標如函數返回地址之間的空間甚至連Shellcode也放置不下了,這時就只能采用其他的構造類型如RNS型(或稱中端同步型)。與NSR不同,RNS型是將返回地址安排在植入代碼的最前端,用返回地址去溢出緩沖區并改寫相鄰區域數據,再向后跳轉到Shellcode處執行。其溢出原理如圖9所示。
還有一種AR型(或稱環境變量型)。AR型不同于NSR和RNS,這種構造類型必須事先將Shellcode放置在環境變量中,然后將Shellcode的入口地址和填充數據構成植入代碼進行溢出攻擊。這種構造類型對于大、小溢出緩沖區都適合。但是由于該構造類型必須事先將Shellcode放置到環境變量中,故其應用就受到了限制,只能用于本地而不能用于遠程攻擊。其溢出原理如圖10所示。
3防御策略
從緩沖區溢出規律角度分析,一次成功的攻擊必須具備以下三個條件:①必須讓緩沖區發生溢出;②讓目標程序發生意外跳轉;③Shellcode要被正確執行。對于①,程序員在編寫程序時,不要使用沒有邊界檢查的函數,或者編譯程序時采用帶邊界檢查機制的編譯器[8,9],或設計一種可以在程序運行前后對邊界進行比較、檢查的機制等措施;對于②,可以在關鍵數據前設置警戒字[10]或者對關鍵數據進行備份,以防止惡意竄改等措施;對于③,則可以采取一種隨機內存分配技術[11]或堆棧不可執行[12,13]等措施。
從植入代碼的結構特點分析,對于以下幾個特征可以認為發生了溢出攻擊:①輸入的字符串中含有一段連續的較長單字節指令(有時可能幾百上千個字節長);②輸入的字符串中含有較長且相同的一段內存地址;③輸入的字符串中含有系統調用或者一些修改文件的指令。當發現了以上特征時,應立即采取適當措施,如中斷用戶數據的輸入、中斷用戶的訪問或者中斷該進程的運行等。此外,也可結合植入代碼的構造類型特點,對于輸入的一串連續數據,假如其前端是一段單(多)字節指令、中端是系統調用、后端是一段相同的地址,或者其前端是一段相同的地址、中端是一段單(多)字節指令、后端是系統調用,要立即對此數據認真分析,判明是否發生了溢出攻擊,以采取相應的措施。
也可以采用一種新的防御技術——緩沖區溢出防御虛擬機(DMBOVM)。采用該技術,可以通過如下幾個措施來防御緩沖區溢出:①接管所有可能涉及緩沖區溢出的系統調用指令;②檢查變量的邊界;③備份關鍵數據,如函數返回地址和函數指針;④對溢出攻擊進行記錄,對攻擊進行分析和判斷;⑤溢出攻擊發生時,中斷相應進程的運行。
4總結
緩沖區溢出漏洞是目前最主要的安全漏洞之一,而利用該漏洞的緩沖區溢出攻擊是目前最主要、最具威脅性的網絡攻擊之一。本文從多個角度研究了緩沖區溢出,給出了溢出的一般規律;另外深入分析了植入代碼的結構組成,給出了植入代碼的結構特征。本文根據溢出的一般規律,提出了相對應的防御措施;根據植入代碼的結構特征,提出了識別緩沖區溢出攻擊的判斷依據。最后,提出了一種新的防御技術——緩沖區溢出防御虛擬機,利用該技術可以有效地防御緩沖區溢出攻擊。
參考文獻:
[1]Aleph One.Smashing the Stack for Fun and Profit[J].Phrack,1996,7(49).
[2]Matt Conover. w00w00 on Heap Overflows[EB/OL]. http://www.w00w00.org/articles.html,1999.
[3]John Wilander,Mariam Kamkar. A Comparison of Publicly Available Tools for Dynamic Buffer Overflow Prevention[C]. The 10th Network and Distributed System Security Symposium,2003.
[4]aXis.緩沖區溢出筆記之——Stack溢出[EB/OL]. http://www.ph4nt0m.net, 20-04.
[5]邱曉鵬,張玉清,馮登國.緩沖區溢出攻擊代碼的分析研究[J].計算機工程與應用,2005,41(18):134135.
[6]Taeho Oh. Advanced Buffer Overflow Exploit [EB/OL]. http://postech.edu/~ohhara, 20-0409.
[7]于晗,孫龍霞,黃承夏.基于Windows緩沖區溢出漏洞的植入型木馬研究[J].信息安全與通信保密,2005,(7):248252.
[8]Compaq. Compaq C for Linux[EB/OL]. http:// www.unix.digital.com/linux/compaq_c/, 1999.
[9]Richard Jones, Paul Kelly. Bounds Checking for C[EB/OL]. http://wwwala.doc.ic.ac.uk/ phjk/ BoundsChecking.html, 1995.
[10]Crispin Cowan, Calton Pu, Dave Maier, et al. StackGuard: Automa ̄tic Adaptive Detection and Prevention of Buffer Overflow Attacks[C]. The 7th USENIX Security Conference, 1998.6377.
[11]PaX Team. PaX Address Space Layout Randomization (ASLR) [EB/OL]. http://pax.grsecurity.net/docs/aslr.txt.
[12]Solar Designer. NonExecutable User Stack[EB/OL].http://www.openwall.com/linux/,1999.
[13]Casper Dik. NonExecutable Stack for Solaris[EB/OL].http://posted to comp.security.unix,19970102.
作者簡介:
李肖堅(1961),男,副教授,博士研究生,主要研究方向為信息對抗、計算機網絡與信息安全;
鐘達夫(1972),男,工程師,碩士研究生,主要研究方向為計算機網絡與信息安全;
夏冰(1981),男,碩士研究生,主要研究方向為計算機網絡與信息安全;
唐懿芳(1976),女,講師,碩士,主要研究方向為數據挖掘、信息安全。
注:本文中所涉及到的圖表、注解、公式等內容請以PDF格式閱讀原文