文/Yue CHEN
會(huì)找漏洞的時(shí)光機(jī):Pinpointing Vulnerabilities
文/Yue CHEN
當(dāng)我們的系統(tǒng)遭受攻擊時(shí),即便我們已經(jīng)檢測(cè)到攻擊,如何知道被利用的漏洞在哪里呢?
先讓我們來(lái)看一個(gè)簡(jiǎn)單的例子(圖1):假如用戶(hù)輸入的argv[ ]是個(gè)很長(zhǎng)的字符串,由于strcpy不限制拷貝的字符數(shù),buf會(huì)被溢出,那么在第6行函數(shù)return時(shí)候,很可能會(huì)控制流異常。假如使用攻擊檢測(cè),很可能檢測(cè)到第6行的return 0,但是真正的漏洞在第4行。癥狀(Symptom)和根本原因(Root Cause)在不同的地方。對(duì)于復(fù)雜的大程序,想要定位到Root Cause還是非常困難的。

圖1
目前基于memory instrumentation的漏洞檢測(cè),由于要instrument所有的內(nèi)存訪問(wèn),其開(kāi)銷(xiāo)特別大,因此不適合用于程序運(yùn)行時(shí)使用。所以這篇論文提出個(gè)系統(tǒng)叫Ravel (Root-cause Analysis of Vulnerabilities from Exploitation Log), 它包含一個(gè)online模塊和一個(gè)offline模塊. Online的模塊把運(yùn)行的不確定事件都記錄(Record)下來(lái),并且提供攻擊檢測(cè)功能;Offline的模塊可以重放(Replay)程序的運(yùn)行,并且可以instrument memory并且定位漏洞。
這樣做有以下優(yōu)勢(shì):
1. 可以把真實(shí)世界里的攻擊,在實(shí)驗(yàn)室環(huán)境進(jìn)行重放(Replay)并分析。
2. 運(yùn)行時(shí)候性能開(kāi)銷(xiāo)很小,開(kāi)銷(xiāo)大的漏洞定位階段被放在了Offline的實(shí)驗(yàn)室里。
3. 可擴(kuò)展。可以自由添加新的攻擊檢測(cè)和漏洞定位方法。
對(duì)于攻擊檢測(cè),Ravel用了現(xiàn)有的方法,比如程序崩潰,異常系統(tǒng)調(diào)用,CFI等。 對(duì)于Record amp; Replay, Ravel主要記錄了不確定事件,比如系統(tǒng)調(diào)用(syscall)結(jié)果,內(nèi)核到用戶(hù)空間的數(shù)據(jù)拷貝,同步原語(yǔ)等。
對(duì)于Replay階段的Memory Instrumentation,Ravel集成了二進(jìn)制翻譯引擎(Binary Translation Engine,簡(jiǎn)稱(chēng)BT)。其中很重要的功能 (也是一項(xiàng)挑戰(zhàn)) , 是可以把BT發(fā)出的syscall和目標(biāo)程序本身的syscall區(qū)分開(kāi)來(lái),以確保replay和record的一致性。
對(duì)于漏洞定位,Ravel設(shè)計(jì)了一系列功能。首先是數(shù)據(jù)流分析(Data-flow Analysis)。寫(xiě)入一塊內(nèi)存叫做define(簡(jiǎn)稱(chēng)def),把從這塊內(nèi)存里讀出數(shù)據(jù)叫use。假如指令A(yù)寫(xiě)入這塊內(nèi)存,指令B接著讀了出來(lái),它們就構(gòu)成了一組def-use關(guān)系。一堆這種關(guān)系可以構(gòu)成一張圖,叫DFG (Data-flow Graph)。
我們可以預(yù)先把這些關(guān)系找出來(lái),做一張DFG。假如運(yùn)行時(shí)候有之前沒(méi)見(jiàn)過(guò)的def-use關(guān)系出現(xiàn),可以視為發(fā)現(xiàn) 違規(guī)(violation),意味著很可能有漏洞。那么如何知道出問(wèn)題的地方是在def,還是在use呢?

圖2

圖3
Ravel用了一些啟發(fā)式搜索(heuristics)。這里用buffer overflow舉個(gè)例子。
比如我們本來(lái)有三個(gè)use (也就是read from memory),如圖2所示。然后有一個(gè)大的def (write to memory) 把這三個(gè)use都覆蓋了,如圖3所示。假如發(fā)現(xiàn)這些def-use關(guān)系是violation,那么很有可能,漏洞在def的指令及其周?chē)2⑶液苡锌赡苁莃uffer overflow, memory overwriting之類(lèi)的。Violation關(guān)系如圖4所示。別的漏洞,如information leakage,也可以用類(lèi)似的方法來(lái)定位。

圖4
對(duì)于整數(shù)錯(cuò)誤(Integer Errors), Ravel關(guān)注的是一些用整數(shù)計(jì)數(shù)(比如放在RCX/ECX寄存器里)的指令,比如MOVS, STOS 等; 和有整數(shù)參數(shù)的函數(shù),比如memcpy, recvfrom 等。從這些參數(shù)開(kāi)始,Ravel進(jìn)行backwards search來(lái)定位漏洞。
可以定位的整數(shù)錯(cuò)誤漏洞包括:
Assignment Truncation (比 如0x12345678 → 0x5678)
檢測(cè)方法:從longer type賦值到shorter type
Integer Overflow/underflow (比如0xFFFFFFFF + 1)
檢測(cè)方法:檢測(cè)RFLAGS/EFLAGS寄存器
Signedness Error (比如 unsigned_int_var = signed_int_var)
檢測(cè)方法:收集Hint。比如一些指令或者函數(shù)會(huì)指定特定的signed或者unsigned參數(shù)。詳情參見(jiàn)slides或者paper.
那么有些整數(shù)錯(cuò)誤是程序員/編譯器故意設(shè)置的,如何區(qū)分呢?
由于這些錯(cuò)誤已經(jīng)和reported的漏洞相關(guān),所以非常大可能是漏洞,而不是正常的整數(shù)操作。
假如Race Condition存在,replay的execution trace會(huì)和record下來(lái)的不一樣,所以可以用該方法來(lái)檢測(cè)。
一旦檢測(cè)到,Ravel繼續(xù)用happens-before relation來(lái)進(jìn)一步嘗試定位漏洞。

圖5
Ravel還能檢測(cè)以下一系列漏洞,此處不再贅述:
Use-after-free and Double-free
Buffer Overflow
Integer Errors
Information Leakage
Format String Vulnerabilities
Ravel的Record amp; Replay功能基于FreeBSD 10.2實(shí)現(xiàn),漏洞定位基于Valgrind實(shí)現(xiàn)。
圖5用Nginx的CVE-2013-2028 漏洞舉了一個(gè)Ravel如何從攻擊定位到漏洞的例子, 具體描述參見(jiàn)論文(論文鏈接: http://ww2.cs.fsu.edu/~ychen/paper/ravel.pdf)。
更多的Evaluation實(shí)驗(yàn), 比如Heartbleed,以及別的漏洞類(lèi)型比如Null Pointer Dereference,Heap Overflow,Out-of-bounds Read,Untrusted Pointer Dereference,也可以在論文里找到。

圖6
圖6 是Online Performance Overhead的Evaluation, 實(shí)驗(yàn)是在流行的Web服務(wù)器和SPEC CPU 2006上做的,可以看出性能開(kāi)銷(xiāo)非常小。
(責(zé)編:楊潔)
(作者單位為佛羅里達(dá)州立大學(xué))