緩沖區(qū)溢出:十年來攻擊和防衛(wèi)的弱點(diǎn)
更新時(shí)間:2007年01月16日 00:00:00 作者:
摘要: 在過去的十年中,以緩沖區(qū)溢出為類型的安全漏洞占是最為 常見的一種形式了。更為嚴(yán)重的是,緩沖區(qū)溢出漏洞占了遠(yuǎn)程網(wǎng) 絡(luò)攻擊的絕大多數(shù),這種攻擊可以使得一個(gè)匿名的Internet用戶 有機(jī)會(huì)獲得一臺(tái)主機(jī)的部分或全部的控制權(quán)!如果能有效地消除 緩沖區(qū)溢出的漏洞,則很大一部分的安全威脅可以得到緩解。在 本文中,我們研究了各種類型的緩沖區(qū)溢出漏洞和攻擊手段,同 時(shí)我們也研究了各種的防御手段,這些手段用來消除這些漏洞所 造成的影響,其中包括我們自己的堆棧保護(hù)方法。然后我們要考 慮如何在保證現(xiàn)有系統(tǒng)功能和性能不變的情況下,如何使用這些 方法來消除這些安全漏洞。
一、前言 在過去的十年中,以緩沖區(qū)溢出為類型的安全漏洞占是最為 常見的一種形式了。更為嚴(yán)重的是,緩沖區(qū)溢出漏洞占了遠(yuǎn)程網(wǎng) 絡(luò)攻擊的絕大多數(shù),這種攻擊可以使得一個(gè)匿名的Internet用戶 有機(jī)會(huì)獲得一臺(tái)主機(jī)的部分或全部的控制權(quán)!由于這類攻擊使任 何人都有可能取得主機(jī)的控制權(quán),所以它代表了一類極其嚴(yán)重的 安全威脅。 緩沖區(qū)溢出攻擊之所以成為一種常見安全攻擊手段其原因在 于緩沖區(qū)溢出漏洞太普通了,并且易于實(shí)現(xiàn)。而且,緩沖區(qū)溢出 成為遠(yuǎn)程攻擊的主要手段其原因在于緩沖區(qū)溢出漏洞給予了攻擊 者他所想要的一切:殖入并且執(zhí)行攻擊代碼。被殖入的攻擊代碼 以一定的權(quán)限運(yùn)行有緩沖區(qū)溢出漏洞的程序,從而得到被攻擊主 機(jī)的控制權(quán)。 比如,在1998年Lincoln實(shí)驗(yàn)室用來評(píng)估入侵檢測(cè)的的5種遠(yuǎn) 程攻擊中,有3種是基于社會(huì)工程學(xué)的信任關(guān)系,2種是緩沖區(qū)溢 出。而在1998年CERT的13份建議中,有9份是是與緩沖區(qū)溢出有 關(guān)的,在1999年,至少有半數(shù)的建議是和緩沖區(qū)溢出有關(guān)的。在 Bugtraq的調(diào)查中,有2/3的被調(diào)查者認(rèn)為緩沖區(qū)溢出漏洞是一個(gè) 很嚴(yán)重的安全問題。 緩沖區(qū)溢出漏洞和攻擊有很多種形式,我們會(huì)在第二部分對(duì) 他們進(jìn)行描述和分類。相應(yīng)地防衛(wèi)手段也隨者攻擊方法的不同而 不同,我們會(huì)放在第三部分描述,它的內(nèi)容包括針對(duì)每種攻擊類 型的有效的防衛(wèi)手段。我們還要要介紹堆棧保護(hù)方法,這種方法 在解決緩沖區(qū)溢出的漏洞方面很有效果,并且沒有犧牲系統(tǒng)的兼 容性和性能。在第四部分,我們要討論各種防衛(wèi)方法的綜合使 用。最后在第五部分是我們的結(jié)論。
二、緩沖區(qū)溢出的漏洞和攻擊 緩沖區(qū)溢出攻擊的目的在于擾亂具有某些特權(quán)運(yùn)行的程序的 功能,這樣可以使得攻擊者取得程序的控制權(quán),如果該程序具有 足夠的權(quán)限,那么整個(gè)主機(jī)就被控制了。一般而言,攻擊者攻擊 root程序,然后執(zhí)行類似“exec(sh)”的執(zhí)行代碼來獲得root的 shell,但不一直是這樣的。為了達(dá)到這個(gè)目的,攻擊者必須達(dá) 到如下的兩個(gè)目標(biāo):
1. 在程序的地址空間里安排適當(dāng)?shù)拇a。
2. 通過適當(dāng)?shù)爻跏蓟拇嫫骱痛鎯?chǔ)器,讓程序跳轉(zhuǎn)到我們安排 的地址空間執(zhí)行。 我們根據(jù)這兩個(gè)目標(biāo)來對(duì)緩沖區(qū)溢出攻擊進(jìn)行分類。在2.1 部分,我們將描述攻擊代碼是如何放入被攻擊程序的地址空間的 (這個(gè)就是“緩沖區(qū)”名字的的由來)。在2.2部分,我們介紹 攻擊者如何使一個(gè)程序的緩沖區(qū)溢出,并且執(zhí)行轉(zhuǎn)移到攻擊代碼 (這個(gè)就是“溢出”的由來)。在2.3部分,我們介紹綜合在2.1 和2.2部分所討論的代碼安排和控制程序執(zhí)行流程的技術(shù)。 2.1 在程序的地址空間里安排適當(dāng)?shù)拇a的方法 有兩種在被攻擊程序地址空間里安排攻擊代碼的方法: 殖入法: 攻擊者向被攻擊的程序輸入一個(gè)字符串,程序會(huì)把這個(gè)字符 串放到緩沖區(qū)里。這個(gè)字符串包含的數(shù)據(jù)是可以在這個(gè)被攻擊的 硬件平臺(tái)上運(yùn)行的指令序列。在這里攻擊者用被攻擊程序的緩沖 區(qū)來存放攻擊代碼。具體的方式有以下兩種差別: 1. 攻擊者不必為達(dá)到此目的而溢出任何緩沖區(qū),可以找到足夠 的空間來放置攻擊代碼 2. 緩沖區(qū)可以設(shè)在任何地方:堆棧(自動(dòng)變量)、堆(動(dòng)態(tài)分 配的)和靜態(tài)數(shù)據(jù)區(qū)(初始化或者未初始化的數(shù)據(jù)) 利用已經(jīng)存在的代碼: 有時(shí)候,攻擊者想要的代碼已經(jīng)在被攻擊的程序中了,攻擊 者所要做的只是對(duì)代碼傳遞一些參數(shù),然后使程序跳轉(zhuǎn)到我們的 目標(biāo)。比如,攻擊代碼要求執(zhí)行“exec("/bin/sh")”,而在 libc庫中的代碼執(zhí)行“exec(arg)”,其中arg使一個(gè)指向一個(gè)字 符串的指針參數(shù),那么攻擊者只要把傳入的參數(shù)指針改向指向 "/bin/sh",然后調(diào)轉(zhuǎn)到libc庫中的相應(yīng)的指令序列。 2.2 控制程序轉(zhuǎn)移到攻擊代碼的方法 所有的這些方法都是在尋求改變程序的執(zhí)行流程,使之跳轉(zhuǎn) 到攻擊代碼。最基本的就是溢出一個(gè)沒有邊界檢查或者其他弱點(diǎn) 的緩沖區(qū),這樣就擾亂了程序的正常的執(zhí)行順序。通過溢出一個(gè) 緩沖區(qū),攻擊者可以用近乎暴力的方法改寫相鄰的程序空間而直 接跳過了系統(tǒng)的檢查。 這里分類的基準(zhǔn)是攻擊者所尋求的緩沖區(qū)溢出的程序空間類 型。原則上是可以任意的空間。比如,最初的Morris Worm使用 了fingerd程序的緩沖區(qū)溢出,擾亂fingerd要執(zhí)行的文件的名 字。實(shí)際上,許多的緩沖區(qū)溢出是用暴力的方法來尋求改變程序 指針的。這類程序的不同的地方就是程序空間的突破和內(nèi)存空間 的定位不同。 (圖1) 激活紀(jì)錄(Activation Records): 每當(dāng)一個(gè)函數(shù)調(diào)用發(fā)生時(shí),調(diào)用者會(huì)在堆棧中留下一個(gè)激活 紀(jì)錄,它包含了函數(shù)結(jié)束時(shí) 返回的地址。攻擊者通過溢出這些自動(dòng)變量,使這個(gè)返回地址指 向攻擊代碼,如圖1所示。通過改變程序的返回地址,當(dāng)函數(shù)調(diào) 用結(jié)束時(shí),程序就跳轉(zhuǎn)到攻擊者設(shè)定的地址,而不是原先的地 址。這類的緩沖區(qū)溢出被稱為“stack smashing attack”,使 目前常用的緩沖區(qū)溢出攻擊方式。 函數(shù)指針(Function Pointers): “void (* foo)()”聲明了一個(gè)返回值為void函數(shù)指針的變 量foo。函數(shù)指針可以用來定 位任何地址空間,所以攻擊者只需在任何空間內(nèi)的函數(shù)指針附近 找到一個(gè)能夠溢出的緩沖區(qū),然后溢出這個(gè)緩沖區(qū)來改變函數(shù)指 針。
在某一時(shí)刻,當(dāng)程序通過函數(shù)指針調(diào)用函數(shù)時(shí),程序的流程 就按攻擊者的意圖實(shí)現(xiàn)了!它的一個(gè)攻擊范例就是在Linux系統(tǒng) 下的superprobe程序。 長(zhǎng)跳轉(zhuǎn)緩沖區(qū)(Longjmp buffers): 在C語言中包含了一個(gè)簡(jiǎn)單的檢驗(yàn)/恢復(fù)系統(tǒng),稱為 setjmp/longjmp。意思是在檢驗(yàn)點(diǎn)設(shè) 定“setjmp(buffer)”,用“l(fā)ongjmp(buffer)”來恢復(fù)檢驗(yàn) 點(diǎn)。然而,如果攻擊者能夠進(jìn)入緩沖區(qū)的空間,那么 “l(fā)ongjmp(buffer)”實(shí)際上是跳轉(zhuǎn)到攻擊者的代碼。象函數(shù)指 針一樣,longjmp緩沖區(qū)能夠指向任何地方,所以攻擊者所要做 的就是找到一個(gè)可供溢出的緩沖區(qū)。一個(gè)典型的例子就是Perl 5.003,攻擊者首先進(jìn)入用來恢復(fù)緩沖區(qū)溢出的的longjmp緩沖 區(qū),然后誘導(dǎo)進(jìn)入恢復(fù)模式,這樣就使Perl的解釋器跳轉(zhuǎn)到攻擊 代碼上了! 2.3 綜合代碼殖入和流程控制技術(shù) 現(xiàn)在我們研究綜合代碼殖入和流程控制的技術(shù)。 最簡(jiǎn)單和常見的緩沖區(qū)溢出攻擊類型就是在一個(gè)字符串里 綜合了代碼殖入和激活紀(jì)錄。攻擊者定位一個(gè)可供溢出的自動(dòng)變量, 然后向程序傳遞一個(gè)很大的字符串,在引發(fā)緩沖區(qū)溢出改變 激活紀(jì)錄的同時(shí)殖入了代碼。這個(gè)是由Levy指出的攻擊的模板。 因?yàn)镃在習(xí)慣上只為用戶和參數(shù)開辟很小的緩沖區(qū),因此這種漏 洞攻擊的實(shí)例不在少數(shù)。 代碼殖入和緩沖區(qū)溢出不一定要在在一次動(dòng)作內(nèi)完成。攻擊 者可以在一個(gè)緩沖區(qū)內(nèi)放置代碼,這是不能溢出緩沖區(qū)。然后, 攻擊者通過溢出另外一個(gè)緩沖區(qū)來轉(zhuǎn)移程序的指針。這種方法一 般用來解決可供溢出的緩沖區(qū)不夠大(不能放下全部的代碼)的 情況。 如果攻擊者試圖使用已經(jīng)常駐的代碼而不是從外部殖入代 碼,他們通常有必須把代碼作為參數(shù)化。舉例來說,在libc(幾 乎所有的C程序都要它來連接)中的部分代碼段會(huì)執(zhí)行 “exec(something)”,其中somthing就是參數(shù)。攻擊者然后使 用緩沖區(qū)溢出改變程序的參數(shù),然后利用另一個(gè)緩沖區(qū)溢出使程 序指針指向libc中的特定的代碼段。 3. 緩沖區(qū)溢出的保護(hù)方法 目前有四種基本的方法保護(hù)緩沖區(qū)免受緩沖區(qū)溢出的攻擊和 影響。在3.1中介紹了強(qiáng)制寫正確的代碼的方法。
在3.2中介紹了 通過操作系統(tǒng)使得緩沖區(qū)不可執(zhí)行,從而阻止攻擊者殖入攻擊代 碼。這種方法有效地阻止了很多緩沖區(qū)溢出的攻擊,但是攻擊者 并不一定要殖入攻擊代碼來實(shí)現(xiàn)緩沖區(qū)溢出的攻擊(參見2.1 節(jié)),所以這種方法還是存在很弱點(diǎn)的。在3.3中,我們介紹了 利用編譯器的邊界檢查來實(shí)現(xiàn)緩沖區(qū)的保護(hù)。這個(gè)方法使得緩沖 區(qū)溢出不可能出現(xiàn),從而完全消除了緩沖區(qū)溢出的威脅,但是相 對(duì)而言代價(jià)比較大。在3.4中我們介紹一種間接的方法,這個(gè)方 法在程序指針失效前進(jìn)行完整性檢查。這樣雖然這種方法不能使 得所有的緩沖區(qū)溢出失效,但它的的確確阻止了絕大多數(shù)的緩沖 區(qū)溢出攻擊,而能夠逃脫這種方法保護(hù)的緩沖區(qū)溢出也很難實(shí) 現(xiàn)。然后在3.5,我們要分析這種保護(hù)方法的兼容性和性能優(yōu)勢(shì) (與數(shù)組邊界檢查)。 3.1 編寫正確的代碼 編寫正確的代碼是一件非常有意義但耗時(shí)的工作,特別象編 寫C語言那種具有容易出錯(cuò)傾向的程序(如:字符串的零結(jié) 尾),這種風(fēng)格是由于追求性能而忽視正確性的傳統(tǒng)引起的。盡 管花了很長(zhǎng)的時(shí)間使得人們知道了如何編寫安全的程序,具有安 全漏洞的程序依舊出現(xiàn)。因此人們開發(fā)了一些工具和技術(shù)來幫助 經(jīng)驗(yàn)不足的程序員編寫安全正確的程序。 最簡(jiǎn)單的方法就是用grep來搜索源代碼中容易產(chǎn)生漏洞的庫 的調(diào)用,比如對(duì)strcpy和sprintf的調(diào)用,這兩個(gè)函數(shù)都沒有檢 查輸入?yún)?shù)的長(zhǎng)度。事實(shí)上,各個(gè)版本C的標(biāo)準(zhǔn)庫均有這樣的問 題存在。 為了尋找一些常見的諸如緩沖區(qū)溢出和操作系統(tǒng)競(jìng)爭(zhēng)條件等 漏洞,代碼檢查小組檢查了很多的代碼。然而依然有漏網(wǎng)之魚存 在。盡管采用了strncpy和snprintf這些替代函數(shù)來防止緩沖區(qū) 溢出的發(fā)生,但是由于編寫代碼的問題,仍舊會(huì)有這種情況發(fā) 生。比如lprm程序就是最好的例子,雖然它通過了代碼的安全 檢查,但仍然有緩沖區(qū)溢出的問題存在。 為了對(duì)付這些問題,人們開發(fā)了一些高級(jí)的查錯(cuò)工具,如 fault injection等。這些工具的目的在于通過人為隨機(jī)地產(chǎn)生 一些緩沖區(qū)溢出來尋找代碼的安全漏洞。還有一些靜態(tài)分析工具 用于偵測(cè)緩沖區(qū)溢出的存在。 雖然這些工具幫助程序員開發(fā)更安全的程序,但是由于C語 言的特點(diǎn),這些工具不可能找出所有的緩沖區(qū)溢出漏洞。所以, 偵錯(cuò)技術(shù)只能用來減少緩沖區(qū)溢出的可能,并不能完全地消除它 的存在。除非程序員能保證他的程序萬無一失,否則還是要用到 以下3.2到3.4部分的內(nèi)容來保證程序的可靠性能。
3.2 非執(zhí)行的緩沖區(qū) 通過使被攻擊程序的數(shù)據(jù)段地址空間不可執(zhí)行,從而使得攻 擊者不可能執(zhí)行被殖入被攻擊程序輸入緩沖區(qū)的代碼,這種技術(shù) 被稱為非執(zhí)行的緩沖區(qū)技術(shù)。事實(shí)上,很多老的Unix系統(tǒng)都是這 樣設(shè)計(jì)的,但是近來的Unix和MS Windows系統(tǒng)由于實(shí)現(xiàn)更好的性 能和功能,往往在在數(shù)據(jù)段中動(dòng)態(tài)地放入可執(zhí)行的代碼。所以為 了保持程序的兼容性不可能使得所有程序的數(shù)據(jù)段不可執(zhí)行。 但是我們可以設(shè)定堆棧數(shù)據(jù)段不可執(zhí)行,這樣就可以最大限 度地保證了程序的兼容性。Linux和Solaris都發(fā)布了有關(guān)這方面 的內(nèi)核補(bǔ)丁。因?yàn)閹缀鯖]有任何合法的程序會(huì)在堆棧中存放代 碼,這種做法幾乎不產(chǎn)生任何兼容性問題,除了在Linux中的兩 個(gè)特例,這時(shí)可執(zhí)行的代碼必須被放入堆棧中: 信號(hào)傳遞: Linux通過向進(jìn)程堆棧釋放代碼然后引發(fā)中斷來執(zhí)行在堆棧 中的代碼來實(shí)現(xiàn)向進(jìn)程發(fā)送Unix信號(hào)。非執(zhí)行緩沖區(qū)的補(bǔ)丁在發(fā) 送信號(hào)的時(shí)候是允許緩沖區(qū)可執(zhí)行的。 GCC的在線重用: 研究發(fā)現(xiàn)gcc在堆棧區(qū)里放置了可執(zhí)行的代碼作為在線重用 之用。然而,關(guān)閉這個(gè)功能并不產(chǎn)生任何問題,只有部分功能似 乎不能使用。 非執(zhí)行堆棧的保護(hù)可以有效地對(duì)付把代碼殖入自動(dòng)變量的緩 沖區(qū)溢出攻擊,而對(duì)于其他形式的攻擊則沒有效果(參見2.1)。 通過引用一個(gè)駐留的程序的指針,就可以跳過這種保護(hù)措施。其 他的攻擊可以采用把代碼殖入堆或者靜態(tài)數(shù)據(jù)段中來跳過保護(hù)。
3.3 數(shù)組邊界檢查 殖入代碼引起緩沖區(qū)溢出是一個(gè)方面,擾亂程序的執(zhí)行流程 是另一個(gè)方面。不象非執(zhí)行緩沖區(qū)保護(hù),數(shù)組邊界檢查完全放置 了緩沖區(qū)溢出的產(chǎn)生和攻擊。這樣,只要數(shù)組不能被溢出,溢出 攻擊也就無從談起。為了實(shí)現(xiàn)數(shù)組邊界檢查,則所有的對(duì)數(shù)組的 讀寫操作都應(yīng)當(dāng)被檢查以確保對(duì)數(shù)組的操作在正確的范圍內(nèi)。最 直接的方法是檢查所有的數(shù)組操作,但是通??梢圆捎靡恍﹥?yōu)化 的技術(shù)來減少檢查的次數(shù)。目前有以下的幾種檢查方法: 3.3.1 Compaq C 編譯器 Compaq公司為Alpha CPU開發(fā)的C編譯器(在Tru64的Unix平 臺(tái)上是cc,在Alpha Linux平臺(tái)上是ccc)支持有限度的邊界檢查 (使用-check_bounds參數(shù))。這些限制是: ·只有顯示的數(shù)組引用才被檢查,比如“a[3]”會(huì)被檢查,而 “*(a+3)”則不會(huì)。 ·由于所有的C數(shù)組在傳送的時(shí)候是指針傳遞的,所以傳遞給函 數(shù)的的數(shù)組不會(huì)被檢查。 ·帶有危險(xiǎn)性的庫函數(shù)如strcpy不會(huì)在編譯的時(shí)候進(jìn)行邊界檢 查,即便是指定了邊界檢查。 由于在C語言中利用指針進(jìn)行數(shù)組操作和傳遞是如此的頻 繁,因此這種局限性是非常嚴(yán)重的。通常這種邊界檢查用來程序 的查錯(cuò),而且不能保證不發(fā)生緩沖區(qū)溢出的漏洞。
一、前言 在過去的十年中,以緩沖區(qū)溢出為類型的安全漏洞占是最為 常見的一種形式了。更為嚴(yán)重的是,緩沖區(qū)溢出漏洞占了遠(yuǎn)程網(wǎng) 絡(luò)攻擊的絕大多數(shù),這種攻擊可以使得一個(gè)匿名的Internet用戶 有機(jī)會(huì)獲得一臺(tái)主機(jī)的部分或全部的控制權(quán)!由于這類攻擊使任 何人都有可能取得主機(jī)的控制權(quán),所以它代表了一類極其嚴(yán)重的 安全威脅。 緩沖區(qū)溢出攻擊之所以成為一種常見安全攻擊手段其原因在 于緩沖區(qū)溢出漏洞太普通了,并且易于實(shí)現(xiàn)。而且,緩沖區(qū)溢出 成為遠(yuǎn)程攻擊的主要手段其原因在于緩沖區(qū)溢出漏洞給予了攻擊 者他所想要的一切:殖入并且執(zhí)行攻擊代碼。被殖入的攻擊代碼 以一定的權(quán)限運(yùn)行有緩沖區(qū)溢出漏洞的程序,從而得到被攻擊主 機(jī)的控制權(quán)。 比如,在1998年Lincoln實(shí)驗(yàn)室用來評(píng)估入侵檢測(cè)的的5種遠(yuǎn) 程攻擊中,有3種是基于社會(huì)工程學(xué)的信任關(guān)系,2種是緩沖區(qū)溢 出。而在1998年CERT的13份建議中,有9份是是與緩沖區(qū)溢出有 關(guān)的,在1999年,至少有半數(shù)的建議是和緩沖區(qū)溢出有關(guān)的。在 Bugtraq的調(diào)查中,有2/3的被調(diào)查者認(rèn)為緩沖區(qū)溢出漏洞是一個(gè) 很嚴(yán)重的安全問題。 緩沖區(qū)溢出漏洞和攻擊有很多種形式,我們會(huì)在第二部分對(duì) 他們進(jìn)行描述和分類。相應(yīng)地防衛(wèi)手段也隨者攻擊方法的不同而 不同,我們會(huì)放在第三部分描述,它的內(nèi)容包括針對(duì)每種攻擊類 型的有效的防衛(wèi)手段。我們還要要介紹堆棧保護(hù)方法,這種方法 在解決緩沖區(qū)溢出的漏洞方面很有效果,并且沒有犧牲系統(tǒng)的兼 容性和性能。在第四部分,我們要討論各種防衛(wèi)方法的綜合使 用。最后在第五部分是我們的結(jié)論。
二、緩沖區(qū)溢出的漏洞和攻擊 緩沖區(qū)溢出攻擊的目的在于擾亂具有某些特權(quán)運(yùn)行的程序的 功能,這樣可以使得攻擊者取得程序的控制權(quán),如果該程序具有 足夠的權(quán)限,那么整個(gè)主機(jī)就被控制了。一般而言,攻擊者攻擊 root程序,然后執(zhí)行類似“exec(sh)”的執(zhí)行代碼來獲得root的 shell,但不一直是這樣的。為了達(dá)到這個(gè)目的,攻擊者必須達(dá) 到如下的兩個(gè)目標(biāo):
1. 在程序的地址空間里安排適當(dāng)?shù)拇a。
2. 通過適當(dāng)?shù)爻跏蓟拇嫫骱痛鎯?chǔ)器,讓程序跳轉(zhuǎn)到我們安排 的地址空間執(zhí)行。 我們根據(jù)這兩個(gè)目標(biāo)來對(duì)緩沖區(qū)溢出攻擊進(jìn)行分類。在2.1 部分,我們將描述攻擊代碼是如何放入被攻擊程序的地址空間的 (這個(gè)就是“緩沖區(qū)”名字的的由來)。在2.2部分,我們介紹 攻擊者如何使一個(gè)程序的緩沖區(qū)溢出,并且執(zhí)行轉(zhuǎn)移到攻擊代碼 (這個(gè)就是“溢出”的由來)。在2.3部分,我們介紹綜合在2.1 和2.2部分所討論的代碼安排和控制程序執(zhí)行流程的技術(shù)。 2.1 在程序的地址空間里安排適當(dāng)?shù)拇a的方法 有兩種在被攻擊程序地址空間里安排攻擊代碼的方法: 殖入法: 攻擊者向被攻擊的程序輸入一個(gè)字符串,程序會(huì)把這個(gè)字符 串放到緩沖區(qū)里。這個(gè)字符串包含的數(shù)據(jù)是可以在這個(gè)被攻擊的 硬件平臺(tái)上運(yùn)行的指令序列。在這里攻擊者用被攻擊程序的緩沖 區(qū)來存放攻擊代碼。具體的方式有以下兩種差別: 1. 攻擊者不必為達(dá)到此目的而溢出任何緩沖區(qū),可以找到足夠 的空間來放置攻擊代碼 2. 緩沖區(qū)可以設(shè)在任何地方:堆棧(自動(dòng)變量)、堆(動(dòng)態(tài)分 配的)和靜態(tài)數(shù)據(jù)區(qū)(初始化或者未初始化的數(shù)據(jù)) 利用已經(jīng)存在的代碼: 有時(shí)候,攻擊者想要的代碼已經(jīng)在被攻擊的程序中了,攻擊 者所要做的只是對(duì)代碼傳遞一些參數(shù),然后使程序跳轉(zhuǎn)到我們的 目標(biāo)。比如,攻擊代碼要求執(zhí)行“exec("/bin/sh")”,而在 libc庫中的代碼執(zhí)行“exec(arg)”,其中arg使一個(gè)指向一個(gè)字 符串的指針參數(shù),那么攻擊者只要把傳入的參數(shù)指針改向指向 "/bin/sh",然后調(diào)轉(zhuǎn)到libc庫中的相應(yīng)的指令序列。 2.2 控制程序轉(zhuǎn)移到攻擊代碼的方法 所有的這些方法都是在尋求改變程序的執(zhí)行流程,使之跳轉(zhuǎn) 到攻擊代碼。最基本的就是溢出一個(gè)沒有邊界檢查或者其他弱點(diǎn) 的緩沖區(qū),這樣就擾亂了程序的正常的執(zhí)行順序。通過溢出一個(gè) 緩沖區(qū),攻擊者可以用近乎暴力的方法改寫相鄰的程序空間而直 接跳過了系統(tǒng)的檢查。 這里分類的基準(zhǔn)是攻擊者所尋求的緩沖區(qū)溢出的程序空間類 型。原則上是可以任意的空間。比如,最初的Morris Worm使用 了fingerd程序的緩沖區(qū)溢出,擾亂fingerd要執(zhí)行的文件的名 字。實(shí)際上,許多的緩沖區(qū)溢出是用暴力的方法來尋求改變程序 指針的。這類程序的不同的地方就是程序空間的突破和內(nèi)存空間 的定位不同。 (圖1) 激活紀(jì)錄(Activation Records): 每當(dāng)一個(gè)函數(shù)調(diào)用發(fā)生時(shí),調(diào)用者會(huì)在堆棧中留下一個(gè)激活 紀(jì)錄,它包含了函數(shù)結(jié)束時(shí) 返回的地址。攻擊者通過溢出這些自動(dòng)變量,使這個(gè)返回地址指 向攻擊代碼,如圖1所示。通過改變程序的返回地址,當(dāng)函數(shù)調(diào) 用結(jié)束時(shí),程序就跳轉(zhuǎn)到攻擊者設(shè)定的地址,而不是原先的地 址。這類的緩沖區(qū)溢出被稱為“stack smashing attack”,使 目前常用的緩沖區(qū)溢出攻擊方式。 函數(shù)指針(Function Pointers): “void (* foo)()”聲明了一個(gè)返回值為void函數(shù)指針的變 量foo。函數(shù)指針可以用來定 位任何地址空間,所以攻擊者只需在任何空間內(nèi)的函數(shù)指針附近 找到一個(gè)能夠溢出的緩沖區(qū),然后溢出這個(gè)緩沖區(qū)來改變函數(shù)指 針。
在某一時(shí)刻,當(dāng)程序通過函數(shù)指針調(diào)用函數(shù)時(shí),程序的流程 就按攻擊者的意圖實(shí)現(xiàn)了!它的一個(gè)攻擊范例就是在Linux系統(tǒng) 下的superprobe程序。 長(zhǎng)跳轉(zhuǎn)緩沖區(qū)(Longjmp buffers): 在C語言中包含了一個(gè)簡(jiǎn)單的檢驗(yàn)/恢復(fù)系統(tǒng),稱為 setjmp/longjmp。意思是在檢驗(yàn)點(diǎn)設(shè) 定“setjmp(buffer)”,用“l(fā)ongjmp(buffer)”來恢復(fù)檢驗(yàn) 點(diǎn)。然而,如果攻擊者能夠進(jìn)入緩沖區(qū)的空間,那么 “l(fā)ongjmp(buffer)”實(shí)際上是跳轉(zhuǎn)到攻擊者的代碼。象函數(shù)指 針一樣,longjmp緩沖區(qū)能夠指向任何地方,所以攻擊者所要做 的就是找到一個(gè)可供溢出的緩沖區(qū)。一個(gè)典型的例子就是Perl 5.003,攻擊者首先進(jìn)入用來恢復(fù)緩沖區(qū)溢出的的longjmp緩沖 區(qū),然后誘導(dǎo)進(jìn)入恢復(fù)模式,這樣就使Perl的解釋器跳轉(zhuǎn)到攻擊 代碼上了! 2.3 綜合代碼殖入和流程控制技術(shù) 現(xiàn)在我們研究綜合代碼殖入和流程控制的技術(shù)。 最簡(jiǎn)單和常見的緩沖區(qū)溢出攻擊類型就是在一個(gè)字符串里 綜合了代碼殖入和激活紀(jì)錄。攻擊者定位一個(gè)可供溢出的自動(dòng)變量, 然后向程序傳遞一個(gè)很大的字符串,在引發(fā)緩沖區(qū)溢出改變 激活紀(jì)錄的同時(shí)殖入了代碼。這個(gè)是由Levy指出的攻擊的模板。 因?yàn)镃在習(xí)慣上只為用戶和參數(shù)開辟很小的緩沖區(qū),因此這種漏 洞攻擊的實(shí)例不在少數(shù)。 代碼殖入和緩沖區(qū)溢出不一定要在在一次動(dòng)作內(nèi)完成。攻擊 者可以在一個(gè)緩沖區(qū)內(nèi)放置代碼,這是不能溢出緩沖區(qū)。然后, 攻擊者通過溢出另外一個(gè)緩沖區(qū)來轉(zhuǎn)移程序的指針。這種方法一 般用來解決可供溢出的緩沖區(qū)不夠大(不能放下全部的代碼)的 情況。 如果攻擊者試圖使用已經(jīng)常駐的代碼而不是從外部殖入代 碼,他們通常有必須把代碼作為參數(shù)化。舉例來說,在libc(幾 乎所有的C程序都要它來連接)中的部分代碼段會(huì)執(zhí)行 “exec(something)”,其中somthing就是參數(shù)。攻擊者然后使 用緩沖區(qū)溢出改變程序的參數(shù),然后利用另一個(gè)緩沖區(qū)溢出使程 序指針指向libc中的特定的代碼段。 3. 緩沖區(qū)溢出的保護(hù)方法 目前有四種基本的方法保護(hù)緩沖區(qū)免受緩沖區(qū)溢出的攻擊和 影響。在3.1中介紹了強(qiáng)制寫正確的代碼的方法。
在3.2中介紹了 通過操作系統(tǒng)使得緩沖區(qū)不可執(zhí)行,從而阻止攻擊者殖入攻擊代 碼。這種方法有效地阻止了很多緩沖區(qū)溢出的攻擊,但是攻擊者 并不一定要殖入攻擊代碼來實(shí)現(xiàn)緩沖區(qū)溢出的攻擊(參見2.1 節(jié)),所以這種方法還是存在很弱點(diǎn)的。在3.3中,我們介紹了 利用編譯器的邊界檢查來實(shí)現(xiàn)緩沖區(qū)的保護(hù)。這個(gè)方法使得緩沖 區(qū)溢出不可能出現(xiàn),從而完全消除了緩沖區(qū)溢出的威脅,但是相 對(duì)而言代價(jià)比較大。在3.4中我們介紹一種間接的方法,這個(gè)方 法在程序指針失效前進(jìn)行完整性檢查。這樣雖然這種方法不能使 得所有的緩沖區(qū)溢出失效,但它的的確確阻止了絕大多數(shù)的緩沖 區(qū)溢出攻擊,而能夠逃脫這種方法保護(hù)的緩沖區(qū)溢出也很難實(shí) 現(xiàn)。然后在3.5,我們要分析這種保護(hù)方法的兼容性和性能優(yōu)勢(shì) (與數(shù)組邊界檢查)。 3.1 編寫正確的代碼 編寫正確的代碼是一件非常有意義但耗時(shí)的工作,特別象編 寫C語言那種具有容易出錯(cuò)傾向的程序(如:字符串的零結(jié) 尾),這種風(fēng)格是由于追求性能而忽視正確性的傳統(tǒng)引起的。盡 管花了很長(zhǎng)的時(shí)間使得人們知道了如何編寫安全的程序,具有安 全漏洞的程序依舊出現(xiàn)。因此人們開發(fā)了一些工具和技術(shù)來幫助 經(jīng)驗(yàn)不足的程序員編寫安全正確的程序。 最簡(jiǎn)單的方法就是用grep來搜索源代碼中容易產(chǎn)生漏洞的庫 的調(diào)用,比如對(duì)strcpy和sprintf的調(diào)用,這兩個(gè)函數(shù)都沒有檢 查輸入?yún)?shù)的長(zhǎng)度。事實(shí)上,各個(gè)版本C的標(biāo)準(zhǔn)庫均有這樣的問 題存在。 為了尋找一些常見的諸如緩沖區(qū)溢出和操作系統(tǒng)競(jìng)爭(zhēng)條件等 漏洞,代碼檢查小組檢查了很多的代碼。然而依然有漏網(wǎng)之魚存 在。盡管采用了strncpy和snprintf這些替代函數(shù)來防止緩沖區(qū) 溢出的發(fā)生,但是由于編寫代碼的問題,仍舊會(huì)有這種情況發(fā) 生。比如lprm程序就是最好的例子,雖然它通過了代碼的安全 檢查,但仍然有緩沖區(qū)溢出的問題存在。 為了對(duì)付這些問題,人們開發(fā)了一些高級(jí)的查錯(cuò)工具,如 fault injection等。這些工具的目的在于通過人為隨機(jī)地產(chǎn)生 一些緩沖區(qū)溢出來尋找代碼的安全漏洞。還有一些靜態(tài)分析工具 用于偵測(cè)緩沖區(qū)溢出的存在。 雖然這些工具幫助程序員開發(fā)更安全的程序,但是由于C語 言的特點(diǎn),這些工具不可能找出所有的緩沖區(qū)溢出漏洞。所以, 偵錯(cuò)技術(shù)只能用來減少緩沖區(qū)溢出的可能,并不能完全地消除它 的存在。除非程序員能保證他的程序萬無一失,否則還是要用到 以下3.2到3.4部分的內(nèi)容來保證程序的可靠性能。
3.2 非執(zhí)行的緩沖區(qū) 通過使被攻擊程序的數(shù)據(jù)段地址空間不可執(zhí)行,從而使得攻 擊者不可能執(zhí)行被殖入被攻擊程序輸入緩沖區(qū)的代碼,這種技術(shù) 被稱為非執(zhí)行的緩沖區(qū)技術(shù)。事實(shí)上,很多老的Unix系統(tǒng)都是這 樣設(shè)計(jì)的,但是近來的Unix和MS Windows系統(tǒng)由于實(shí)現(xiàn)更好的性 能和功能,往往在在數(shù)據(jù)段中動(dòng)態(tài)地放入可執(zhí)行的代碼。所以為 了保持程序的兼容性不可能使得所有程序的數(shù)據(jù)段不可執(zhí)行。 但是我們可以設(shè)定堆棧數(shù)據(jù)段不可執(zhí)行,這樣就可以最大限 度地保證了程序的兼容性。Linux和Solaris都發(fā)布了有關(guān)這方面 的內(nèi)核補(bǔ)丁。因?yàn)閹缀鯖]有任何合法的程序會(huì)在堆棧中存放代 碼,這種做法幾乎不產(chǎn)生任何兼容性問題,除了在Linux中的兩 個(gè)特例,這時(shí)可執(zhí)行的代碼必須被放入堆棧中: 信號(hào)傳遞: Linux通過向進(jìn)程堆棧釋放代碼然后引發(fā)中斷來執(zhí)行在堆棧 中的代碼來實(shí)現(xiàn)向進(jìn)程發(fā)送Unix信號(hào)。非執(zhí)行緩沖區(qū)的補(bǔ)丁在發(fā) 送信號(hào)的時(shí)候是允許緩沖區(qū)可執(zhí)行的。 GCC的在線重用: 研究發(fā)現(xiàn)gcc在堆棧區(qū)里放置了可執(zhí)行的代碼作為在線重用 之用。然而,關(guān)閉這個(gè)功能并不產(chǎn)生任何問題,只有部分功能似 乎不能使用。 非執(zhí)行堆棧的保護(hù)可以有效地對(duì)付把代碼殖入自動(dòng)變量的緩 沖區(qū)溢出攻擊,而對(duì)于其他形式的攻擊則沒有效果(參見2.1)。 通過引用一個(gè)駐留的程序的指針,就可以跳過這種保護(hù)措施。其 他的攻擊可以采用把代碼殖入堆或者靜態(tài)數(shù)據(jù)段中來跳過保護(hù)。
3.3 數(shù)組邊界檢查 殖入代碼引起緩沖區(qū)溢出是一個(gè)方面,擾亂程序的執(zhí)行流程 是另一個(gè)方面。不象非執(zhí)行緩沖區(qū)保護(hù),數(shù)組邊界檢查完全放置 了緩沖區(qū)溢出的產(chǎn)生和攻擊。這樣,只要數(shù)組不能被溢出,溢出 攻擊也就無從談起。為了實(shí)現(xiàn)數(shù)組邊界檢查,則所有的對(duì)數(shù)組的 讀寫操作都應(yīng)當(dāng)被檢查以確保對(duì)數(shù)組的操作在正確的范圍內(nèi)。最 直接的方法是檢查所有的數(shù)組操作,但是通??梢圆捎靡恍﹥?yōu)化 的技術(shù)來減少檢查的次數(shù)。目前有以下的幾種檢查方法: 3.3.1 Compaq C 編譯器 Compaq公司為Alpha CPU開發(fā)的C編譯器(在Tru64的Unix平 臺(tái)上是cc,在Alpha Linux平臺(tái)上是ccc)支持有限度的邊界檢查 (使用-check_bounds參數(shù))。這些限制是: ·只有顯示的數(shù)組引用才被檢查,比如“a[3]”會(huì)被檢查,而 “*(a+3)”則不會(huì)。 ·由于所有的C數(shù)組在傳送的時(shí)候是指針傳遞的,所以傳遞給函 數(shù)的的數(shù)組不會(huì)被檢查。 ·帶有危險(xiǎn)性的庫函數(shù)如strcpy不會(huì)在編譯的時(shí)候進(jìn)行邊界檢 查,即便是指定了邊界檢查。 由于在C語言中利用指針進(jìn)行數(shù)組操作和傳遞是如此的頻 繁,因此這種局限性是非常嚴(yán)重的。通常這種邊界檢查用來程序 的查錯(cuò),而且不能保證不發(fā)生緩沖區(qū)溢出的漏洞。
相關(guān)文章
DVBBS7.1后臺(tái)備份得到一個(gè)webshell
DVBBS7.1后臺(tái)備份得到一個(gè)webshell...2007-02-022007 10.30動(dòng)易網(wǎng)站管理系統(tǒng)vote.asp頁面存在SQL注入漏洞
2007 10.30動(dòng)易網(wǎng)站管理系統(tǒng)vote.asp頁面存在SQL注入漏洞...2007-12-12Windows 2003 Enterprise Edition IIS6 .ASP目錄執(zhí)行缺陷
Windows 2003 Enterprise Edition IIS6 .ASP目錄執(zhí)行缺陷...2007-02-02