羅斌
摘要:緩沖區(qū)溢出是一種在各種操作系統(tǒng)、應(yīng)用軟件中廣泛存在普遍且危險(xiǎn)的漏洞,利用緩沖區(qū)溢出攻擊可以導(dǎo)致程序運(yùn)行失敗、系統(tǒng)崩潰等后果。更為嚴(yán)重的是,可以利用它執(zhí)行非授權(quán)指令,甚至可以取得系統(tǒng)特權(quán),進(jìn)而進(jìn)行各種非法操作。本文分析了緩沖區(qū)溢出的基本原理及緩沖區(qū)漏洞利用的技術(shù)條件、利用途徑,最后提出了Linux和Windows下緩沖區(qū)漏洞利用的防范策略。
關(guān)鍵詞:緩沖區(qū)溢出;漏洞利用;漏洞防范
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2018)33-0044-02
1 緩沖區(qū)溢出概述
緩沖區(qū)是一個(gè)較寬泛的概念,用于存放數(shù)據(jù)的內(nèi)存空間都可以叫作緩沖區(qū)。緩沖區(qū)自身沒有數(shù)據(jù)越界的防范機(jī)制,當(dāng)放入緩沖區(qū)的數(shù)據(jù)超出了緩沖區(qū)的范圍就叫緩沖區(qū)溢出。某些可被利用的緩沖區(qū)溢出是安全缺陷,嚴(yán)重威脅著計(jì)算機(jī)安全。
每個(gè)進(jìn)程在內(nèi)存中都有一段相對(duì)獨(dú)立的存儲(chǔ)空間,并且這段存儲(chǔ)空間被分為了許多小分區(qū),我們所關(guān)注的進(jìn)程內(nèi)存分區(qū)主要有六個(gè):text、data、bss、堆、棧和Env段。其中,需要重點(diǎn)關(guān)注用于存儲(chǔ)用戶輸入和變量的緩沖區(qū)——棧和堆。棧主要被用來保存局部變量和函數(shù)調(diào)用的軌跡,在大多數(shù)操作系統(tǒng)中,棧的增長方向剛好與內(nèi)存地址的增長方向相反——棧從內(nèi)存的高地址空間向低地址空間增長,正是由于這種增長方式,導(dǎo)致了棧緩沖區(qū)溢出的存在。堆則被用來存儲(chǔ)動(dòng)態(tài)分配的變量,它的增長方向是從內(nèi)存的地址空間向高地址空間增長。
2 棧緩沖區(qū)溢出漏洞
2.1 原理
在討論棧緩沖區(qū)溢出之前先厘清兩個(gè)不同的概念[1]:棧緩沖區(qū)溢出和棧溢出,前者是文章要討論的漏洞,是一種安全缺陷,后者則是由于內(nèi)存資源不足引起,通常會(huì)導(dǎo)致程序崩潰,是一種編程錯(cuò)誤。
當(dāng)發(fā)生函數(shù)調(diào)用時(shí),計(jì)算機(jī)按如下步驟操作:參數(shù)入棧,指令寄存器eip入棧(RET),?;芳拇嫫鱡bp入棧,將棧指針esp賦值給棧基址寄存器ebp,本地變量入棧。
棧緩沖區(qū)溢出的本質(zhì)是某個(gè)棧幀(Stack Frame)發(fā)生溢出,溢出的內(nèi)容覆蓋了保存在棧內(nèi)的eip,即返回地址被改變,當(dāng)函數(shù)返回時(shí),被破壞的eip值從stack里彈出到eip寄存器中執(zhí)行,從而產(chǎn)生三種結(jié)果:一是最直接也是最“好”的結(jié)果就是程序發(fā)生段錯(cuò)誤而造成系統(tǒng)崩潰,二是eip寄存器可能被控制以執(zhí)行用戶級(jí)執(zhí)行惡意代碼,三是最壞結(jié)果eip被控制以執(zhí)行系統(tǒng)級(jí)或root級(jí)的惡意代碼。
2.2 漏洞利用
2.2.1 技術(shù)條件
一次完整的漏洞利用都需要用到以下組件:
一是NOP片段,是在有效凈荷前用NOP填充緩沖區(qū),這樣就能有效地增大了目標(biāo)區(qū)域的范圍,便于溢出點(diǎn)定位[1]。盡管NOP片段大大提高了攻擊成功率,但是在一定程度上它的成功還依賴于對(duì)棧偏移的估測(cè),如果棧偏移估測(cè)錯(cuò)誤將可能引起程序崩潰并觸發(fā)報(bào)警。此外,NOP片段需要耗費(fèi)大量的內(nèi)存來存儲(chǔ)NOP,這就有可能由于緩沖區(qū)太小而導(dǎo)致棧區(qū)不夠用。由于該技術(shù)太流行,所以很多廠商提供了通過匹配NOP模式的入侵防范系統(tǒng)來檢測(cè)shellcode。
二是Shellcode,指將達(dá)到黑客目的的機(jī)器代碼,shellcode是實(shí)實(shí)在在的二進(jìn)制機(jī)器代碼,常以16進(jìn)制的形式表示。
三是修改返回地址,在緩沖區(qū)漏洞利用中最關(guān)鍵的元素就是返回地址,攻擊者必須能夠成功地用想要返回的地址覆蓋棧緩沖區(qū)里的eip值,才能夠控制程序跳轉(zhuǎn)到其所想要執(zhí)行的代碼執(zhí)行。
2.2.2 漏洞利用途徑
一是從命令行利用棧緩沖區(qū)漏洞,直接使用一些腳本語言編寫攻擊代碼,以獲得root權(quán)限。
二是通過通用的攻擊代碼利用緩沖區(qū)漏洞,通用的攻擊代碼在網(wǎng)上或其他參考資料里都能找到,可以在許多情形下起對(duì)許多利用有效。
三是利用小緩沖區(qū),當(dāng)有缺陷的緩沖區(qū)很小不足以存儲(chǔ)相對(duì)較長的shellcode時(shí),可以將shellcode注入環(huán)境變量中,利用main函數(shù)的argv參數(shù)從環(huán)境變量中將shellcode讀入內(nèi)存。這中途徑之所以能成功是由于進(jìn)程內(nèi)存空間中的Env段是可寫的。
2.2.3 實(shí)施過程
緩沖區(qū)溢出漏洞利用大致有以下步驟:控制eip、確定偏移量、確定攻擊向量、構(gòu)建一個(gè)攻擊“三明治”(NOP片段—Shellcode—跳轉(zhuǎn)地址)、測(cè)試、根據(jù)需要調(diào)試。
3 堆緩沖區(qū)溢出漏洞
堆緩沖區(qū)溢出是指發(fā)生在堆數(shù)據(jù)區(qū)域內(nèi)的緩沖區(qū)溢出,也是可利用的,但利用方式與利用棧緩沖區(qū)溢出不一樣[2]。
程序在運(yùn)行過程中需要分配和釋放內(nèi)存時(shí),函數(shù)庫提供了malloc()、realloca()、和free()等函數(shù)。Malloc()會(huì)將brk()分配的大內(nèi)存塊分成小塊,并將合適的塊分給用戶,同樣,需要釋放時(shí),free()時(shí)會(huì)判斷新釋放的塊是否可以與其他塊合并,以減小碎片。為了提高運(yùn)行效率,用分配元數(shù)據(jù)(malloc meta data)保存塊的位置、大小、或相關(guān)的數(shù)據(jù),這些分配元數(shù)據(jù)一般以兩種方式存在:一種是保存在全局變量中,另一種是保存在分配給用戶的存儲(chǔ)塊前或后。后一種方式就為利用堆溢出改變分配元數(shù)據(jù)從而改變某個(gè)程序的函數(shù)指針[3]。
4 緩沖區(qū)溢出漏洞防范
4.1 Linux下的緩沖區(qū)溢出漏洞防范
由于緩沖區(qū)溢出和堆溢出的出現(xiàn),許多程序員設(shè)計(jì)了內(nèi)存保護(hù)方案來防范此類攻擊:
一是編譯器改進(jìn):libsafe能有效防范棧緩沖區(qū)溢出攻擊,但是對(duì)防范堆緩沖區(qū)溢出攻擊卻沒有太大作用;Stackshield取代了gcc編譯時(shí)的某些不安全選項(xiàng);StackGuard在棧緩沖區(qū)和幀狀態(tài)數(shù)據(jù)(frame state data)中加入了一個(gè)標(biāo)識(shí),一旦緩沖區(qū)溢出破壞了保存在棧里的eip,這個(gè)標(biāo)識(shí)就會(huì)被毀壞并且被檢測(cè)到;SSP在stackgaurd的基礎(chǔ)上重新分布了棧變量,從而使攻擊變得困難;基于gcc的不可運(yùn)行棧,GCC從4.1版本開始已經(jīng)實(shí)現(xiàn)了不可執(zhí)行棧,這就意味著攻擊者要在棧里運(yùn)行shellcode是不可行的。
二是內(nèi)核補(bǔ)丁和腳本:不可執(zhí)行內(nèi)存頁,這種技術(shù)的主體思想是棧和堆應(yīng)該不可執(zhí)行,用戶代碼一旦被載入內(nèi)存也應(yīng)不可寫。Page-eXec(PaX)補(bǔ)丁嘗試通過改變內(nèi)存分頁的方法對(duì)棧和堆提供執(zhí)行控制。PaX補(bǔ)丁實(shí)現(xiàn)了一組關(guān)于TLB緩存的狀態(tài)表,該狀態(tài)表記錄著某個(gè)內(nèi)存頁是處于讀/寫模式還是執(zhí)行模式。當(dāng)進(jìn)程請(qǐng)求將某個(gè)內(nèi)存頁從讀/寫模式轉(zhuǎn)換為執(zhí)行模式時(shí),PaX補(bǔ)丁進(jìn)行干預(yù),在日志里記錄并殺死發(fā)出這個(gè)請(qǐng)求的進(jìn)程,包括SEGMEXEC方式和PAGEEXEC方式。
4.2 Windows下的緩沖區(qū)溢出漏洞防范
(1)基于棧的緩沖區(qū)越界檢測(cè)(/GS),編譯器選項(xiàng)/GS是微軟實(shí)現(xiàn)的一個(gè)棧canary,一個(gè)隱藏標(biāo)識(shí)被存儲(chǔ)到以存儲(chǔ)的ebp和以存儲(chǔ)的RETN地址之上,一旦發(fā)生函數(shù)返回,就檢測(cè)隱藏標(biāo)識(shí)是否被改變。有很多方法可以繞過/GS:猜Cookie的值,這種方法僅適用于本地系統(tǒng)的攻擊;覆蓋調(diào)用函數(shù)的指針;用特定標(biāo)記替代Cookie;覆蓋SEH記錄。
(2)安全結(jié)構(gòu)化的異常處理(SafeSEH),SafeSEH的目的是在棧里使用SEH結(jié)構(gòu)體存儲(chǔ)以防止覆蓋發(fā)生。SafeSEH保護(hù)機(jī)制對(duì)于異常處理相當(dāng)有效,但是相對(duì)復(fù)雜。
(3)SEH覆蓋保護(hù)(SEHOP),SEHOP是Windows Server 2008中新增的一種保護(hù)機(jī)制。SEHOP通過 RtlDispatchException程序?qū)崿F(xiàn),遍歷異常處理鏈并且確保其能到達(dá)FinalExceptionHandler函數(shù),如果攻擊者覆蓋了某個(gè)異常處理結(jié)構(gòu),RtlDispathcException將不能正常到達(dá)FinalExceptionHandler函數(shù)。
(4)堆保護(hù):針對(duì)內(nèi)存釋放函數(shù)可能被利用偽造數(shù)據(jù)塊頭指針,微軟實(shí)現(xiàn)了一系列的方法來保護(hù)堆避免受到此類攻擊:安全釋放和堆元數(shù)據(jù)標(biāo)識(shí),前者在釋放前檢測(cè)要?jiǎng)h除塊的前一個(gè)堆的后向堆塊指針和后一個(gè)堆的前向堆塊指針是否均指向當(dāng)前的數(shù)據(jù)塊,在實(shí)踐中這個(gè)過程很難被破壞,但不幸的是,這個(gè)方法也受到某些先決條件的限制,后者則是通過在堆數(shù)據(jù)塊頭指針中保存一個(gè)標(biāo)識(shí),在刪除鏈接之前對(duì)其進(jìn)行檢測(cè)。
(5)數(shù)據(jù)執(zhí)行防范(DEP)是防止放入堆、?;驍?shù)據(jù)段中的代碼運(yùn)行。這一直是操作系統(tǒng)的一個(gè)目標(biāo),但是直到2004年,AMD在CPU里增加了NX位,首次實(shí)現(xiàn)DEP的硬件支持,通過NX位可識(shí)別內(nèi)存頁是否可執(zhí)行,隨后不久,Intel也在CPU里加入了XD位來完成同樣的事情。但是由于兼容性問題,DEP并非一直處于使能狀態(tài)。
(6)地址空間隨機(jī)分布(ASLR),將隨機(jī)性引入到進(jìn)程所使用的內(nèi)存地址中,由于內(nèi)存地址的變化使得攻擊更加困難。Windows Vista和隨后的版本都引入了ASLR技術(shù)。繞過ASLR最簡(jiǎn)單的方法就是返回到?jīng)]有與ASLR保護(hù)鏈接的模塊。
5結(jié)束語
緩沖區(qū)溢出攻擊會(huì)對(duì)程序和系統(tǒng)造成嚴(yán)重危害,因此需要采取有效措施來防范緩沖區(qū)溢出漏洞。在Linux和Windows操作系統(tǒng)中,通??梢酝ㄟ^漏洞檢測(cè)、漏洞修復(fù)、運(yùn)行時(shí)防護(hù)等技術(shù)來達(dá)到防范目的。
參考文獻(xiàn):
[1] Wikipedia. Stack buffer overflow. Retrieved from Wikipedia: http://en.wikipedia.org/wiki/Stack_buffer_overflow
[2] Wikipedia. Heap overflow. Retrieved from Wikipedia: http://en.wikipedia.org/wiki/Heap_overflow.
[3] Anley, C. The Shellcoder's Handbook: Discovering and Exploiting Security Holes. Wiley Publishing,Inc,2007.
【通聯(lián)編輯:光文玲】