我們在此章中先簡介 buffer overflow 之攻擊原理,並在之後介紹防範 buffer overflow 攻擊的相關研究。
2.1 Background: Overview of Buffer Overflow
由於 C 語言本身設計上的不安全與普遍使用,導致 buffer overflow 時常發生,當使 用指標與陣列時,C 語言本身設計上並不會自動檢查邊界,而需程式設計者自行在程式 中檢查之,再加上 C standard library 中的一些 string function 也是不安全的,因此若在撰 寫程式時稍有不慎,就可能有 buffer overflow 漏洞發生。
Buffer overflow 的攻擊方法大致可由 Figure 1 解釋之,當程式進入 foo( )時,若 sprintf( )沒有做好邊界檢查,可能導致字串寫入 buf 陣列時超過其邊界,而由於 stack 的 生長方向與字串寫入方向是相反的,則會 overwrite 進入方程式時 push 至堆疊的返回位 址(ret-address),當 foo( )返回時,processor 會使用 stack 中被改寫過的 ret-address 作為返 回位址,這將使程式執行流程改變,返回到被改寫的位址上繼續執行。
Overwrite & return to malicious code from attacker’s network input
Figure 1: Typical scenario of buffer overflow attack
通常攻擊者會對使用有漏洞的網路服務軟體作為伺服器的機器進行 buffer overflow 攻擊,這些網路服務軟體的權限通常都系統管理者或 root,攻擊者會把他希望執行的惡 意程式碼作為 network input,而在 ret-address 處填入這些惡意程式碼的起始點,因此由 foo( )返回時,就會以 root 權限執行這些惡意程式碼,完全控制此系統。這表示一旦被
攻擊者成功改寫控制程式流程的指標(如 return address 或 function pointer,這類資料稱為 control-sensitive data),就代表了系統遭受控制,由此可知保護 control-sensitive data 的重 要性。
2.2 Buffer Overflow Detector
Buffer overflow detector大致可以簡單的以static detector和dynamic detector分成兩 類。Static detector [12][19][43][15][23]在compile time時判斷是否array或pointer有遭受攻 擊的可能,若可能被攻擊,則detector就會有警告顯示。然而這樣的方法有許多的缺點 [1][28],無法偵測到所有的程式漏洞、會出現許多false positive warning,越注意所有遭 受攻擊可能的工具越會顯示出許多假的警告;而為了降低顯示假警告的機會,則又反而 可能會遺漏一些bug而讓攻擊者有機會入侵系統。再者,這些顯示出來的警告必須由 programmer自行一個一個的檢查是否真的有問題,這些缺點讓static detector變得難以實 際使用,因此許多的研究都朝向dynamic detector發展。
2.2.1 Detecting a Part of Program Regions
Dynamic detector能夠在攻擊發生時自動偵測之,有些buffer overflow detector,雖然 overhead較低,但僅只能保護軟體中的部份區域,例如return address、function pointer、
format string等。StackGuard[9][10]修改gcc編譯器,讓編譯過後的程式在執行至呼叫函數 時能夠在stack中的return address之後置入一個canary value,這讓return address被overwrite 時,canary value也會被overwrite,並在該函數返回時,檢察此canary value是否被修改,
就能夠偵測出buffer overflow。Hiroaki Etoh等人[13]利用canary value以及重新排列stack 上的陣列來保護return address與local variables。RAD[6]與StackShield[42]亦是修改編譯 器,在推入return address至stack時,同時將這return address複製到另一處以利之後辨別 比對此值是否遭修改。Libsafe與Libverify[1]在程式執行時動態載入他們修改的library,
讓程式在起始時先修改每個函數,使這些函數能夠在被呼叫時預先儲存return address,
再由函數返回時自動檢查return address是否被改寫而不需重新編譯程式。PointGuard[8]
將指標以XOR加密,在需要時解密並reference該指標,讓攻擊者無法預測指標加密後的 內容,但這樣只能保護到使用者程式的指標部分。FormatGuard[7]在call printf等function 之前增加一個wrapper function,使其能在程式執行時偵測出攻擊者輸入的惡意字串。
2.2.2 Randomization
部分研究利用randomization的方式來使address、instruction不易被猜測而達到保護
效果,卻容易遭受guessing attack[36][41],Address obfuscation[3]與Address Space Randomization (ASR)[26]隨機分配程式的heap、stack等分部位置使得攻擊者難以估算目 標位址,使攻擊者在overwrite資料後因reference error而偵測出攻擊,而利用這樣的方式,
能夠隨機搬移程式中所有區域的位置,因此能夠得到大範圍的保護,並且在執行期間不 會花費額外的負載來偵測攻擊而不會有額外的overhead。Instruction Set Randomization (ISR)[2] [17]加密執行指令,並在每個指令執行前解密後由processor執行之,這讓攻擊者 無法讓程式執行他們輸入的未加密過之惡意程式碼,但仍然會受guessing attack而使攻擊 成功。
2.2.3 Bound Checking
有些detector能夠較大範圍或全面保護所有buffer overflow區域,但是overhead卻高 達2倍至十幾倍,Robert等人[16]與Nicholas等人[24]皆是利用檢查指標指向範圍的方式,
判斷是否於分配給它的記憶體範圍內,若超出範圍則表示可能有buffer overflow發生,但 會產生false alarm。CRED[28]利用類似Robert的方法,但是調整了指標可接受的指向範 圍,只有在指標參考了其指向位址時超出了邊界,才表示有buffer overflow發生,因為只 要超出範圍的指標沒有參考該位址,也算是可接受的指標指向位址。
2.2.4 Other Issues
Non-executable buffer[11][26][40]將程式中的stack、heap區域更改為不可執行,這讓 攻擊者無法執行他們輸入至此區域上的惡意程式碼,但攻擊者仍可利用返回至library的 方式,並輸入他們的惡意參數,而達成攻擊的目的。Program shepherding[18]監視程式 control flow,利用interpreter檢查每個branch之後的指令是否為原來的程式碼,或檢查呼 叫library的指令位置是否正常,以防止執行惡意的程式碼,但也因此需要花費過大的 overhead解譯每個指令所以難以實際使用。Cyclone[15]與CCured[23]結合static與dynamic 分析,因為C是個不安全的語言,所以他們將它修改成較高安全度的語言,而static無法 分析的部份則在程式執行時確認邊界來防止overflow發生,但是與C不相容的新語言卻讓 大眾難以接受,。Zili Shao等人[37]同時利用硬體與軟體的方式,讓保護buffer overflow 變的更有效率,而因為修改了硬體,牽扯到了相容性的問題,因此他們也認定這樣的方 式只是適用在能夠依照不同需求而能讓軟硬體有特殊設計的Embedded System上,對於 general purpose system,則難以實際使用他們的方式。
高 overhead 的 detector 在實際上並不適用,而高 performance 的 detector,或難以全 面保護所有 buffer overflow 攻擊的部分,或有上述這些缺點,因而讓攻擊者仍得以成功
攻擊而控制系統。
2.3 Automatic System
最近幾年出現了一些新的 approach,除了提供新的偵測 buffer overflow 攻擊方式之 外,也希望能自動鑑識 application 漏洞,或自動產生 network input signature,或希望自 動修復系統。
DIRA [39]採用RAD的方式,以偵測他們是否遭受buffer overflow 攻擊這些data,利 用memory updates log來達到memory corruption instruction identification,並需重新編譯以 轉換軟體source code,讓該軟體能夠以checkpoint的方式,在遭受攻擊後能夠recovery軟 體。但是為了降低overhead,DIRA只有在全域變數被修改時才行checkpoint,而非每次 收到封包時就行checkpoint,因此在收到封包後且未行checkpoint時,就無法recovery成 功,更重要的問題是,由於其採用RAD的方式,因此不能夠攔住所有的buffer overflow 攻擊,這些缺點讓DIRA難以實際使用。
Martin Rinard等人[27],試著在軟體發生memory corruption後,忽略掉超出邊界的 寫入,將超出邊界的寫入另外儲存並在讓程式能讀取這些寫入時,而讓軟體繼續執行,
但由於這可能導致程式的buffer儲存了錯誤的資料,所以無法總是recovery成功,使程式 某些部分會執行出錯,100%~400%的overhead更是讓其難以實際使用之。
Stelios Sidiroglou等人[38]希望做到在得知軟體漏洞後,自動產生patch來修補這個漏 洞,但是他們亦只能用一些簡單的方式來修補,例如讓導致overflow的buffer改成動態配 置資料,但這樣的方式,讓此塊buffer還是同樣會發生buffer overflow而能改寫能控制程 式流程的資料,所以無法總是修補成功,而且利用這種offline的方式取得patch,較難及 時修補漏洞。
ARBOR[22][21]利用讀取input的歷史紀錄,取得此時讀取封包函數的位置、長度及 型態,並利用這些資訊產生signature來過濾attack input,但相同的讀取位置可能有不同 型態輸入,導致過濾器認定能接受的讀取長度較大,但攻擊者卻不用超過此長度則能攻 擊成功,因此無法成功過濾所有攻擊,在作者研究的實驗中就有無法過濾攻擊的情況發 生,並且會有產生false positive的情況。
TaintCheck[25]利用network input是否污染記憶體中的資料,只要與network input運 算後寫入至記憶體中的資料,就代表是被污染的資料,若被污染的資料被當作指標使 用,則表示此input為攻擊,並利用被污染的指標內容值作為signature,與Jun Xu等人[44]
相同,如此過於簡單的signature在使用至非ASCII的協定如DNS、SSL時,就很容易有false alarm產生,其偵測攻擊的方式也導致run time overhead達到十倍以上,而讓他難以實際
使用。
在[20]中,Zhenkai Liang等人利用ASR來偵測buffer overflow attack,因此有很低的 overhead,但為了產生較不容易有false alarm發生的signature,需要針對每種service訂定 input format specification。而且他們利用input size作為判斷是否為attack input的方法之 一,也導致了攻擊者可以利用漸漸縮小input size來不斷攻擊ASR的缺點–guessing attack [36][41],使得系統被攻擊成功。
Nebula[14]只簡單使用一個signature就能在Network layer達到過濾buffer overflow attack input的效果,但由於其只判斷input中是否含有數個可能為stack的位址,因此只能 保護stack overflow。針對利用heap overflow的攻擊則無法成功過濾。並且需要依照每種 protocol決定input payload的哪些部份能夠略過filter,否則會有很高的overhead及false positive。然而,即使依照不同的protocol定了哪些input是不需過濾的,也會出現如同 TaintCheck的情況,遇到binary protocol時會有false positive發生。
自動化系統的自動修補方式,有上述dynamic detector的缺點,只能保護程式部分區 域或是overhead過高,這些缺點讓自動修補系統難以使用。而由於自動產生signature的 方式也仍然不夠完善,因此仍會讓攻擊者略過過濾器,使得我們難以防止最希望能夠避 免的問題,而讓攻擊者成功入侵並控制系統,這也是buffer overflow攻擊最主要的目的,
因此這樣的自動化系統仍然難以實際使用。