文/談心
內(nèi)核中的數(shù)據(jù)競(jìng)爭(zhēng)可能導(dǎo)致許多有害行為。最嚴(yán)重的后果是數(shù)據(jù)競(jìng)爭(zhēng)導(dǎo)致內(nèi)存污染最終可能造成非法權(quán)限提升,例如CVE-2016-8655, CVE-2017-2636, and CVE-2017-17712。目前的技術(shù)在內(nèi)核數(shù)據(jù)競(jìng)爭(zhēng)漏洞的檢測(cè)與防護(hù)方面都存在一定的局限性,其原因是內(nèi)核中的數(shù)據(jù)競(jìng)爭(zhēng)漏洞受到一些系統(tǒng)不確定性行為的影響,比如線程的調(diào)度與同步機(jī)制。因此與普通的漏洞相比,要檢測(cè)這類漏洞除了需要控制流和數(shù)據(jù)流信息以外,還需要精準(zhǔn)地執(zhí)行信息。
在該項(xiàng)工作中,本文提出并設(shè)計(jì)了針對(duì)內(nèi)核中的數(shù)據(jù)競(jìng)爭(zhēng)類型漏洞的模糊測(cè)試(Fuzzing)工具Razzer。本文的思路是引導(dǎo)Fuzzing工具去盡可能地執(zhí)行到存在數(shù)據(jù)競(jìng)爭(zhēng)漏洞的代碼。這包含了兩種技術(shù):一是通過(guò)靜態(tài)分析來(lái)定位潛在存在數(shù)據(jù)競(jìng)爭(zhēng)的代碼;二是一種確定性的線程交錯(cuò)技術(shù),來(lái)控制線程調(diào)度,以提供精確的并行執(zhí)行信息,降低不確定性。本工作并沒(méi)有去解決同步機(jī)制對(duì)多線程fuzzing的影響。
為了更好地檢測(cè)數(shù)據(jù)競(jìng)爭(zhēng)類型漏洞,文中對(duì)這類問(wèn)題給出了一個(gè)明確的定義。
如果目標(biāo)程序內(nèi)兩條內(nèi)存訪問(wèn)的指令滿足以下3個(gè)條件,就是數(shù)據(jù)競(jìng)爭(zhēng):1. 訪問(wèn)的內(nèi)存地址相同;2. 至少其中一條指令是對(duì)內(nèi)存的寫;3. 兩條指令可以并發(fā)執(zhí)行。
同時(shí),數(shù)據(jù)競(jìng)爭(zhēng)并不都是漏洞,有一些數(shù)據(jù)競(jìng)爭(zhēng)可能是開發(fā)者有意設(shè)計(jì)的,有一些數(shù)據(jù)競(jìng)爭(zhēng)行為可能產(chǎn)生非預(yù)期的行為,這些才是數(shù)據(jù)競(jìng)爭(zhēng)漏洞。文中還引入了一些標(biāo)注,以便后續(xù)的說(shuō)明:
RacePair_{cand}:可能滿足上面三個(gè)條件的RacePair
- RacePair_{true}:已確定滿足上面三個(gè)條件的RacePair,是RacePair_{cand}的子集
- RacePair_{benign}:屬于預(yù)期內(nèi)的數(shù)據(jù)競(jìng)爭(zhēng)
- RacePair_{harm}:非預(yù)期的數(shù)據(jù)競(jìng)爭(zhēng)
本文給出了一個(gè)具體的例子CVE-2017-2636來(lái)具體說(shuō)明,相關(guān)代碼如圖1所示。
一個(gè)用戶態(tài)程序的兩個(gè)線程A、B,分別調(diào)用了系統(tǒng)函數(shù)ioctl和write,這兩個(gè)系統(tǒng)函數(shù)會(huì)由內(nèi)核來(lái)執(zhí)行。在參數(shù)滿足一定條件的情況下,線程A和B都會(huì)去訪問(wèn)同一個(gè)內(nèi)存地址n_hdlc->tbuf,滿足條件一;其中第440行是對(duì)內(nèi)存的寫,第216行是對(duì)內(nèi)存的讀,滿足條件二;這兩處訪存可以并行執(zhí)行,滿足條件三,因此這是一組RacePair_{true}。而且在這個(gè)例子中,程序的行為會(huì)由于兩條指令執(zhí)行先后的不同而變化。當(dāng)?shù)?16行先于440行執(zhí)行,n_hdlc->buf會(huì)被壓入free_list兩次,在后續(xù)的代碼中,free_list中的元素會(huì)被依次釋放(第269行)。因此最終會(huì)導(dǎo)致double free的問(wèn)題。這一組指令是一組RacePair_{harm}。
圖1 代碼示例
本文把檢測(cè)內(nèi)核中的數(shù)據(jù)競(jìng)爭(zhēng)漏洞拆分成了兩個(gè)設(shè)計(jì)需求。
1. 找到一個(gè)執(zhí)行RacePair_{cand}的程序。即找到一個(gè)多線程的用戶態(tài)程序,每個(gè)線程能夠在內(nèi)核態(tài)分別執(zhí)行到RaceRair_{cand}的指令。
2. 找到一個(gè)線程執(zhí)行序列,使得這RacePair_{cand}的指令能并行執(zhí)行。
需求1把問(wèn)題做了簡(jiǎn)化,并不去考慮并行執(zhí)行的問(wèn)題,就不用考慮線程調(diào)度對(duì)分析的影響。需求2主要是去尋找一個(gè)交錯(cuò)執(zhí)行的線程調(diào)度方案,使得RacePair_{cand}的指令能并行執(zhí)行?,F(xiàn)在的大部分工具都是只針對(duì)上述某一個(gè)需求的,而且都存在一定的局限性。
圖2 工具架構(gòu)
對(duì)于需求1,Google的內(nèi)核Fuzz工具Syzkaller會(huì)隨機(jī)生成一系列的系統(tǒng)調(diào)用,隨機(jī)組合成多線程程序,然后運(yùn)行。但不會(huì)考慮線程調(diào)度對(duì)程序運(yùn)行的影響。而且純隨機(jī)的生成系統(tǒng)調(diào)用使得其效率較低。
對(duì)于需求2,有一些關(guān)注線程交錯(cuò)執(zhí)行隨機(jī)化地工作,比如 SKI和PCT 算法。這些工具對(duì)于給定的一個(gè)程序,會(huì)去探索所有可能的線程交錯(cuò)執(zhí)行序列來(lái)執(zhí)行這個(gè)程序。但其缺點(diǎn)是這些工具的算法隨機(jī)性都比較強(qiáng),效率不高。因?yàn)樵谶@個(gè)研究課題的場(chǎng)景下,我們只需要關(guān)心RacePair_{cand}指令的線程調(diào)度情況,其他指令的執(zhí)行順序不是我們所關(guān)注的。
下面介紹本工作的設(shè)計(jì)思路。
Razzer結(jié)合使用了靜態(tài)分析和動(dòng)態(tài)分析的方法。先通過(guò)靜態(tài)分析得到內(nèi)核中潛在的存在數(shù)據(jù)競(jìng)爭(zhēng)的代碼RacePair_{cand}。之后會(huì)進(jìn)行兩階段的動(dòng)態(tài)分析,第一階段進(jìn)行單線程Fuzz,找到一個(gè)能執(zhí)行到RacePair_{cand}的用戶態(tài)程序。然后按照算法將這個(gè)程序轉(zhuǎn)化為一個(gè)多線程程序(滿足條件一)。第二階段是多線程Fuzz。會(huì)尋找特定的線程交錯(cuò),使得在執(zhí)行多線程程序時(shí)能并行執(zhí)行RacePair_{cand}。如果找到了,則獲得了一個(gè)RacePair_{true}。Razzer還會(huì)檢測(cè)內(nèi)核是否出現(xiàn)了錯(cuò)誤,如果RacePair_{true}在程序后續(xù)執(zhí)行過(guò)程中,導(dǎo)致了內(nèi)核錯(cuò)誤,則得到一個(gè)RacePair_{harm}。整個(gè)工具的架構(gòu)如圖2所示。
圖3 轉(zhuǎn)化算法
以下是一些設(shè)計(jì)上的細(xì)節(jié)問(wèn)題:
1. 靜態(tài)分析:文中使用了Point_to分析來(lái)尋找內(nèi)核中對(duì)同一個(gè)結(jié)構(gòu)體的內(nèi)存訪問(wèn)。但傳統(tǒng)的Point_to分析具有誤報(bào)率高,復(fù)雜度高的缺陷。對(duì)于靜態(tài)分析的結(jié)果,Razzer會(huì)通過(guò)后續(xù)的動(dòng)態(tài)分析來(lái)確認(rèn),以避免誤報(bào)。在性能上,本文基于一定的insight,對(duì)內(nèi)核代碼進(jìn)行了分部分分析,以減小分析的代碼量。
2. 線程調(diào)度:待Fuzz的內(nèi)核運(yùn)行在虛擬化的環(huán)境中,為了控制虛擬CPU的調(diào)度,作者修改了虛擬環(huán)境的Hypervisor,增加了以下功能:為每個(gè)虛擬CPU設(shè)置斷點(diǎn),精確地控制在恢復(fù)執(zhí)行時(shí)哪個(gè)線程的訪存語(yǔ)句先執(zhí)行,不同的執(zhí)行順序會(huì)導(dǎo)致后續(xù)是否存在錯(cuò)誤行為,新的Hypervisor給Razzer提供了準(zhǔn)確控制CPU調(diào)度的能力。
3. 多線程Fuzz:這一步的關(guān)鍵是將單線程Fuzz輸出的一個(gè)單線程程序Pst,轉(zhuǎn)化為一個(gè)多線程版本Pmt。在轉(zhuǎn)換過(guò)程中還會(huì)進(jìn)行一些插樁,與Hypervisor協(xié)作控制程序的調(diào)度。轉(zhuǎn)化算法如圖3所示。
當(dāng)Pmt中的RacePair_{cand}指令都觸發(fā)斷點(diǎn)時(shí),Razzer會(huì)檢查訪存指令的訪問(wèn)地址是否相同,如果相同,則判定為RacePair_{true}??梢宰⒁獾皆赑mt的最后加入了一些隨機(jī)的syscall,這是為了使數(shù)據(jù)競(jìng)爭(zhēng)如果造成了惡性后果,程序會(huì)報(bào)錯(cuò)。當(dāng)檢測(cè)到一個(gè)RacePair_{true},會(huì)把結(jié)果反饋回生成算法,保持前面的代碼不變,只修改后續(xù)隨機(jī)添加的syscall,進(jìn)行新的Fuzz。如果其中某個(gè)Pmt使kernel報(bào)錯(cuò),則是一個(gè)RacePair_{harm}。
Razzer最終發(fā)現(xiàn)了30個(gè)惡性的數(shù)據(jù)競(jìng)爭(zhēng)漏洞。其中有16個(gè)已經(jīng)被確認(rèn)。數(shù)據(jù)競(jìng)爭(zhēng)引起的漏洞往往難以復(fù)現(xiàn),更難以找到原因并修復(fù),Razzer生成的漏洞報(bào)告極大幫助了開發(fā)者對(duì)漏洞進(jìn)行修復(fù)。此外作者還測(cè)量了工具的性能開銷,并與最新的Fuzzing工具進(jìn)行了比較。在可接受的額外開銷下,能夠更有效率地Fuzz這一類漏洞。
Razzer是一個(gè)專門Fuzz內(nèi)核中數(shù)據(jù)競(jìng)爭(zhēng)漏洞的工具,其亮點(diǎn)有兩個(gè):第一是通過(guò)靜態(tài)分析得到一些RacePair_{cand},再用動(dòng)態(tài)分析確認(rèn),即降低誤報(bào)又減少了搜索空間。第二通過(guò)算法與工具的結(jié)合,給Fuzz工具提供了比較準(zhǔn)確的線程并行執(zhí)行狀態(tài),解決了Fuzz多線程程序的重大挑戰(zhàn),值得借鑒。