陳林博,江建慧,張丹青
(同濟(jì)大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)系,上海 201804)
緩沖區(qū)溢出漏洞是困擾系統(tǒng)安全的最常見漏洞之一,近幾年來所占比例不低于當(dāng)年發(fā)布總漏洞數(shù)的10%[1].相比堆緩沖區(qū)漏洞和格式化字符串攻擊的漏洞,棧緩沖區(qū)漏洞仍占多數(shù),且攻擊者利用棧緩沖區(qū)漏洞的手段多樣化,攻擊更隱蔽.
由于C及C++語言中指針的靈活操作與廣泛運(yùn)用,同時(shí)函數(shù)中存在沒有做邊界檢查的緩沖區(qū),并且Intel 80X86體系結(jié)構(gòu)中棧的增長方向及棧中數(shù)據(jù)的增長方向相反,都為緩沖區(qū)溢出攻擊提供了條件.目前,典型的棧緩沖區(qū)攻擊方法有直接篡改函數(shù)的返回地址EIP和調(diào)用函數(shù)的棧基址EBP值、通過指針間接篡改函數(shù)返回地址等.防御緩沖區(qū)溢出最成功的方法有基于編譯器優(yōu)化的StackGuard[2]、基于操作系統(tǒng)優(yōu)化的ASLR[3]和Non-executable stack[4]的組合方法.
本文首先從攻擊者角度對(duì)存儲(chǔ)在棧中的數(shù)據(jù)分類,并簡要描述了棧緩沖區(qū)溢出攻擊的兩種模式.其次,闡述了現(xiàn)有棧緩沖區(qū)溢出防御方法及其存在的問題,在此基礎(chǔ)上提出了基于雙棧結(jié)構(gòu)的緩沖區(qū)溢出防御模型,并分析其有效性.最后開發(fā)了一個(gè)針對(duì)ELF格式的目標(biāo)文件重構(gòu)工具用以實(shí)現(xiàn)雙棧結(jié)構(gòu),以實(shí)驗(yàn)驗(yàn)證了雙棧結(jié)構(gòu)的有效性,并評(píng)估其性能.
緩沖區(qū)溢出攻擊是指攻擊者利用程序中存在的漏洞,向緩沖區(qū)中存入超過設(shè)計(jì)時(shí)定義的數(shù)據(jù)量,從而覆蓋緩沖區(qū)相鄰存儲(chǔ)單元中的數(shù)據(jù),使得程序在執(zhí)行時(shí)跳轉(zhuǎn)執(zhí)行攻擊者所指定的代碼.因此可以將棧緩沖區(qū)溢出攻擊總結(jié)為兩種模式[5-11]:一是通過對(duì)緩沖區(qū)的溢出直接篡改EBP和EIP等關(guān)鍵數(shù)據(jù);二是通過對(duì)緩沖區(qū)的溢出修改指針變量等,間接篡改EBP和EIP等關(guān)鍵數(shù)據(jù).
通過對(duì)攻擊模式的分析,可將棧中數(shù)據(jù)分為安全(security)相關(guān)數(shù)據(jù)和非安全相關(guān)數(shù)據(jù),其中,安全相關(guān)數(shù)據(jù)包括各種保存在內(nèi)存中的寄存器值,如:返回地址、EBP等,指針類型變量,指針數(shù)組、字符數(shù)組等各種數(shù)組變量,包含字符數(shù)組的聯(lián)合體和結(jié)構(gòu)體等.非安全相關(guān)數(shù)據(jù)包括整數(shù)類型變量、浮點(diǎn)數(shù)類型變量及其他攻擊過程中不被利用的變量.
安全相關(guān)數(shù)據(jù)又可以根據(jù)攻擊者的利用程度分為三種類型:載體數(shù)據(jù)、目標(biāo)數(shù)據(jù)和中間數(shù)據(jù).載體數(shù)據(jù)指棧中存在的能被攻擊者直接利用的數(shù)據(jù),主要有緩沖區(qū)類型的變量,如未經(jīng)邊界檢查的字符數(shù)組,它承載了攻擊者的惡意代碼.目標(biāo)數(shù)據(jù)指能直接或者間接影響函數(shù)控制流的數(shù)據(jù),如函數(shù)返回地址、EBP等.介于這兩者之間的為中間數(shù)據(jù).這類數(shù)據(jù)在多步攻擊中既能被攻擊者作為目標(biāo)數(shù)據(jù),又能被間接利用作為載體數(shù)據(jù)來篡改目標(biāo)數(shù)據(jù)的值.例如在int*a=b賦值語句中,若指針變量a的低地址側(cè)存在可溢出的緩沖區(qū),那么可以通過改變a的值使其指向函數(shù)返回地址,再改變b的值使其指向惡意代碼.那么此時(shí)b承載了指向惡意代碼的指針,因此可以作為載體數(shù)據(jù);a被賦予了指向返回地址的指針也可以作為載體數(shù)據(jù),但同時(shí)要通過a修改返回地址的值,所以a也可以作為目標(biāo)數(shù)據(jù).
因此,攻擊模式一可以表述成攻擊者將惡意代碼注入到載體數(shù)據(jù)中,利用載體數(shù)據(jù)的特性直接覆蓋并篡改目標(biāo)數(shù)據(jù)的內(nèi)容.攻擊模式二可以表述成攻擊者將惡意代碼注入到載體數(shù)據(jù)中,通過篡改中間數(shù)據(jù)的內(nèi)容,使其指向目標(biāo)數(shù)據(jù)的地址后間接篡改目標(biāo)數(shù)據(jù)的內(nèi)容.
針對(duì)緩沖區(qū)溢出攻擊,在程序生命周期的每個(gè)階段都有相應(yīng)的防御方法,主要分為兩類:一是防止目標(biāo)數(shù)據(jù)被篡改,二是防止惡意代碼執(zhí)行.本文所述的基于雙棧結(jié)構(gòu)的防御方法是防止目標(biāo)數(shù)據(jù)被篡改.
編譯器在編譯源代碼的過程中采用棧來保存控制程序正常執(zhí)行的數(shù)據(jù),即在棧中保存EBP和EIP等目標(biāo)數(shù)據(jù).因此可通過編譯器在源程序的編譯過程中引入保護(hù)機(jī)制,如在程序中插入安全相關(guān)的代碼,以保護(hù)程序的棧結(jié)構(gòu)或者控制相關(guān)的安全數(shù)據(jù).
2.1.1 邊界檢查
邊界檢查是防御緩沖區(qū)溢出攻擊的有效方法之一.如果在程序運(yùn)行時(shí)能得到每個(gè)數(shù)組的大小信息,并且能實(shí)時(shí)檢查數(shù)組中的數(shù)據(jù)是否發(fā)生越界行為,那么就能保證代碼不會(huì)篡改數(shù)組邊界外的信息,溢出攻擊就不可能發(fā)生.
bcc是一種源到源的高級(jí)語言轉(zhuǎn)換器[12],用來實(shí)現(xiàn)對(duì)指針引用和數(shù)組訪問的邊界檢查.在源代碼中指針引用和數(shù)組訪問的地方添加實(shí)現(xiàn)邊界檢查、空檢查和對(duì)齊檢查的函數(shù),并且封裝經(jīng)常誤用的標(biāo)準(zhǔn)庫函數(shù).但是,使用bcc會(huì)增加編譯時(shí)間,產(chǎn)生更多的代碼,程序的運(yùn)行時(shí)間也會(huì)急劇增加.
類似的方法還有Safe C[13],它為每個(gè)指針定義了安全相關(guān)的屬性,如指針值、指針基地址、指針大小、存儲(chǔ)類型(堆、局部變量、全局變量)等,程序在每次引用指針訪問數(shù)據(jù)時(shí)都會(huì)依據(jù)指針的屬性,檢驗(yàn)指針是否有效、是否越界等安全狀態(tài).C的數(shù)組邊界檢查技術(shù)[14]對(duì)每個(gè)指針只定義了“基指針”單個(gè)屬性,在每次使用指針時(shí)對(duì)這一屬性檢查,比較其是否仍在邊界內(nèi).
從理論角度分析,邊界檢查的方法能從本質(zhì)上解決緩沖區(qū)溢出漏洞,但需直接修改程序中二進(jìn)制代碼甚至是添加額外的局部變量,性能開銷很大.
2.1.2 棧的保護(hù)
目前大部分針對(duì)棧保護(hù)方法的主要目的是為了保護(hù)棧中的返回地址值免受篡改,后來又拓展到保護(hù)棧中其他的安全關(guān)鍵信息,包括EBP值和局部變量指針值.
StackGuard是在緊鄰棧的返回地址低地址處插入一個(gè)canary字,保護(hù)返回地址值不受篡改[2].該canary字可以是程序啟動(dòng)時(shí)隨機(jī)生成的32位的隨機(jī)值,也可是終結(jié)符,如NULL,LF,-1或者CR:0X000aff0d等.當(dāng)被調(diào)用函數(shù)執(zhí)行完返回時(shí),將會(huì)檢查canary字是否變化.如果變化將會(huì)記錄警報(bào)并終止程序,如沒有則從棧中移除canary字,函數(shù)會(huì)正常返回.在后續(xù)版本中,StackGuard將canary字與返回地址異或運(yùn)算得到新的canary字插入棧中,防止返回地址被篡改.即使如此,StackGuard不能防御攻擊模式二和攻擊模式一中針對(duì)EBP的攻擊.
Stack Shield通過修改編譯器插入優(yōu)化代碼來保護(hù)棧上返回地址[15],包括兩個(gè)方法:Global ret stack和Ret range check.前者在程序啟動(dòng)時(shí),在堆上分配全局?jǐn)?shù)組用來存儲(chǔ)函數(shù)的返回地址.在函數(shù)執(zhí)行開始時(shí),將返回地址復(fù)制到堆上的返回地址表中,在函數(shù)結(jié)束時(shí),比較或者直接從堆上的返回地址表讀取返回地址用以替換當(dāng)前棧幀中的返回地址,與此同時(shí)堆上的返回地址表中相應(yīng)的返回地址將會(huì)移除,函數(shù)正常返回.但是在堆上分配的返回地址表的大小是固定的,若函數(shù)中的返回地址數(shù)超過了返回地址表的容量,那么超過容量的部分返回地址將不受保護(hù).后者是在進(jìn)程的數(shù)據(jù)段的開始處設(shè)置全局變量G,在函數(shù)返回時(shí),若返回地址值R<G,表明函數(shù)返回地址指向進(jìn)程的代碼段,函數(shù)將正常返回.若R>G,那么返回地址指向數(shù)據(jù)段,程序?qū)⒔K止.但與StackGuard一樣,Stack Shield也不能防御攻擊模式二及攻擊模式一中針對(duì)EBP的攻擊.
類似的防范還有返回地址保護(hù)RAD[16],但與Stack Shield不同之處,RAD采用兩種方法保護(hù)RAR中存儲(chǔ)的返回地址值,分別是Minezone RAD和Read-Only RAD.Minezone RAD是聲明一個(gè)全局的整型數(shù)組作為RAR用來存儲(chǔ)返回地址值,并將RAR兩端的相鄰頁置為只讀.這樣可避免基于堆緩沖區(qū)溢出的攻擊,篡改RAR中的返回地址值.Read-Only RAD是將整個(gè)RAR標(biāo)志為只讀,只有在函數(shù)開始時(shí),設(shè)置RAR為可寫,加入新的函數(shù)返回地址值.RAD也采用連續(xù)彈出RAR中地址,利用窮舉法找出相同的值,以解決系統(tǒng)調(diào)用setjmp()和longjmp()造成的RAR和棧中返回地址不一致的問題.
盡管GCC 4.0以上版本通過在棧中插入隨機(jī)canary字實(shí)現(xiàn)了對(duì)返回地址和EBP值的保護(hù),但是相應(yīng)的破解方法依然層出不窮,如通過篡改EBP來破解StackGuard和Stack Shield的方法[8].而若程序中存在能被利用的代碼,那么攻擊者可以不需要注入代碼,直接篡改返回地址使其指向程序中代碼位置,這樣就能破解 RAD[9-10].
2.1.3 指針的保護(hù)
現(xiàn)有的通過注入惡意代碼而實(shí)現(xiàn)的緩沖區(qū)溢出攻擊都需要篡改內(nèi)存中指針的值,攻擊者首先要篡改指針使其指向注入的惡意代碼,當(dāng)程序在引用該指針時(shí),原來指向合法代碼的指針卻指向惡意代碼,而程序?qū)?zhí)行該惡意代碼.
PointGuard[17]在進(jìn)程啟動(dòng)時(shí)隨機(jī)生成一個(gè)密鑰,利用異或運(yùn)算加密存儲(chǔ)在內(nèi)存中的指針值.在程序需要訪問指針的數(shù)據(jù)時(shí),利用相同的密鑰解引用該指針,然后才能正確找到數(shù)據(jù)所在的位置.若攻擊者沒有密鑰,在溢出緩沖區(qū)并篡改了指針的值后,程序在正常執(zhí)行過程中解密指針,得到的數(shù)據(jù)將不會(huì)指向惡意代碼處,而會(huì)指向不可預(yù)知的地址,在多數(shù)情況下會(huì)導(dǎo)致進(jìn)程退出或異常行為.若程序中存在能被格式化字符串攻擊利用的漏洞,攻擊者可以獲得部分加密指針的數(shù)據(jù),且由于加密算法僅僅是采用異或運(yùn)算,因此攻擊者很容易得到加密的密鑰.
針對(duì)緩沖區(qū)溢出攻擊防御的方法還有許多,在硬件上實(shí)現(xiàn)的對(duì)函數(shù)返回地址的保護(hù),如SplitStack[18]、StackGhost[19]等.在操作系統(tǒng)層面上實(shí)現(xiàn)的有指令集隨機(jī)化方法 RISE[20]和 Kc[21];內(nèi)存空間地址變形方法,包括地址隨機(jī)化方法ASLR[3]及 TRR[22]、地址迷惑方法[23-24]和地址空間分離[25];不可執(zhí)行棧PAGEEXEC[26]、Non-executable stack[4].在操作系統(tǒng)層面實(shí)現(xiàn)的方法大部分是在攻擊者修改了目標(biāo)數(shù)據(jù)后,作為最后一道防御措施用以防止惡意代碼的執(zhí)行.
現(xiàn)有的基于編譯器的棧緩沖區(qū)溢出防御方法存在下列問題:① 密鑰的存儲(chǔ)與保護(hù)難以防范攻擊者的竊取.以StackGuard中的canary、PointGuard中密鑰為例,若程序中存在能被格式化字符串攻擊利用的漏洞,攻擊者可以直接得知canary[6],或者獲得部分加密指針的數(shù)據(jù),并通過簡單的異或運(yùn)算得出加密的密鑰;② 保護(hù)的數(shù)據(jù)類型過少,如StackGuard和Stack Shield僅對(duì)返回地址進(jìn)行保護(hù).因此攻擊者可以參照攻擊模式一,篡改EBP,利用偽造的棧幀構(gòu)造指向惡意代碼的EIP值,繞過保護(hù)字進(jìn)行攻擊[8];③ 性能開銷大,編譯器優(yōu)化的方法是要在二進(jìn)制代碼中插入安全代碼,而增加操作指令,會(huì)增加性能的開銷.
針對(duì)已有防御方法所存在的問題,本文提出了一種基于雙棧結(jié)構(gòu)的緩沖區(qū)溢出防御方法,該方法將在棧中常被攻擊者利用的載體數(shù)據(jù)從棧中剝離.一方面將目標(biāo)數(shù)據(jù)與載體數(shù)據(jù)分離,并在兩者間加入防護(hù)頁,達(dá)到直接保護(hù)目標(biāo)數(shù)據(jù)的目的.另一方面將中間數(shù)據(jù)與載體數(shù)據(jù)分離,使繞過該雙棧結(jié)構(gòu)中保護(hù)頁的攻擊變得困難.最重要的是本方法不需在程序中添加額外指令來保護(hù)棧,僅需修改程序中某些特定的指令即可,因此它對(duì)性能的影響較小.
雙棧結(jié)構(gòu)是在緊鄰原棧的低地址處構(gòu)造新棧,用以存儲(chǔ)程序中的載體數(shù)據(jù),因?yàn)橹挥休d體數(shù)據(jù)能被攻擊者直接利用,所以在兩個(gè)棧之間設(shè)立防護(hù)頁(guard page),確保新棧中的數(shù)據(jù)不能有越過棧邊界的行為.圖1中給出了采用雙棧結(jié)構(gòu)和未采用雙棧結(jié)構(gòu)的數(shù)據(jù)分布情況.
圖1 采用雙棧結(jié)構(gòu)和未采用雙棧結(jié)構(gòu)的數(shù)據(jù)分布Fig.1 Data distribution on stack with or without dualstack structure
防護(hù)頁是不能讀也不能寫的頁,任何訪問該頁的操作都會(huì)引起程序異常中斷.采用雙棧結(jié)構(gòu)可以保證攻擊者最常利用的緩沖區(qū)類型變量都放在新棧中,而棧的增長方向與緩沖區(qū)增長方向相反,緩沖區(qū)的溢出只能覆蓋其在新棧中相鄰的高地址處的變量值,由于防護(hù)頁的存在而不能篡改目標(biāo)數(shù)據(jù),也不能通過更改中間數(shù)據(jù)間接篡改目標(biāo)數(shù)據(jù).
棧指針ESP隨著局部變量出入棧而不斷地改變,但EBP值從函數(shù)被調(diào)用開始就被確定下來,所以對(duì)被調(diào)用函數(shù)中局部變量的訪問都將通過該變量與EBP的偏移量來計(jì)算該變量的存儲(chǔ)地址.不同于SplitStack[18]中采用額外的棧指針CSP來確定新分配棧的位置,這里只要對(duì)程序中的某些指令做修改就能實(shí)現(xiàn)雙棧結(jié)構(gòu).首先在函數(shù)的初始棧楨代碼prologue中修改初始棧的指令,如sub sizeofstack,%esp,將其設(shè)置為sub 2×sizeofstack+pagesize,%esp,即將棧大小拓展為當(dāng)前所分配棧的2倍,并且多分配一頁空間作為防護(hù)頁.棧中局部變量的訪問都是通過計(jì)算變量與EBP的偏移量offset來實(shí)現(xiàn),因此上述修改不會(huì)影響棧中數(shù)據(jù)的分布.然后修改機(jī)器指令中所有對(duì)載體數(shù)據(jù)的訪問指令,即修改原棧中載體數(shù)據(jù)與EBP的偏移量,將(sizeofstack+pagesize)加入到偏移量中,使得載體數(shù)據(jù)移到新棧當(dāng)中.
圖2給出了一個(gè)具體示例.采用雙棧結(jié)構(gòu)后,buf在內(nèi)存中的位置比在原棧中的位置要下移(sizeofstack+pagesize).原棧中的目標(biāo)數(shù)據(jù)、中間數(shù)據(jù)及非安全相關(guān)的數(shù)據(jù)所處的位置不改變.隨著指令的執(zhí)行,被調(diào)用函數(shù)strcpy()的參數(shù)也將會(huì)通過push指令存放在緊鄰新棧的低地址處,這不影響程序的正常運(yùn)行,因?yàn)樵诤瘮?shù)調(diào)用完成后,將通過ret指令返回,棧指針ESP也將被重新賦值,函數(shù)運(yùn)行過程中使用的原棧和新棧也將會(huì)釋放.
圖2 含漏洞的源碼及其采用和未采用雙棧結(jié)構(gòu)的棧分布Fig.2 Source code with vulnerability and stack layouts with or without dual-stack structure
為了避免發(fā)生繞過防護(hù)頁的間接指針攻擊,在新棧中不能存儲(chǔ)類似指針數(shù)組等可能被攻擊利用的變量類型.可以在指針數(shù)組中插入動(dòng)態(tài)的邊界檢查,檢查數(shù)組下標(biāo)是否在界內(nèi).這樣可以將作為載體數(shù)據(jù)的指針數(shù)組轉(zhuǎn)換成非載體數(shù)據(jù)類型,并將修改后的指針數(shù)組存放到原棧中.
假設(shè)程序中存在棧緩沖區(qū)溢出漏洞,并且攻擊者能估計(jì)出當(dāng)前棧緩沖區(qū)所在的虛擬地址.
對(duì)于攻擊模式一,在未加入防御機(jī)制前,攻擊者能有效利用惡意代碼注入到緩沖區(qū)中,直接覆蓋目標(biāo)數(shù)據(jù),篡改EIP值.當(dāng)程序在從當(dāng)前棧幀返回時(shí)將已篡改的EIP出棧,程序的控制流將跳轉(zhuǎn)到攻擊者指定的惡意代碼處.采用雙棧結(jié)構(gòu)的防御,將程序中的載體數(shù)據(jù)與目標(biāo)數(shù)據(jù)隔離,即使攻擊者能猜測到當(dāng)前載體數(shù)據(jù)的虛擬地址,也會(huì)因?yàn)閮烧咧g存在guard page而導(dǎo)致惡意代碼不能越過保護(hù)頁覆蓋目標(biāo)數(shù)據(jù),因此雙棧結(jié)構(gòu)能有效防御攻擊模式一.
對(duì)于攻擊模式二,假設(shè)程序在載體數(shù)據(jù)的低地址處存在能被利用的指針數(shù)據(jù),攻擊者能估計(jì)出當(dāng)前棧緩沖區(qū)所在的虛擬地址.在未加入防御機(jī)制前,攻擊者能夠通過修改指針使其指向當(dāng)前棧幀中的返回地址,并再次利用該漏洞將其指向的地址值(即返回地址)修改為惡意代碼處的起始位置.在采用雙棧結(jié)構(gòu)后,被間接利用的中間數(shù)據(jù)同樣也會(huì)與載體數(shù)據(jù)隔離,并與目標(biāo)數(shù)據(jù)保存到原棧中,因此在新棧中的載體數(shù)據(jù)盡管寫入了惡意代碼,但是當(dāng)前棧中不存在中間數(shù)據(jù),攻擊不能繞過guard page,因此,攻擊模式二間接轉(zhuǎn)換為攻擊模式一.
從程序編譯后的二進(jìn)制代碼可以看出,編譯器在編譯過程中設(shè)置棧中數(shù)據(jù)的排列及棧幀大小.開發(fā)了一個(gè)重構(gòu)ELF格式的目標(biāo)文件的工具軟件,用以在程序中實(shí)現(xiàn)雙棧結(jié)構(gòu),該工具的目標(biāo)對(duì)象是通過GCC編譯器-g選項(xiàng)獲得的可重構(gòu)文件.
首先反匯編 .text節(jié)區(qū)中機(jī)器指令,在其匯編代碼中通過修改prologue初始棧的指令,如sub sizeofstack,%esp,將其設(shè)置為sub2×sizeofstack+pagesize,%esp,將新分配大小為sizeofstack的空間作為新棧.
然后通過GCC編譯器中的-g選項(xiàng)獲得可重定位文件的DWARF3格式下的調(diào)試信息,利用調(diào)試信息定位載體數(shù)據(jù)所在的函數(shù)塊及其在棧中的位置,即與當(dāng)前棧幀中EBP的偏移量.在匯編代碼中修改該載體數(shù)據(jù)的偏移量,其實(shí)質(zhì)只需修改該載體數(shù)據(jù)相關(guān)的每條指令,將(sizeofstack+pagesize)加入到載體數(shù)據(jù)的偏移量中,即可實(shí)現(xiàn)在新棧中存儲(chǔ)載體數(shù)據(jù).最后將修改后的匯編代碼轉(zhuǎn)換成機(jī)器指令后重新放入 .text節(jié)區(qū).
考慮到指令在修改后長度會(huì)變化,因此,利用指令長度增加的總量重新定位 .rel.text節(jié)區(qū)中各表項(xiàng)的值(主要為offset值),使其指向修改后的 .text節(jié)中引用外部變量的地址處.最后鏈接該可重構(gòu)文件,所得的可執(zhí)行文件就具有了雙棧結(jié)構(gòu)的保護(hù)功能.
GCC 4.1以上版本中開始使用Propolice堆棧保護(hù)方法[27].該方法是在函數(shù)prologue處先設(shè)置棧的初始大小,而后插入保護(hù)代碼,即將canary插入到棧中緊鄰EBP的低地址處,并在函數(shù)返回時(shí)比較判斷是否發(fā)生溢出.由于插入的保護(hù)代碼指令和比較指令都需要通過EBP的偏移量確定棧中的數(shù)據(jù),因此本文所述目標(biāo)文件重構(gòu)工具可借用該保護(hù)機(jī)制來實(shí)現(xiàn)防護(hù)頁功能,即通過修改該指令,將canary移動(dòng)到原棧和新棧的連接處,并在函數(shù)返回時(shí)比較canary的值,實(shí)現(xiàn)了防護(hù)頁的保護(hù)功能.
為驗(yàn)證本方法及依據(jù)其原理所開發(fā)的工具軟件的有效性,并分析其性能,設(shè)計(jì)了相應(yīng)的實(shí)驗(yàn).實(shí)驗(yàn)環(huán)境建立在Dell微機(jī)(3GHz core2CPU、2GB內(nèi)存)上,運(yùn)行包含Red Hat 7.0企業(yè)版的虛擬機(jī),內(nèi)存設(shè)置為256MB,編譯器選用GCC 4.2.1.
目前絕大部分的緩沖區(qū)溢出攻擊代碼都是由Aleph One攻擊代碼衍生而來[5],因此本實(shí)驗(yàn)也采用Aleph One攻擊集作為實(shí)驗(yàn)對(duì)象.它們主要由四組程序構(gòu)成,分別是overflow1.c,vulnerable.c/exploit2.c,vulnerable.c/exploit3.c 和 vulnerable.c/exploit4.c,其中,overflow1.c中包含攻擊程序和被攻擊程序,vulnerable.c作為被攻擊程序,承受來自exploit2.c,exploit3.c及exploit4.c等攻擊程序的攻擊,exploit4.c屬攻擊模式二,其他屬攻擊模式一.
本文使用所開發(fā)的目標(biāo)代碼重構(gòu)工具重構(gòu)了overflow1.c和vulnerable.c的二進(jìn)制代碼,測試結(jié)果如表1所示.
表1 Aleph One攻擊代碼的測試結(jié)果Tab.1 Results of attack test with Aleph One codes
從表1可知,采用雙棧結(jié)構(gòu)后的被攻擊程序能有效防御緩沖區(qū)溢出攻擊,并能正常返回.本工具的實(shí)質(zhì)是修改目標(biāo)文件中 .text節(jié)區(qū)的二進(jìn)制代碼,因此在表中列出了重構(gòu)前后指令序列長度以作比較.這四組程序中只有overflow1.c在重構(gòu)前后指令序列長度發(fā)生變化,這是因?yàn)槠湓谥貥?gòu)時(shí),有三處指令需修改,這三處指令使用的立即數(shù)或地址均小于0x7f.指令在重構(gòu)后所使用的立即數(shù)或地址超過0x7f,因此遵循指令對(duì)齊原則,其長度增加三個(gè)字節(jié),這樣,重構(gòu)后指令序列總長度增加到149.而vulnerable.c的程序相對(duì)簡單,需要重寫的指令的立即數(shù)或地址均大于0x7f,故重構(gòu)后的指令序列長度沒有變化.重構(gòu)后overflow1.c的可執(zhí)行文件所占用存儲(chǔ)空間也相應(yīng)增加了9個(gè)字節(jié),而vulnerable.c在重構(gòu)前后的可執(zhí)行文件所占用存儲(chǔ)空間沒有變化.盡管如此,overflow1.c與vulnerable.c在重構(gòu)前后的可執(zhí)行文件的指令數(shù)沒有變化.
為了評(píng)估程序采用雙棧結(jié)構(gòu)后的性能,采用文獻(xiàn)[23-24]中所用的幾組驗(yàn)證程序作為基準(zhǔn)程度集macrobenchmarks,并編寫了幾組常用的程序作為基準(zhǔn)程度集microbenchmarks,如表2所示.
利用目標(biāo)代碼重構(gòu)工具重構(gòu)程序,比較其重構(gòu)前后的運(yùn)行時(shí)間和所占用存儲(chǔ)空間,其中macrobenchmarks分別運(yùn)行20次,測量結(jié)果見表3.
表3 性能測試結(jié)果Tab.3 Performance test results
從表3中可以看出,采用雙棧結(jié)構(gòu)并未對(duì)程序的性能產(chǎn)生不利影響.這是因?yàn)橹貥?gòu)僅僅是修改了訪問局部變量時(shí)引用的偏移量,沒有添加額外的安全指令,因此使用雙棧結(jié)構(gòu)后的程序的性能開銷可以忽略不計(jì).重構(gòu)工具修改了函數(shù)prologue中的棧的初始分配指令及函數(shù)中對(duì)載體數(shù)據(jù)類型數(shù)據(jù)變量的訪問指令,所以重構(gòu)后增加的代碼長度不僅與程序中函數(shù)調(diào)用的次數(shù)相關(guān),也與其對(duì)相關(guān)變量的具體操作指令相關(guān).另外從表3中還可以看出,采用雙棧結(jié)構(gòu)后,程序也沒有占用過多的存儲(chǔ)空間.
采用雙棧結(jié)構(gòu)程序在運(yùn)行時(shí)所分配棧的大小至少是原來?xiàng)5?倍,因而在虛擬地址空間上開銷較大.但只要沒有數(shù)據(jù)寫入到新分配的棧中相應(yīng)頁上,根據(jù)寫時(shí)復(fù)制技術(shù)可認(rèn)為進(jìn)程只會(huì)在頁面表中占據(jù)一項(xiàng),不會(huì)對(duì)實(shí)際的物理地址空間造成額外開銷.
本文闡述分析了棧緩沖區(qū)溢出攻擊的兩種模式,并由此對(duì)棧中的數(shù)據(jù)進(jìn)行分類.在分析基于編譯器的典型緩沖區(qū)溢出攻擊防御方法的基礎(chǔ)上,提出了利用雙棧結(jié)構(gòu)實(shí)現(xiàn)緩沖區(qū)溢出漏洞的防御方法,分析了該結(jié)構(gòu)的有效性.為了評(píng)估該結(jié)構(gòu)的有效性和性能,設(shè)計(jì)并開發(fā)了一個(gè)重構(gòu)ELF格式的目標(biāo)文件的工具軟件.實(shí)驗(yàn)表明,所提出的方法及開發(fā)的工具能有效防御緩沖區(qū)溢出攻擊,并且性能開銷較小.
本文所述的重構(gòu)工具主要是修改局部變量的訪問指令,讓局部變量在棧中呈稀疏分布,隱藏甚至消除漏洞.為了更有效地迷惑攻擊者,可以利用該工具修改函數(shù)中所有的局部變量訪問方式,使各變量在棧中呈隨機(jī)化排列,讓攻擊者更難猜測各種安全相關(guān)數(shù)據(jù)在棧中的地址,提高攻擊代價(jià),有效地提高防御性能.也可以對(duì)該工具進(jìn)行修改,適當(dāng)加大新棧,使其有足夠的空間包容各類攻擊帶來的大量溢出數(shù)據(jù),即使在受到攻擊時(shí),函數(shù)也能返回正確的值.
[1]National Institute of Standards and Technology.National vulnerability database statistics[EB/OL].[2009-06-15].http://nvd.nist.gov/statistics.cfm.
[2]Cowan C,Pu C,Maier D,et al.StackGuard:automatic adaptive detection and prevention of buffer-overflow attacks[C]∥Proceedings of the 7th Conference on USENIX Security Symposium.San Antonio:USENIX Association,1998:63-78.
[3]The PaX Team.PaX address space layout randomization(ASLR)[EB/OL].[2009-06-15].http://pax.grsecurity.net/docs/aslr.tx.
[4]Solar Designer.Non-executable stack patch[EB/OL].[2009-06-15].http://www.openwall.com.
[5]Aleph One.Smashing stack for fun and profit[EB/OL].(1996-08-11)[2009-06-15].http://www.phrack.com/issues.html?issue:49&id=14#article.
[6]Strackx R,Younan Y,Philippaerts P,et al.Breaking the memory secrecy assumption[C]∥Proceedings of the Second European Workshop on System Security.Nuremburg:ACM New York Press,2009:1-8.
[7]Salamat B,Jackson T,Wagner G,et al.Runtime defense against code injection attacks using replicated execution [J].IEEE Transactions on Dependable and Secure Computing,2011,8(4):588.
[8]Richarte G.Four different tricks to bypass StackShield and StackGuard protection[EB/OL].(2002-04-06)[2009-06-15 ]. http://www. core security. com/files/attachments/StackGuard.pdf.
[9]Krahmer S.x86-64 buffer overflow exploits and the borrowed code chunks exploitation technique[EB/OL].[2009-06-15].http://www.suse.de/~krahmer-/no-nx.pdf.
[10]Roglia G F, Martignoni L,Paleari R,et al.Surgically returning to randomized lib(c)[C]∥Proceedings of Annual Computer Security Applications Conference.Honolulu:ACM New York Press,2009:60-69.
[11]Dalton M,Kannan H,Kozyrakis C.Real-world buffer overflow protection for userspace &kernelspace[C]∥Proceedings of the 17th USENIX Security Symposium.San Jose:USENIX Association,2008:395-410.
[12]Kendall S.Bcc:Runtime checking for C programs [C]∥Proceedings of the USENIX Summer 1983 Conference.Toronto:USENIX Association,1983:5-16.
[13]Austin T M,Breach S E,Sohi G S.Efficient detection of all pointer and array access errors[C]∥Proceedings of the ACM Conference on Programming Language Design and Implementation.Orlando:ACM New York Press,1994:290-301.
[14]Jones R W M,Kelly P H J.Backwards-compatible bounds checking for arrays and pointers in C programs [C]∥Proceedings of the 3rd International Workshop on Automatic Debugging.Link?ping:Link?ping Univerty Electronic Press,1997:13-26.
[15]Vendicator. Stack Sheild: a stack smashing technique protection tool for Linux [EB/OL].(2000-01-08)[2009-06-15].http://www.angelfire.com/sk/stackshield.
[16]Chiueh T,Hsu F.RAD:A compile-time solution to buffer overflow attacks[C]∥Proceedings of the 21st International Conference on Distributed Computing Systems.Phoenix:IEEE Computer Society,2001:409-420.
[17]Cowan C, Beattie S,Johansen J,et al. Point-guard:Protecting pointers from buffer overflow vulnerabilities[C]∥Proceedings of the 12th USENIX Security Symposium.Washington:USENIX Association,2003:91-104.
[18]Xu J,Kalbarczyk Z,Patel S,et al.Architecture support for defending against buffer overflow attacks[C]∥Proceedings of the Second Workshop on Evaluating and Architecting System Dependability.San Jose:ACM New York Press,2002:51-62.
[19]Frantzen M,Shuey M.StackGhost:hardware facilitated stack protection [C]∥Proceedings of the 10th USENIX Security Symposium.Washington:USENIX Association,2001:271-286.
[20]Barrantes E G,Ackley D H,F(xiàn)orrest S,et al.Randomized instruction set emulation to disrupt binary code injection attacks[C]∥Proceedings of the 10th ACM Conference on Computer and Communications Security.Washington:ACM New York Press,2003:281-289.
[21]Kc G S,Keromytis A D,Prevelakis V.Countering codeinjection attacks with instruction-set randomization [C]∥Proceedings of the 10th ACM Conference on Computer and Communications Security.Washington:ACM New York Press,2003:272-280.
[22]Xu J, Kalbarczyk Z, Iyer R. Transparent runtime randomization for security [C]∥Proceedings of the 22nd International Symposium on Reliable Distributed Systems.Florence:IEEE Computer Society,2003:260-269.
[23]Bhatkar S,DuVarney D C,Sekar R.Address obfuscation:An efficient approach to combat a board range of memory error exploits[C]∥Proceedings of the 12th USENIX Security Symposium.Washington:USENIX Association,2003:105-120.
[24]Bhatkar S,Sekar R,DuVarney D C.Efficient techniques for comprehensive protection from memory error exploits[C]∥Proceedings of the 14th USENIX Security Symposium.Baltimore:USENIX Association,2005:105-120.
[25]Riley R,Jiang X,Xu D.An architectural approach to preventing code injection attacks[J].IEEE Transactions on Dependable and Secure Computing,2010,7(4):351.
[26]The PaX Team.Documentation for the PaX project[EB/OL].[2009-09-15].http://pax.grsecurity.net/docs/pageexec.txt.
[27]Etoh H.ProPolice:GCC extension fro protecting applications from stack-smashing attacks [EB/OL].(2005-08-22)[2009-09-15].http://www.trl.ibm.com/projects/security/ssp/.