linux 符号执行,[原创]符号执行Symcc与模糊测试AFL结合实践
上個(gè)月末無(wú)聊的劃水時(shí)間段內(nèi),在推上看到有人發(fā)了一篇關(guān)于如何結(jié)合去年新發(fā)布的符號(hào)執(zhí)行Symcc與模糊測(cè)試引擎AFL,以提升Fuzz效率的視頻貼。打開(kāi)這個(gè)鏈接后才發(fā)現(xiàn)是個(gè)賣課的,emmm.... , 看價(jià)格£1499果斷打擾了,果然沒(méi)錢的都不配學(xué)安全嗎23333;原技術(shù)文章看來(lái)是看不到了,只能靠公開(kāi)的一段視頻還原操作。
背景介紹
雖然有很多大佬肯定已經(jīng)十分熟悉模糊測(cè)試和符號(hào)執(zhí)行了,還是簡(jiǎn)單介紹一下背景知識(shí),這樣更有連貫性。
模糊測(cè)試 AFL
模糊測(cè)試(fuzz testing, fuzzing)是一種軟件測(cè)試技術(shù)。 其核心思想是將自動(dòng)或半自動(dòng)生成的隨機(jī)數(shù)據(jù)輸入到一個(gè)程序中,并監(jiān)視程序異常,如崩潰,斷言(assertion)失敗,以發(fā)現(xiàn)可能的程序錯(cuò)誤,比如內(nèi)存泄漏。 模糊測(cè)試常常用于檢測(cè)軟件或計(jì)算機(jī)系統(tǒng)的安全漏洞。
模糊測(cè)試誕生于1988年秋季的一個(gè)黑暗暴風(fēng)雨之夜 [Takanen et al, 2008.]。巴頓·米勒教授坐在麥迪遜威斯康星州的公寓里,通過(guò)一條1200波特的電話線連接到他所屬大學(xué)的計(jì)算機(jī)。陣陣的雷暴在線路上造成噪音,這些噪音又導(dǎo)致兩端的UNIX命令獲得錯(cuò)誤的輸入,并導(dǎo)致崩潰。頻繁的崩潰使他感到驚訝—我們編寫的程序不是應(yīng)該十分強(qiáng)大嗎?作為一名科學(xué)家,他想探究該問(wèn)題的嚴(yán)重程度及其原因。因此,他為威斯康星大學(xué)麥迪遜分校的學(xué)生編寫了一個(gè)編程練習(xí),而該練習(xí)將使他的學(xué)生創(chuàng)建第一個(gè)模糊測(cè)試器。
這項(xiàng)作業(yè)的原文描述是這樣的:
The goal of this project is to evaluate the robustness of various UNIX utility programs, given an unpredictable input stream. [...] First, you will build a fuzz generator. This is a program that will output a random character stream. Second, you will take the fuzz generator and use it to attack as many UNIX utilities as possible, with the goal of trying to break them.
該項(xiàng)目的目標(biāo)是在給定不可預(yù)測(cè)的輸入流的情況下評(píng)估各種UNIX實(shí)用程序的健壯性。[...]首先,您將構(gòu)建一個(gè)模糊發(fā)生器。這是一個(gè)將輸出隨機(jī)字符流的程序。其次,您將使用模糊發(fā)生器,并使用它來(lái)攻擊盡可能多的UNIX實(shí)用程序,以試圖破壞它們。
這個(gè)作業(yè)在不經(jīng)意間抓住了模糊測(cè)試的本質(zhì):創(chuàng)建隨機(jī)的輸入,并持續(xù)性觀察它是否會(huì)破壞目標(biāo)應(yīng)用程序,理論上只要運(yùn)行足夠長(zhǎng)的時(shí)間,我們就會(huì)看到錯(cuò)誤的發(fā)生。
AFL(american fuzzy lop)最初由Micha? Zalewski開(kāi)發(fā),和libFuzzer等一樣是基于覆蓋引導(dǎo)(Coverage-guided)的模糊測(cè)試工具,它通過(guò)記錄輸入樣本的代碼覆蓋率,從而調(diào)整輸入樣本以提高覆蓋率,增加發(fā)現(xiàn)漏洞的概率。其工作流程大致如下:
從源碼編譯程序時(shí)進(jìn)行插樁,以記錄代碼覆蓋率(Code Coverage)
選擇一些輸入文件,作為初始測(cè)試集加入輸入隊(duì)列(queue)
將隊(duì)列中的文件按一定的策略進(jìn)行“突變”
如果經(jīng)過(guò)變異文件更新了覆蓋范圍,則將其保留添加到隊(duì)列中
上述過(guò)程會(huì)一直循環(huán)進(jìn)行,期間觸發(fā)了crash的文件會(huì)被記錄下來(lái)
符號(hào)執(zhí)行 Symcc
符號(hào)執(zhí)行簡(jiǎn)單來(lái)說(shuō)是在目標(biāo)程序的執(zhí)行過(guò)程中跟蹤中間值是如何計(jì)算的,每一個(gè)中間值都可以表示為程序輸入的一個(gè)公式。在任何點(diǎn),系統(tǒng)都會(huì)使用這個(gè)公式查看這個(gè)點(diǎn)是否可達(dá),這個(gè)指針是否為空等。如果答案是確定的,那么符號(hào)執(zhí)行引擎將會(huì)提供測(cè)試用例,一個(gè)新的輸入例子來(lái)觸發(fā)對(duì)應(yīng)的行為。所以符號(hào)執(zhí)行可以被方便的用來(lái)探測(cè)程序路徑以及觸發(fā)bug。
新的符號(hào)執(zhí)行Symcc發(fā)表于去年頂會(huì)USENIX’20,論文名稱為Symbolic execution with SYMCC: Don’t interpret, compile!
那么Symcc和它的前輩們又有什么不同呢?論文作者Aurélien Francillon認(rèn)為傳統(tǒng)符號(hào)執(zhí)行主要分為兩類:IR-based和IR-less。
IR-based是指無(wú)論測(cè)試對(duì)象如何,先把目標(biāo)的二進(jìn)制程序給轉(zhuǎn)換到IR層(中間表達(dá)形式)再進(jìn)行抽象解釋,其缺點(diǎn)是特別容易路徑爆炸。常見(jiàn)用此方法的符號(hào)執(zhí)行有angr、KLEE和Mayhem,主要過(guò)程如下圖所示:
IR-less是指符號(hào)執(zhí)行引擎通過(guò)動(dòng)態(tài)插樁技術(shù)(利用PIN插樁框架等)先對(duì)二進(jìn)制程序插樁,執(zhí)行部分指令后再構(gòu)造符號(hào)表達(dá)式。其優(yōu)點(diǎn)是快,缺點(diǎn)是插入的代碼函數(shù)可能無(wú)法生成正確的符號(hào)表達(dá)式,且較依賴于指令集。常見(jiàn)用此方法的符號(hào)執(zhí)行有Triton、QSYM、SAGE和Driller,主要過(guò)程如下圖所示:
作者提出的SymCC不同點(diǎn)在于,直接在編譯期就開(kāi)始在生成的IR上植入符號(hào)執(zhí)行相關(guān)代碼,進(jìn)一步提升性能;其流程大致如下:
開(kāi)始結(jié)合
整體的結(jié)合流程沒(méi)什么新花樣,基本是按照 安裝各種包環(huán)境—> 編譯簡(jiǎn)單的后端/功能更強(qiáng)的qsym后端 —> 用symcc編譯llvm的libcxx —> 開(kāi)始嘗試fuzz 的過(guò)程循序漸進(jìn)。
安裝各種環(huán)境
先安裝各種包和依賴:
這里的LLVM 要求是 8, 9, 10 或者11 ,C++編譯器要支持 C++17。實(shí)際安裝過(guò)程中有些unbuntu版本可能無(wú)法直接apt-get,使用llvm官方https://apt.llvm.org/ 的包支持。
隨后安裝Z3,要求版本號(hào)大于4.5
這里有點(diǎn)小坑,用該方式安裝的z3可能在后面編譯時(shí)llvm無(wú)法找到路徑。如果報(bào)此類似錯(cuò)誤的,該步可用cmake的Ninja進(jìn)行編譯,指路鏈接:https://github.com/Z3Prover/z3/blob/master/README-CMake.md
然后安裝afl,沒(méi)什么可說(shuō)的先安原版
下載symcc的源碼做好之后編譯backend的準(zhǔn)備
編譯backend
symcc帶了兩種后端,一種是功能簡(jiǎn)單(simple)的;另一種是qsym的后端,與前者相比功能性更強(qiáng),我們兩種都編譯一下。在配置構(gòu)建時(shí),選項(xiàng)都將通過(guò)“ -D”傳遞給CMake,常見(jiàn)選項(xiàng)如下表所示:
選項(xiàng)
作用
- QSYM_BACKEND=ON/OFF (default OFF)
是否編譯QSYM后端,若否即為編譯simple后端;每次執(zhí)行時(shí)可以使用LD_LIBRARY_PATH在后端之間切換
- TARGET_32BIT=ON/OFF (default OFF)
啟用64位主機(jī)對(duì)32位編譯的支持
- LLVM_DIR/LLVM_32BIT_DIR (default empty)
提供編譯時(shí)llvm的位置
- Z3_DIR/Z3_32BIT_DIR (default empty)
提供編譯時(shí)Z3的位置
- Z3_TRUST_SYSTEM_VERSION (default OFF)
信任系統(tǒng)版本的Z3,不檢查Z3版本的兼容性;如果Z3的安裝版本太舊,則可能發(fā)生編譯錯(cuò)誤。
首先編譯簡(jiǎn)單的后端,這里的LLVM_DIR指向你所用版本llvm位置
后面在ninja check時(shí)會(huì)有8個(gè)錯(cuò)誤,不影響后面的正常執(zhí)行。(參考視頻的編譯過(guò)程中也有錯(cuò)誤)
下面編譯qsym的后端,與前者相比區(qū)別就是把DQSYM_BACKEND改成ON
check時(shí)也會(huì)有一些錯(cuò)誤,問(wèn)題不大。
用symcc編譯llvm的libcxx
對(duì)于更加復(fù)雜的C++代碼,symcc提供了兩種解決方法。一種是使用系統(tǒng)提供的C ++標(biāo)準(zhǔn)庫(kù)。這是最簡(jiǎn)單的不需要額外的編譯,但是它有一個(gè)重要的缺點(diǎn):會(huì)影響符號(hào)執(zhí)行的過(guò)程。另一種方法是自己編譯一個(gè)檢測(cè)的C ++標(biāo)準(zhǔn)庫(kù),這樣就可以通過(guò)庫(kù)跟蹤數(shù)據(jù),但這需要構(gòu)建庫(kù)并針對(duì)它編譯所有代碼。
建立C ++標(biāo)準(zhǔn)庫(kù)是一項(xiàng)一次性的工作,建一次后可以在所有后續(xù)的C ++編譯中使用,每當(dāng)我們使用libc++就會(huì)自動(dòng)使用我們編譯的llvm標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)。
編譯過(guò)程如下:
請(qǐng)自己手動(dòng)把$BASE指向之前準(zhǔn)備工作的文件位置
開(kāi)始嘗試Fuzz!
這里我們準(zhǔn)備一個(gè)特殊的苛刻例子
根據(jù)這段源碼,我們可以判斷出當(dāng)且僅當(dāng)文件輸入為0xdeadbeef 時(shí),return (int)(20 / t1)會(huì)出現(xiàn)除零錯(cuò)誤,而該例子單純使用afl是難以發(fā)現(xiàn)錯(cuò)誤的。
我們分別使用qsym編譯出的后端symcc和afl對(duì)其進(jìn)行編譯,并且創(chuàng)建一個(gè)樣例AAAAAAA
之后就開(kāi)始運(yùn)行吧,我們使用afl的并行模式
這里symcc_fuzzing_helper的參數(shù)中-o指向afl的out目錄,-a指向要輔助的afl進(jìn)程名字,如上所示這里我就是fuzz2.
symcc_fuzzing_helper其實(shí)就是在使用afl生產(chǎn)的testcase進(jìn)行符號(hào)執(zhí)行,如果它認(rèn)為樣例有趣就會(huì)生成新的,并將它傳送到正在執(zhí)行的afl隊(duì)列里,afl就能夠使用該新生成樣例進(jìn)行測(cè)試,這一流程輔助了afl能夠到達(dá)一些之前無(wú)法到達(dá)的路徑。
這里有個(gè)小坑,單純執(zhí)行afl時(shí)afl-fuzz命令后面的--可帶也可不帶,但是symcc_fuzzing_helper是通過(guò)檢測(cè)afl-fuzz里--后面命令執(zhí)行的,所以如果我們?cè)赼fl-fuzz后習(xí)慣性不加--就可能導(dǎo)致symcc無(wú)有效的輸出結(jié)果。
一切準(zhǔn)備就緒,開(kāi)始運(yùn)行吧!
我們驚喜的發(fā)現(xiàn)單純fuzz難以找到的漏洞,在symcc_fuzzing_helper的協(xié)助下,直接就找到了crash。
簡(jiǎn)單分析一下,用Hexdump看到crash真的是0xdeadbeef,symcc輔助AFL找到了使目標(biāo)程序崩潰的testcase。
總結(jié)
使用符號(hào)執(zhí)行Symcc與模糊測(cè)試引擎Afl的簡(jiǎn)單驗(yàn)證成功了,之后可以就試一試現(xiàn)實(shí)中的一些項(xiàng)目,比如論文中提的Tcpdump之類的;還可以考慮將Symcc與其他模糊測(cè)試引擎相結(jié)合,比如Afl++,在各種算法加持下的Afl++再結(jié)合符號(hào)執(zhí)行可能會(huì)有更好的效果。
最后特別感謝Symcc發(fā)明人和論文的原作者 Aurélien Francillon 與 ADALogics 的安全研究員David Korczynski,它們?cè)谕铺厣蠋椭宋液芏?#xff0c;同時(shí)感謝Discord的Fuzzing板塊討論讓我有興趣進(jìn)行各種新的嘗試,我仍有很多需要學(xué)習(xí)的地方。
參考資料
帶你搞懂符號(hào)執(zhí)行的前世今生與最近技術(shù)
https://www.anquanke.com/post/id/231413
G.O.S.S.I.P 學(xué)術(shù)論文推薦
https://wemp.app/posts/40a16228-7a86-4985-b7c2-b3507e3fc161
Symcc源碼
https://github.com/eurecom-s3/symcc/
入門afl
https://i-m.dev/posts/20191001-225746.html
總結(jié)
以上是生活随笔為你收集整理的linux 符号执行,[原创]符号执行Symcc与模糊测试AFL结合实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C++打卡4-宝箱密码
- 下一篇: 本科计算机 在职研究生想读英语,想读在职