趙暉
(山東理工大學(xué),山東 淄博255000)
隨著Internet 網(wǎng)絡(luò)規(guī)模的不斷擴(kuò)大,各種軟件硬件的應(yīng)用不斷加入,隨著互聯(lián)網(wǎng)深入政治、經(jīng)濟(jì)、民生乃至軍事的各個(gè)方面,安全漏洞不開避免,安全更是不能忽視的。安全漏洞是指程序代碼、協(xié)議、硬件在功能實(shí)現(xiàn)上的一種缺陷。漏洞一旦被攻擊者發(fā)現(xiàn),就可以使目標(biāo)機(jī)器拒絕服務(wù)(宕機(jī))、在未授權(quán)的情況下執(zhí)行任意代碼、從一個(gè)比較低的權(quán)限獲取更高的權(quán)限,用戶的信息安全不能夠得到保障。
近些年以來,軟件數(shù)量和種類在不斷的增加,軟件的代碼量也在不斷地增加,漏洞挖掘就比較重要了。當(dāng)前,軟件漏洞挖掘技術(shù)正在不斷朝著高度自動(dòng)化、智能化的方向進(jìn)行發(fā)展,以模糊測(cè)試為代表的軟件漏洞挖掘技術(shù)更是成為研究的熱點(diǎn)。
程序在運(yùn)行的時(shí)候,每個(gè)函數(shù)都有一個(gè)函數(shù)運(yùn)行棧,在棧中保存了函數(shù)運(yùn)行時(shí)所使用的臨時(shí)變量、緩沖區(qū)等信息,函數(shù)運(yùn)行棧的范圍通過基地址寄存器(EBP/RBP)跟棧頂寄存器(ESP/RSP)框定。棧上除了臨時(shí)變量外還有函數(shù)運(yùn)行時(shí)緩沖區(qū)、函數(shù)運(yùn)行時(shí)參數(shù),在他們中間還有一個(gè)值是返回地址,這個(gè)地址一般表示為該函數(shù)執(zhí)行完以后要執(zhí)行的匯編指令的地址,如果這個(gè)值被攻擊者惡意覆蓋以后,就可以控制指令指令指針寄存器(EIP/RIP),就可以控制代碼流,就可以做到執(zhí)行任意代碼。在使用一些輸入函數(shù),比如gets()、read()、scanf()等函數(shù)時(shí),如果沒有對(duì)輸入字符串長度進(jìn)行驗(yàn)證,就會(huì)發(fā)生溢出。
在計(jì)算機(jī)中,堆與棧雖然都是用來保存數(shù)據(jù)的內(nèi)存區(qū)域,但是這兩個(gè)內(nèi)存區(qū)域區(qū)別很大而且??臻g的生命周期是在函數(shù)運(yùn)行時(shí),但是堆內(nèi)存空間可以貫穿在整個(gè)程序運(yùn)行周期內(nèi)。假設(shè)程序申請(qǐng)了0x10 的堆內(nèi)存空間,但是輸入0x50 長度的字符串,因此可以導(dǎo)致溢出。但是堆內(nèi)存空間中沒有相關(guān)數(shù)據(jù)、函數(shù)指針,因此該例子并不能劫持程序流,也就意味著程序可能會(huì)正常退出,一般方法無法捕獲到該漏洞。
單獨(dú)的整數(shù)溢出漏洞,但是整數(shù)溢出漏洞,往往會(huì)產(chǎn)生堆棧溢出漏洞,整數(shù)溢出漏洞的產(chǎn)生往往是對(duì)變量定義不嚴(yán)格導(dǎo)致。
漏洞挖掘技術(shù)主要有以下幾種方法:(1)靜態(tài)審計(jì),這種方法十分依賴經(jīng)驗(yàn),而且需要耗費(fèi)大量的精力;(2)靜態(tài)分析,這種技術(shù)不用運(yùn)行目標(biāo)程序就可以發(fā)現(xiàn)漏洞,但是存在較高的誤報(bào)率;(3)污點(diǎn)分析跟符號(hào)執(zhí)行,這兩項(xiàng)技術(shù)需要較大的性能開銷,而且程序越大,對(duì)性能的開銷越大,因此在工業(yè)中的應(yīng)用存在較大的局限性;(4)模糊測(cè)試,通過傳入畸形數(shù)據(jù),檢測(cè)程序行為,從而發(fā)現(xiàn)漏洞。
因?yàn)槟:郎y(cè)試對(duì)系統(tǒng)性能開銷低效率高,因此在工業(yè)界應(yīng)用廣泛。
Fuzzing 技術(shù)是一種自動(dòng)或者半自動(dòng)化的軟件漏洞挖掘技術(shù),通過向目標(biāo)程序(文件處理、協(xié)議處理、API)注入畸形數(shù)據(jù)或者其他非預(yù)期輸入,程序在解析這些文件或者數(shù)據(jù)時(shí),因?yàn)轭A(yù)定義類型的問題,可能會(huì)導(dǎo)致解析結(jié)果與預(yù)期不一致,但是仍能通過一些程序的條件判斷,因此就會(huì)出現(xiàn)異常的行為、輸出,因此可以發(fā)現(xiàn)漏洞,與手工分析的區(qū)別是,fuzz 技術(shù)是一種半自動(dòng)化技術(shù),只需要用戶指定被測(cè)程序,并提供輸入樣本就可以自動(dòng)化的執(zhí)行目標(biāo)程序,并將樣本輸入到程序中,效率非常高。
主流fuzz 平臺(tái)設(shè)計(jì)一般有以下幾種類型:
基于變異:根據(jù)已知數(shù)據(jù)樣本通過隨機(jī)數(shù)據(jù)變異的方法進(jìn)行fuzz。例如AFL。
基于生成:根據(jù)已知協(xié)議接口或規(guī)范進(jìn)行建模生成測(cè)試用例。例如Syzkaller。
基于機(jī)器學(xué)習(xí):神經(jīng)網(wǎng)絡(luò)輸入的隨機(jī)突變由覆蓋度量指導(dǎo)。
基于符號(hào)執(zhí)行:將輸入進(jìn)行符號(hào)化,對(duì)于所有可能的路徑進(jìn)行約束求解,從而達(dá)到更好的代碼覆蓋率,增大發(fā)現(xiàn)漏洞的概率。但是符號(hào)執(zhí)行雖然在理論上可行,但是在實(shí)際系統(tǒng)中存在大量的路徑分支,路徑爆炸的問題很難解決。
插樁模塊:依據(jù)編譯原理,在編譯時(shí),對(duì)目標(biāo)程序基本塊進(jìn)行插裝,以便目標(biāo)程序向fuzz 系統(tǒng)反饋樣本能夠觸發(fā)的路徑信息,程序運(yùn)行狀態(tài)信息,以及程序退出信息。
監(jiān)控模塊:fuzz 程序根據(jù)目標(biāo)程序反饋的路徑信息判斷被測(cè)樣本是否有效,以及可以出發(fā)的路徑數(shù)量;根據(jù)反饋的程序退出信息,判斷程序是正常退出還是因?yàn)橛|發(fā)了內(nèi)存破壞漏洞等原因?qū)е峦顺觥?/p>
變異模塊:fuzz 程序結(jié)合目標(biāo)程序反饋的路徑信息,對(duì)文件的關(guān)鍵字段進(jìn)行變異,以增加代碼覆蓋率。
forkserver 模塊:該模塊主要是為了啟動(dòng)被fuzz 程序的子進(jìn)程,通過forkserver 模型,可以減少系統(tǒng)性能與內(nèi)存開銷。
插樁模塊,將實(shí)現(xiàn)特定功能的匯編指令插入到程序的每一個(gè)基本塊中;路徑信息反饋模塊通過插樁模塊插入到被fuzz 程序中,并通過共享內(nèi)存與主fuzz 進(jìn)程通信;變異模塊主要是通過啟發(fā)式算法檢測(cè)出文件token,其余的策略基于效率考慮的數(shù)據(jù)變異,主要是為了讓程序在解析相關(guān)字段的時(shí)候出現(xiàn)非預(yù)期的值導(dǎo)致崩潰。
其工作流程是首先將樣本讀入進(jìn)內(nèi)存,通過forkserver 框架通過fork 系統(tǒng)調(diào)用產(chǎn)生一個(gè)被fuzz 的子進(jìn)程,并將測(cè)試樣本讀入程序,fuzzer 記錄下程序運(yùn)行時(shí)相關(guān)信息,比如代碼覆蓋率等。然后捕獲程序退出時(shí)發(fā)出的信號(hào),并根據(jù)此信號(hào)判斷程序是否異常退出,然后根據(jù)程序運(yùn)行時(shí)相關(guān)信息對(duì)測(cè)試樣本進(jìn)行變異,然后繼續(xù)讀入內(nèi)存?zhèn)鬟f給被fuzz 程序。如果產(chǎn)生crash,則將可以導(dǎo)致crash 的樣本放入crash 文件夾,供crash 監(jiān)控進(jìn)程讀取。
首先下載開源軟件,并編譯生成可執(zhí)行文件。然后運(yùn)行軟件查看功能。
本次選取的軟件名為pdfcrack,是一款linux 下開源的pdf文件密碼破解工具。
第一步:首先將建立fuzz 所需要的輸入輸出目錄,并將種子文件放入輸入文件夾中。
第二步:編寫配置文件,主要參數(shù)如下:
(1)輸入目錄
(2)輸出目錄
(3)Crashes 目錄
(4)文件輸入方式
(5)被fuzz 程序路徑
第三步:執(zhí)行fuzz 框架主程序,并讀入配置文件
幾分鐘之后,便發(fā)現(xiàn)了crash,如下圖1 所示。
圖1 fuzz 運(yùn)行
并且崩潰日志通過云端推送到了微信上面。
圖2 崩潰位置
結(jié)合軟件崩潰位置,使用gdb 調(diào)試器可以發(fā)現(xiàn)崩潰位置,如圖2 所示。
通過調(diào)試起可以發(fā)現(xiàn)這個(gè)地方s_handler 的值為0,程序試圖往一個(gè)0 地址進(jìn)行寫入,但是操作系統(tǒng)是禁止這一行為的,所以存在一個(gè)0 地址解引用漏洞。
通過gdb 調(diào)試器進(jìn)行函數(shù)調(diào)用回溯,發(fā)現(xiàn)該漏洞存在于函數(shù)loadstate 中,如圖3 所示。
圖3 調(diào)用回溯
通過對(duì)該函數(shù)代碼進(jìn)行分析,對(duì)e->s_handler 的復(fù)制存在于下面的代碼中,如圖4 所示。
圖4 漏洞代碼
函數(shù)對(duì)判斷l(xiāng)en 的大小,只要len 大于0 并且len 小于256就會(huì)通過malloc 分配一段內(nèi)存,并將堆內(nèi)存指針賦給e->s_handler,但是這里沒有考慮len 等于0 的情況,當(dāng)len 等于0,就會(huì)直接跳過申請(qǐng)內(nèi)存,然后直接對(duì)e->s_handler 進(jìn)行寫操作,并沒有判斷是否等于0 的情況,因此造成空指針解引用,如圖5 所示。
圖5 崩潰位置
因?yàn)閘en 等于0,因此不會(huì)進(jìn)入for 循環(huán),所以崩潰發(fā)生在最后一行。
本文以設(shè)計(jì)基于反饋驅(qū)動(dòng)的fuzz 工具設(shè)計(jì)為目的,通過一系列的變異策略,可以在軟件中發(fā)現(xiàn)更多的漏洞,并且通過實(shí)際的開源軟件進(jìn)行測(cè)試,證實(shí)該fuzz 工具確實(shí)有發(fā)現(xiàn)軟件漏洞的能力。該fuzz 工具可以融合進(jìn)devsecops 中,開發(fā)者自己提供軟件編譯,然后進(jìn)行測(cè)試,這樣做的好處是開發(fā)者更懂自己的程序功能,因此可以提供更好的測(cè)試樣本,而且這樣可以減少軟件中潛在的漏洞。