文 琪 江喆越 張 源
(復(fù)旦大學(xué)軟件學(xué)院 上海 201203)
近年來(lái),新發(fā)現(xiàn)的安全漏洞數(shù)量快速增長(zhǎng)[1],給用戶的安全帶來(lái)了巨大的隱患。為了保障應(yīng)用軟件的安全,開(kāi)發(fā)者需要及時(shí)對(duì)漏洞進(jìn)行修復(fù)。而在開(kāi)源日漸成為一種趨勢(shì)的今天[3],代碼重用或者引用越來(lái)越多[4-5,9],一個(gè)漏洞影響到的軟件不再局限于其自身,還包括了許多引用到這些代碼的軟件,所有這些受影響的軟件都需要對(duì)這個(gè)安全漏洞進(jìn)行修復(fù)。當(dāng)漏洞影響到的軟件范圍廣泛、軟件維護(hù)者分散時(shí),如何保證所有受影響的軟件都能及時(shí)地修復(fù)漏洞成為了一個(gè)巨大的挑戰(zhàn)。也正是由于大量的軟件并沒(méi)有做到及時(shí)修復(fù)所有已知漏洞,使得無(wú)論是對(duì)于安全防護(hù)人員還是惡意攻擊者而言,能夠準(zhǔn)確地檢測(cè)安全漏洞是否被修復(fù),都有著無(wú)法忽視的意義。
為了避免出現(xiàn)歧義,我們首先對(duì)漏洞補(bǔ)丁存在性檢測(cè)的目標(biāo)和范圍給出一個(gè)明確的定義。漏洞補(bǔ)丁存在性檢測(cè)的目標(biāo)是,對(duì)于某一個(gè)開(kāi)源庫(kù)或軟件的漏洞補(bǔ)丁,檢查一個(gè)給定的目標(biāo)程序是否被應(yīng)用了該補(bǔ)丁,其中目標(biāo)程序可能對(duì)原函數(shù)做了定制化等檢測(cè)者未知的修改。同時(shí),我們假設(shè)漏洞補(bǔ)丁的所有信息對(duì)檢測(cè)者來(lái)說(shuō)是已知的,且待檢測(cè)目標(biāo)程序必須含有該漏洞影響的函數(shù),即我們不會(huì)去詢問(wèn)諸如“iOS12的內(nèi)核中是否應(yīng)用了安卓?jī)?nèi)核上的某漏洞補(bǔ)丁”這樣沒(méi)有任何實(shí)際價(jià)值的問(wèn)題。我們研究的目標(biāo)專注于如何高效且準(zhǔn)確地檢測(cè)漏洞補(bǔ)丁的存在性,不會(huì)過(guò)多的關(guān)注漏洞修復(fù)與否會(huì)對(duì)程序造成多大的危害,即無(wú)論是一個(gè)可以造成遠(yuǎn)程代碼執(zhí)行的漏洞還是一個(gè)僅僅只能造成DoS攻擊(denial-of-service attack)[2]的漏洞,對(duì)我們的檢測(cè)程序來(lái)說(shuō)是沒(méi)有區(qū)別的。
對(duì)于漏洞補(bǔ)丁存在性檢測(cè)這樣一個(gè)問(wèn)題而言,檢測(cè)結(jié)果的準(zhǔn)確性十分重要,較低的準(zhǔn)確率使得我們無(wú)法對(duì)工具的結(jié)果置信,只能對(duì)待測(cè)程序重新進(jìn)行人工檢查。而現(xiàn)有工作主要是利用函數(shù)相似度比較來(lái)找尋有漏洞的函數(shù),盡管它們提到這些技術(shù)也可以用來(lái)判斷一個(gè)函數(shù)中的漏洞是否被修復(fù),但實(shí)際它們會(huì)以犧牲準(zhǔn)確性為代價(jià)優(yōu)先保證效率。FIBER[18]作為以漏洞補(bǔ)丁存在性測(cè)試為目標(biāo)的工具,雖然在準(zhǔn)確性上有很大提升,但由于其簽名包含代碼結(jié)構(gòu)信息,仍難以在檢測(cè)的準(zhǔn)確性和穩(wěn)定性之間取得一個(gè)很好的平衡。
針對(duì)現(xiàn)有工具在漏洞補(bǔ)丁檢測(cè)準(zhǔn)確性上的缺陷,本文設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)工具,彌補(bǔ)現(xiàn)有工具的不足,通過(guò)將關(guān)注點(diǎn)從整個(gè)函數(shù)轉(zhuǎn)移到函數(shù)的局部,盡量擺脫函數(shù)補(bǔ)丁之外的代碼改動(dòng)帶來(lái)的影響,在保證方法適用性的前提條件下,準(zhǔn)確識(shí)別出一個(gè)函數(shù)是否應(yīng)用了特定的漏洞補(bǔ)丁。
本文的主要貢獻(xiàn)有:
? 指出漏洞補(bǔ)丁存在性檢測(cè)技術(shù)的重要性以及當(dāng)前相關(guān)領(lǐng)域研究匱乏的現(xiàn)狀。
? 設(shè)計(jì)并實(shí)現(xiàn)了漏洞補(bǔ)丁存在性檢測(cè)工具PatchChecker,通過(guò)聚焦于函數(shù)的單條路徑,利用漏洞補(bǔ)丁的語(yǔ)義特征,克服了之前工作在大范圍測(cè)試中準(zhǔn)確性低、難以在漏報(bào)率和誤報(bào)率中取得平衡的問(wèn)題,真正實(shí)現(xiàn)了高效、穩(wěn)定、準(zhǔn)確的檢測(cè)效果。
? 對(duì)PatchChecker進(jìn)行了全面的實(shí)驗(yàn)和評(píng)估,驗(yàn)證了其檢測(cè)效果。
目前的相關(guān)研究中,以漏洞補(bǔ)丁存在性檢測(cè)為目標(biāo)的工作寥寥無(wú)幾,絕大多數(shù)的工作都是圍繞函數(shù)相似度比較展開(kāi),指出可以利用函數(shù)相似度比較進(jìn)行漏洞補(bǔ)丁存在性檢測(cè)。
在源代碼層面,CP-Miner[7]利用數(shù)據(jù)挖掘技術(shù)檢測(cè)出了眾多由簡(jiǎn)單復(fù)制粘貼導(dǎo)致的漏洞。CCFinder[6]將輸入的源碼信息轉(zhuǎn)義成特征序列,改進(jìn)了之前行對(duì)行的檢測(cè)工作中所存在的缺陷。為了解決搜索空間和效率的問(wèn)題,Deckard[8]將源碼轉(zhuǎn)為樹(shù)結(jié)構(gòu),通過(guò)子樹(shù)相似度來(lái)尋找相似代碼。ReDeBug[4]給出了一個(gè)基于語(yǔ)法的獨(dú)特設(shè)計(jì),雖然找到的代碼克隆相對(duì)較少,但檢測(cè)速度高、范圍廣,并減少了誤報(bào)率。VulPecker[9]構(gòu)建了一個(gè)包含不同漏洞及相應(yīng)特征的集合,針對(duì)不同漏洞選取相應(yīng)的相似性算法。
在二進(jìn)制程序?qū)用妫嚓P(guān)工作無(wú)法依賴于變量名、變量類型等基礎(chǔ)信息,于是往往會(huì)選擇基于代碼的結(jié)構(gòu)[10-12]來(lái)實(shí)現(xiàn)。比如BinDiff[13]依賴于控制流圖的同構(gòu)來(lái)分析判斷,而B(niǎo)inSlayer[14]則進(jìn)一步將問(wèn)題轉(zhuǎn)換成二分圖匹配問(wèn)題。discovRE[10]則是提取基本代碼塊中的數(shù)值特征信息,利用梯度下降計(jì)算代碼塊間的距離,最后通過(guò)解決最大公共子圖同構(gòu)的問(wèn)題來(lái)計(jì)算圖之間的距離。改進(jìn)方案如Genius[11]和Gemin[12]則從控制流圖中提取特征并轉(zhuǎn)換成多維向量進(jìn)行檢測(cè)。而在基于語(yǔ)義的研究工作方面,文獻(xiàn)[15]利用基本代碼塊的I/O作為特征進(jìn)行匹配,文獻(xiàn)[16-17]則利用符號(hào)執(zhí)行和定理證明器對(duì)具有相同語(yǔ)義的基本代碼塊進(jìn)行了形式上的證明。
由于漏洞補(bǔ)丁普遍對(duì)程序本身的改動(dòng)十分有限,一般漏洞的修復(fù)往往通過(guò)幾行代碼的修改就能實(shí)現(xiàn),這些改動(dòng)比程序在版本迭代過(guò)程中帶來(lái)的功能性改動(dòng)要小得多。在這種情況下,這些基于整個(gè)函數(shù)的相似度比較的方法,很可能會(huì)被這些功能性改動(dòng)影響到程序判斷的結(jié)果,無(wú)法達(dá)到一個(gè)較高的準(zhǔn)確率。
FIBER是當(dāng)前唯一專門用于漏洞補(bǔ)丁存在性檢測(cè)的工具,其可用性和準(zhǔn)確性相比于函數(shù)相似度比較的方式都得到了一個(gè)質(zhì)的提升。FIBER首先會(huì)在漏洞補(bǔ)丁中選取最為合適的修改位置作為代表,依據(jù)這些選取出來(lái)的修改,在樣本二進(jìn)制程序中生成一個(gè)包含盡量多的源代碼信息的簽名,最后利用這個(gè)生成的簽名在待測(cè)二進(jìn)制程序中進(jìn)行匹配。FIBER是一個(gè)半語(yǔ)義化的工具,其生成的簽名同時(shí)包含了漏洞補(bǔ)丁的部分語(yǔ)義信息和部分結(jié)構(gòu)信息。
FIBER將其關(guān)注點(diǎn)完全放在特定的幾行被漏洞補(bǔ)丁改動(dòng)的代碼上,同時(shí)為了保證簽名的唯一性,額外增加了一些與漏洞補(bǔ)丁本身無(wú)關(guān)的代碼作為上下文進(jìn)入簽名之中。但這樣的處理方式,在保證唯一性的同時(shí),也使得簽名變得更加不穩(wěn)定,如果這些與漏洞補(bǔ)丁無(wú)關(guān)的代碼在功能迭代或定制化的過(guò)程中發(fā)生了改變,F(xiàn)IBER的檢測(cè)很可能因此而產(chǎn)生錯(cuò)誤。
如圖1所示,本文將PatchChecker的工作流程劃分為三個(gè)模塊:預(yù)處理模塊、輸入生成模塊和檢測(cè)模塊。
圖1 PatchChecker的整體模塊劃分
預(yù)處理模塊是PatchChecker執(zhí)行開(kāi)始的部分,除了基本的漏洞補(bǔ)丁文件解析外,其最主要的工作是編譯生成后面模塊需要用的內(nèi)核鏡像樣本。
PatchChecker會(huì)從Linux內(nèi)核的源代碼倉(cāng)庫(kù)中提取出漏洞修復(fù)前后的兩份除了漏洞補(bǔ)丁外完全相同的源代碼,采用完全相同的編譯器與編譯選項(xiàng),基于內(nèi)核的默認(rèn)配置進(jìn)行編譯。為了使后續(xù)的分析能夠正常進(jìn)行,這里需要確認(rèn)漏洞影響到的函數(shù)在生成的內(nèi)核鏡像樣本中存在。對(duì)于不存在的情況,主要是兩種原因?qū)е?,一是漏洞影響到的函?shù)所在的內(nèi)核子模塊默認(rèn)未開(kāi)啟,編譯時(shí)這些代碼直接被忽視,我們需要根據(jù)內(nèi)核的Makefile文件,找到相應(yīng)的控制選項(xiàng)并開(kāi)啟后重新編譯;二是漏洞補(bǔ)丁修改的函數(shù)被編譯器進(jìn)行了內(nèi)聯(lián)處理,這種情況只能嘗試在降低編譯優(yōu)化等級(jí)的情況下重新編譯,但即使如此,函數(shù)仍可能被內(nèi)聯(lián)。
通過(guò)多次的迭代重新編譯,我們能對(duì)大部分情況生成可用的內(nèi)核鏡像樣本。對(duì)個(gè)別函數(shù)始終無(wú)法找到的情況,如果這個(gè)漏洞補(bǔ)丁影響到的其他函數(shù)中有能在內(nèi)核樣本中找到的,我們可以認(rèn)為這個(gè)內(nèi)核樣本是可用的,因?yàn)橐粋€(gè)漏洞的修復(fù)一般來(lái)說(shuō)是原子操作,不存在一個(gè)函數(shù)被修復(fù),而另一個(gè)函數(shù)沒(méi)有被修復(fù)的情況。如果漏洞影響到的所有函數(shù)都始終無(wú)法找到,那么PatchChecker暫時(shí)無(wú)法對(duì)此漏洞進(jìn)行檢測(cè)。
輸入生成模塊的設(shè)計(jì)目標(biāo)是希望找到一組能夠代表漏洞補(bǔ)丁語(yǔ)義的輸入。對(duì)一個(gè)函數(shù)而言,一組輸入對(duì)應(yīng)著一條特定的執(zhí)行路徑,路徑上的信息代表著這次執(zhí)行帶來(lái)的影響。以CVE-2016-7117為例,圖2是其官方補(bǔ)丁的代碼(因篇幅原因省略了對(duì)注釋的修改),可以發(fā)現(xiàn)其對(duì)代碼的修改難以用簡(jiǎn)單的特征準(zhǔn)確代表,但仔細(xì)觀察補(bǔ)丁內(nèi)容,當(dāng)代碼的約束條件為err!=0 && datagrams!=0 && err !=-EAGAIN時(shí),賦值操作ock->sk->skerr=-err和函數(shù)調(diào)用fput_light(sock->file,fput_needed)的執(zhí)行順序在漏洞修復(fù)前后發(fā)生了改變。而這個(gè)漏洞的本質(zhì)正是在調(diào)用fput_light之后,sock指針指向的結(jié)構(gòu)體可能已經(jīng)被free,因此觸發(fā)了UAF(Use After Free)漏洞。而經(jīng)過(guò)官方修補(bǔ)后,這個(gè)賦值操作先于fput_light,因此賦值時(shí)sock指針指向的結(jié)構(gòu)體不可能已經(jīng)被free了。所以,在這個(gè)例子中,用這條關(guān)鍵路徑上這兩個(gè)操作的順序作為簽名來(lái)代表這個(gè)漏洞補(bǔ)丁的語(yǔ)義非常準(zhǔn)確。
圖2 漏洞CVE-2016-7117官方補(bǔ)丁
為了找到這樣能夠代表漏洞語(yǔ)義信息的輸入,PatchChecker采用了以模糊測(cè)試為主、靜態(tài)分析為輔的方法。模糊測(cè)試技術(shù)往往被用于漏洞發(fā)現(xiàn),利用自動(dòng)或半自動(dòng)生成的隨機(jī)數(shù)據(jù)去觸發(fā)漏洞。在這個(gè)場(chǎng)景中,PatchChecker的目標(biāo)相比于觸發(fā)漏洞要容易許多,往往只要執(zhí)行到了漏洞補(bǔ)丁改動(dòng)的代碼部分,路徑上的信息就很可能在漏洞修復(fù)前后的樣本中表現(xiàn)出不一致,足以反映這個(gè)漏洞補(bǔ)丁的語(yǔ)義。
檢測(cè)模塊是PatchChecker工作流程中的最后一環(huán)。在該模塊中,對(duì)于給定的漏洞補(bǔ)丁,PatchChecker將在參考內(nèi)核鏡像樣本中生成的輸入交給待測(cè)內(nèi)核執(zhí)行,獲取其輸出,通過(guò)將得到的輸出分別與該輸入在漏洞補(bǔ)丁應(yīng)用前后的內(nèi)核鏡像上運(yùn)行得到的輸出進(jìn)行對(duì)比,給出判斷結(jié)果。如果發(fā)現(xiàn)待測(cè)內(nèi)核鏡像上運(yùn)行得到的輸出與兩個(gè)樣本上運(yùn)行得到的輸出的相似度一致,那么PatchChecker將無(wú)法判斷待測(cè)目標(biāo)的修補(bǔ)情況。一種常見(jiàn)的例子是待測(cè)內(nèi)核增加或修改了部分關(guān)鍵的判斷條件,導(dǎo)致生成的輸入在待測(cè)內(nèi)核上運(yùn)行時(shí),走到了一條與漏洞補(bǔ)丁修改的代碼無(wú)關(guān)的路徑上。對(duì)于這種情況,我們可以生成多種不同的滿足要求的輸入,只要有一組輸入執(zhí)行時(shí)能走到期望的路徑上,就可以作出正確的判斷。
執(zhí)行引擎是PatchChecker的核心,在輸入生成模塊和檢測(cè)模塊中,執(zhí)行引擎都扮演著舉足輕重的角色,它負(fù)責(zé)將給定的程序輸入轉(zhuǎn)換成其在指定的內(nèi)核上執(zhí)行后產(chǎn)生的輸出。由于在本文場(chǎng)景中,所有的執(zhí)行過(guò)程都是單獨(dú)執(zhí)行一個(gè)函數(shù),在未設(shè)置好上下文的情況下,通過(guò)插樁的手段去實(shí)際運(yùn)行單個(gè)函數(shù)極易出現(xiàn)異常。所以我們選擇使用模擬執(zhí)行的方式來(lái)運(yùn)行,這樣可以更為簡(jiǎn)單地建立一個(gè)可用的運(yùn)行環(huán)境,并對(duì)可能出現(xiàn)的異常進(jìn)行恰當(dāng)?shù)奶幚?。同時(shí),考慮到執(zhí)行引擎的運(yùn)行效率也是衡量工具的關(guān)鍵因素,我們選擇使用具體的數(shù)值進(jìn)行模擬執(zhí)行,以避免符號(hào)化執(zhí)行可能引起的效率問(wèn)題。
綜合考慮,我們選取了Unicorn Engine[19]作為執(zhí)行引擎的核心。Unicorn Engine基于QEMU[20]實(shí)現(xiàn),是一個(gè)輕量級(jí)、跨平臺(tái)、跨架構(gòu)的CPU模擬器框架,其能夠同時(shí)支持Arm、Arm64(Armv8)、M68K、Mips、Sparc和X86(包括X86_64),這為PatchChecker的跨架構(gòu)支持提供了基礎(chǔ)保障。Unicorn Engine由純C代碼實(shí)現(xiàn),并依靠實(shí)時(shí)編譯技術(shù)提供了較高的運(yùn)行效率。
圖3簡(jiǎn)單描繪了執(zhí)行引擎的工作流程,執(zhí)行引擎首先會(huì)將整個(gè)內(nèi)核鏡像加載到內(nèi)核地址空間中,然后按照輸入的要求,初始化其余的內(nèi)存空間和寄存器。在初始化過(guò)程中,由于是利用實(shí)值進(jìn)行模擬執(zhí)行,而交給執(zhí)行引擎的輸入是帶有部分符號(hào)信息的,故執(zhí)行引擎首先需要進(jìn)行去符號(hào)化。由于輸入中的符號(hào),實(shí)際是由內(nèi)存空間的劃分導(dǎo)致的,執(zhí)行引擎首先會(huì)選擇一塊未使用的空間,接著按照輸入中列出的內(nèi)存區(qū)域的編號(hào)順序,依次為每塊內(nèi)存區(qū)域切分一片固定大小的內(nèi)存,例如1 MB。當(dāng)內(nèi)存區(qū)域分配好后,只要將輸入中的所有內(nèi)存區(qū)域編號(hào)和偏移的二元組用該區(qū)域地址加偏移的結(jié)果替換即可消除符號(hào)。鑒于內(nèi)存的分配是按照輸入中內(nèi)存空間的編號(hào)依次有序進(jìn)行的,故只要輸入內(nèi)容不變,去符號(hào)化的結(jié)果也會(huì)保持不變,這確保了程序模擬執(zhí)行輸出的穩(wěn)定,有利于之后對(duì)輸出的比較。輸入轉(zhuǎn)換完后,執(zhí)行引擎需要利用Unicorn Engine的鉤子機(jī)制,對(duì)所有的內(nèi)存操作進(jìn)行掛鉤,以記錄發(fā)生過(guò)改變的內(nèi)存,減少不必要的噪聲和分析開(kāi)銷。
圖3 執(zhí)行引擎的工作流程
所有初始狀態(tài)設(shè)置完成后,執(zhí)行引擎將從目標(biāo)函數(shù)的開(kāi)頭開(kāi)始單步執(zhí)行。對(duì)于每一條指令,執(zhí)行引擎會(huì)判斷其是否是一個(gè)輸出點(diǎn),對(duì)于當(dāng)前PatchChecker的實(shí)現(xiàn),我們只選取了所有的函數(shù)調(diào)用及函數(shù)返回作為輸出點(diǎn)。對(duì)于所有的輸出點(diǎn),執(zhí)行引擎將記錄下程序執(zhí)行到該位置時(shí),相比于初始狀態(tài)發(fā)生改變的內(nèi)存地址及其值、當(dāng)前調(diào)用函數(shù)的地址或名稱。針對(duì)函數(shù)調(diào)用,由于函數(shù)原型解析的復(fù)雜性,暫時(shí)只會(huì)記錄第一個(gè)參數(shù)。對(duì)于函數(shù)返回,則會(huì)記錄下相應(yīng)的函數(shù)返回值。在一個(gè)輸出點(diǎn)記錄下來(lái)的所有這些信息稱作一個(gè)impact,當(dāng)執(zhí)行結(jié)束時(shí),執(zhí)行引擎按順序記錄下了一系列的impact,即得到了一個(gè)impact的序列,這個(gè)序列可以視為執(zhí)行引擎針對(duì)本次輸入執(zhí)行得到的輸出。
由于本文目標(biāo)在于找到一組能夠在漏洞修復(fù)前后的兩個(gè)內(nèi)核樣本中運(yùn)行產(chǎn)生不同效果的帶符號(hào)的輸入,現(xiàn)有的模糊測(cè)試工具難以滿足這一需求,因此我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的模糊測(cè)試引擎。圖4描繪了這個(gè)模糊測(cè)試引擎的工作流程。
圖4 模糊測(cè)試的工作流程
首先,根據(jù)預(yù)處理模塊解析出的函數(shù)原型,我們對(duì)函數(shù)參數(shù)進(jìn)行隨機(jī)的賦值。為了提高模糊測(cè)試的效率,這里我們對(duì)隨機(jī)賦值的可選范圍進(jìn)行了限制,其中基礎(chǔ)的數(shù)值包括0、0xffffffff、2n(n為任意值),而指針則包括任意一塊已經(jīng)存在內(nèi)存區(qū)域的基地址或者新分配的一塊內(nèi)存區(qū)域的基地址。通過(guò)該限制條件,我們?cè)诳s小隨機(jī)范圍的情況下,盡可能覆蓋了包括但不限于空值、長(zhǎng)度、符號(hào)位、指針在內(nèi)的各種數(shù)據(jù)類型,使我們有能力遍歷整個(gè)函數(shù)中足夠多的分支。
由于初始隨機(jī)產(chǎn)生的輸入沒(méi)有經(jīng)過(guò)任何校驗(yàn),故極有可能是非法輸入。當(dāng)這樣一個(gè)非法輸入交由執(zhí)行引擎執(zhí)行時(shí),很可能會(huì)在執(zhí)行中途因?yàn)楦鞣N異常而無(wú)法繼續(xù),尤其是內(nèi)存訪問(wèn)異常最為常見(jiàn)。當(dāng)模糊測(cè)試引擎捕獲到這樣的異常時(shí),將采取符號(hào)執(zhí)行或數(shù)據(jù)流分析的方式,確定導(dǎo)致這個(gè)異常的輸入數(shù)據(jù)源頭,直接對(duì)相應(yīng)的輸入點(diǎn)進(jìn)行矯正。然后將修改后的輸入重新交由執(zhí)行引擎執(zhí)行,如果執(zhí)行的過(guò)程中又出現(xiàn)了新的異常,將采用相同的方式繼續(xù)嘗試修復(fù)輸入,通過(guò)這樣不斷地迭代,便能得到可以完成整個(gè)函數(shù)運(yùn)行的合法輸入。
對(duì)于修復(fù)后的合法輸入,將對(duì)其在漏洞修復(fù)前后的兩個(gè)內(nèi)核樣本中執(zhí)行產(chǎn)生的輸出(impact序列)進(jìn)行對(duì)比。當(dāng)這兩個(gè)輸出完全一致時(shí),該輸入是不滿足需求的,將以其為種子進(jìn)行變種。每次變種,我們都會(huì)在當(dāng)前種子輸入的函數(shù)參數(shù)、當(dāng)前種子輸入在樣本內(nèi)核上運(yùn)行時(shí)訪問(wèn)到的內(nèi)存空間、外部函數(shù)調(diào)用的返回值中隨機(jī)選取一個(gè)作為本次的修改點(diǎn),在賦值范圍限制內(nèi)對(duì)其重新進(jìn)行隨機(jī)賦值。多次迭代變種之后,會(huì)找到滿足要求的輸入,即能在漏洞修復(fù)前后的兩個(gè)內(nèi)核樣本中執(zhí)行時(shí)產(chǎn)生不同的輸出。
雖然這個(gè)模糊測(cè)試引擎的實(shí)現(xiàn)仍在初步階段,且對(duì)輸入變種的范圍進(jìn)行了較大的限制,但由于需要找到的輸入條件并不苛刻,所以仍可以達(dá)到較好的效果。在之后的實(shí)驗(yàn)中,這個(gè)簡(jiǎn)單的模糊測(cè)試引擎已能全自動(dòng)地完成大部分漏洞補(bǔ)丁的輸入生成工作。
本文選擇ARM32作為實(shí)驗(yàn)中可執(zhí)行程序的架構(gòu),在實(shí)驗(yàn)中采用的機(jī)器CPU是64核的Intel?Xeon?CPU E7- 4830,主頻為2.13 GHz,內(nèi)存為64 GB。
從CVE Details[21]上2013年-2016年的Linux內(nèi)核漏洞中,選擇了12個(gè)漏洞作為測(cè)試集合,在選取時(shí)盡量覆蓋了不同類型、不同CVSS[22]分?jǐn)?shù)、不同影響力的漏洞進(jìn)行測(cè)試,以驗(yàn)證PatchChecker的適用性。
表1展示了在選出的 12個(gè)CVE上,生成滿足要求的輸入所需的時(shí)間,其中小括號(hào)內(nèi)的代表輸入生成失敗及失敗用時(shí)??梢园l(fā)現(xiàn),12個(gè)CVE對(duì)應(yīng)的20個(gè)函數(shù)中,只有兩個(gè)函數(shù)沒(méi)有能夠成功生成滿足我們要求的輸入。由于這兩個(gè)函數(shù)所在CVE均有多個(gè)影響到的函數(shù),而其他函數(shù)成功生成了滿足要求的輸入,因此即使其生成輸入失敗,也不會(huì)影響PatchChecker對(duì)其所在CVE的檢測(cè)。
表1 PatchChecker輸入生成運(yùn)行時(shí)間
續(xù)表1
對(duì)于運(yùn)行效率,由于函數(shù)復(fù)雜度不同,不同函數(shù)的輸入生成用時(shí)差異巨大。但由于輸入的生成可以離線完成,故這些生成用時(shí)均在可以接受范圍內(nèi)。
對(duì)于檢測(cè)模塊,使用與輸入生成測(cè)試中相同的CVE漏洞進(jìn)行測(cè)試,函數(shù)輸入使用輸入生成測(cè)試過(guò)程得到的結(jié)果,對(duì)于生成失敗的兩個(gè)函數(shù),通過(guò)人工介入,分別生成了一組滿足要求的輸入。而對(duì)于待測(cè)內(nèi)核,則是在Linux內(nèi)核代碼倉(cāng)庫(kù)中選取了100個(gè)不同的提交,每個(gè)提交均采用4種不同的編譯選項(xiàng)進(jìn)行編譯,得到400個(gè)待測(cè)內(nèi)核,這些內(nèi)核的漏洞補(bǔ)丁實(shí)際修復(fù)情況可以直接根據(jù)代碼提交記錄準(zhǔn)確得知。提交的選取方式大致如下:首先根據(jù)選出的漏洞,找到其影響的文件,然后在內(nèi)核代碼倉(cāng)庫(kù)中,找出所有對(duì)這些被影響到的文件有改動(dòng)的提交,從這些提交中,在保證時(shí)間跨度的前提下隨機(jī)選取。這樣的選取方式使得選出的提交更容易對(duì)漏洞影響的代碼有功能上的改變,可以在一定程度上測(cè)試PatchChecker在不同版本代碼中的穩(wěn)定性。
表2展示了PatchChecker對(duì)12個(gè)漏洞的檢測(cè)情況。由于PatchChecker設(shè)計(jì)的目的是為了能夠通過(guò)自動(dòng)化的檢測(cè)為用戶提供多一層的安全檢查,防止用戶暴露在已知漏洞的威脅之下,因此最需要關(guān)注的還是錯(cuò)誤的比例。我們希望PatchChecker能夠做到不產(chǎn)生誤判,因?yàn)槊恳粋€(gè)誤判都會(huì)降低這個(gè)工具的可信度。根據(jù)表2中的數(shù)據(jù),在大部分的CVE上,PatchChecker實(shí)現(xiàn)了很好的效果,只有CVE-2013-7446的unix_create1和CVE-2015-5706的path_openat錯(cuò)誤率相對(duì)較高。對(duì)于CVE-2013-7446而言,由于其有多個(gè)函數(shù)可以用來(lái)進(jìn)行檢測(cè),因此綜合這些函數(shù)的判定結(jié)果后,還是可以實(shí)現(xiàn)很低的錯(cuò)誤率。
表2 PatchChecker在待測(cè)CVE各個(gè)函數(shù)上的測(cè)試結(jié)果
本文對(duì)漏洞補(bǔ)丁存在性檢測(cè)這樣一個(gè)新興的問(wèn)題進(jìn)行了深入的研究,為了能夠解決現(xiàn)有工具在效率、準(zhǔn)確率等各方面的不足和所面臨的問(wèn)題,設(shè)計(jì)并實(shí)現(xiàn)了一個(gè)準(zhǔn)確高效的全自動(dòng)漏洞補(bǔ)丁存在性檢測(cè)工具PatchChecker。PatchChecker聚焦于函數(shù)的單條路徑,提取漏洞補(bǔ)丁的語(yǔ)義信息進(jìn)行檢查。通過(guò)對(duì)12個(gè)真實(shí)漏洞在400個(gè)Linux內(nèi)核鏡像上進(jìn)行測(cè)試,驗(yàn)證了PatchChecker能夠給出一個(gè)準(zhǔn)確的檢測(cè)結(jié)果。