1. 程式人生 > 實用技巧 >Angora: Efficient Fuzzing by Principled Search

Angora: Efficient Fuzzing by Principled Search

目錄

1、論文介紹

Fuzzing是一種流行的發現軟體錯誤的技術。

然而,最先進的fuzzer的效能還有很多需要改進的地方。基於符號執行的fuzzer產生高質量的輸入,但執行速度慢;而基於隨機變異的fuzzer執行速度快,但難以產生高質量的輸入。該論文提出了一種新的基於變異的Angora,它的效能遠遠優於最新的fuzzer。

Angora的主要目標是通過解決路徑約束而不需要符號執行來增加分支覆蓋率。為了有效地解決路徑約束問題,論文引入了幾個關鍵技術:可擴充套件的位元組級汙點跟蹤、上下文相關的分支計數、基於梯度下降的搜尋和輸入長度探索。

2、基礎知識

  • 軟體脆弱性研究中主要有三種形式:模糊測試、汙點分析、符號執行。

    • 模糊測試(Fuzzing)

      一般來說,模糊測試通過生成大量的隨機測試用例,並以這些測試用例為輸入執行被測程式,希望能到導致程式異常或崩潰,從而捕捉到導致程式異常或崩潰的錯誤或安全漏洞。模糊測試之所以能夠受到軟體測試業界的青睞,是因為它具有以下的優點:1)

      模糊測試可以針對任意輸入的程式,可在程式原始碼或可執行位元組碼上進行;2)模糊測試針對實際可執行的被測程式,不會出現靜態測試技術中的誤報問題;3)模糊測試不需要進行大量的準備工作,只需要提供被測程式及其初始檔案或符合規範的輸入,便可進行模糊測試用例生成,對軟體進行安全漏洞檢測;4)模糊測試易於自動化實現。在模糊測試技術的眾多優點中,易於自動化實現是其能夠被人們廣泛關注的主要優點之一。

    • 符號執行

      符號執行是一種資訊流分析技術,它在程式執行過程中以符號輸入代替實際輸入,將程式變數符號化,並在分析中通過插樁(Instrumenation)不斷收集路徑約束條件(Path Condition),通過約束求解器

      (Solver)生成測試用例以發現軟體存在的脆弱性。

      插樁是指通過注入插樁程式碼,來分析二進位制應用程式在執行時的行為的方法。

      符號執行的最大問題是,由於軟體分支數目和迴圈次數巨大,存在著天文數字的執行路徑,導致符號執行在實際應用中具有潛在的路徑爆炸問題,這已經成為符號執行應用的最大瓶頸。

    • 汙點分析

      汙點分析是檢測蠕蟲攻擊和自動提取行為特徵的有效方法。該方法將一切來自於非信任源的資料標記為“汙染”,對“汙染”資料進行追蹤,所有對“汙染”資料的運算操作結果均會標記為“汙染操作”,然後根據各種安全策略對“汙染操作”進行分析,凡違反安全策略的“汙染操作”都會發出警報,以此達到發現軟體脆弱性的目的。

  • Fuzzing技術分類(按測試用例來分)

    • 基於隨機變異(Mutation-based)

      是將一個(或一組)正常的符合規範(或協議)的初始輸入檔案作為初始種子(seed),通過對種子進行隨機變異,生成大量的測試用例,對軟體進行安全漏洞檢測,是目前廣泛使用的一種模糊測試技術。但基於變異的模糊測試用例生成對種子的依賴性較強,不同的初始種子,其安全漏洞檢測效果也大不一樣。因此,如何選取合適的種子,進行基於變異的模糊測試用例生成,是提高模糊測試技術安全漏洞檢測能力的一個關鍵問題。

    • 基於模板(Generation-based)

      不需要種子檔案,依賴於安全人員結合自己的知識,給出輸入資料的模板,構造豐富的輸入測試資料。

  • 覆蓋率

    If( a > 2)
        a=2;
    if (b > 2)
        b=2;
    else
        a=3;b=4;
    
    • 路徑覆蓋( 覆蓋程式中所有可能的路徑 ): 4個數據集(a=3,b=3 ; a=1, b=3 ; a=3,b=2 ; a=1,b=2)
    • 分支覆蓋( 使得程式中每個判斷的取真分支和取假分支至少經歷一次 ): 4個數據集(a=3,b=3 ; a=1, b=3 ; a=3,b=2 ; a=1,b=2)
    • 程式碼行覆蓋: 2個數據集(a=3,b=3 ; a=3,b=2)
  • AFL介紹

    灰盒模糊測試介於白盒模糊測試和黑盒模糊測試之間,是一種針對程式可執行程式碼進行的模糊測試方法。灰盒模糊測試是基於二進位制插樁而不是原始碼分析上。基於覆蓋的灰盒模糊測試(Coverage-based grey-box fuzzing)試圖在生成大量隨機測試用例的生成過程中,更有效地進行路徑探索,增加程式碼覆蓋率,因此,基於覆蓋的灰盒模糊測試已成為目前檢測軟體安全漏洞的一種有效測試方法。

    AInerican Fuzzy Lop (AFL) 是目前使用廣泛的一種基於覆蓋的灰盒模糊測試工具。它通過對被測程式的可執行程式碼進行插樁,跟蹤記錄測試用例的覆蓋情況;以輸入(種子)的二進位制位元組、雙位元組、四位元組為單位,進行隨機測試用例的生成,以覆蓋新基本塊為指導,進行種子隊列的更新,生成大量的隨機測試用例,對被測程式進行測試,從而捕捉到導致程式異常或崩潰的錯誤或安全漏洞。但AFL難以發現隱藏在被測程式迴圈或巢狀條件語句深處的錯誤和安全漏洞。

3、論文創新點及結果

論文創新點

AFL和其他類似的fuzzer使用分支覆蓋作為度量。不同的是,Angora通過解決路徑約束而不使用符號執行來探索程式的狀態。Angora跟蹤未探明的分支,並試圖解決這些分支的路徑限制。 有效地解決路徑約束的技術如下:

  • 1)用上下文敏感的方式進行分支覆蓋

    AFL所使用的上下文不敏感的分支處理,不能識別相同分支潛在的不同內部狀態。相較於AFL只將分支的始塊和終塊作為分支特徵,作者在此基礎上又加入了上下文特徵

    如下圖所示,f中的x引數是輸入的一部分,而trigger所控制的分支,受 "是否第一次呼叫f" 這個上下文環境影響。該分支是一種內部狀態。在第一次執行期間,程式接受輸入10。當它在第19行呼叫 \(f()\) 時,它在第4行執行\(true\)分支。稍後,當它在第21行呼叫 \(f()\) 時,它在第10行執行\(false\)分支。由於AFL對分支的定義是上下文不敏感的,它認為兩個分支都已執行。後來,當程式接受一個新的輸入01時,AFL認為這個輸入不會觸發新的內部狀態,因為第4行和第10行的分支都是在前一次執行中執行的。但事實上,這個新輸入觸發了一個新的內部狀態,因為當輸入input[2]=1時,它將導致第6行崩潰。

    Angora用一個三元組\((l_{prev},l_{cur},context)\)定義一個分支,其中 \(context\)\(h(stack)\)\(stack\)包含了呼叫棧的狀態。利用對棧進行 \(hash\) 的方法,來獲取上下文。為了避免產生過多的獨立分支,作者使用的hash函式會異或呼叫棧。即 \(h(stack)=⊕cs∈stackID(cs)\)

  • 2)位元組級的汙點追蹤

    汙點跟蹤的代價是昂貴的,特別是跟蹤的位元組不是相互獨立的情況下,所以AFL沒有使用這個。

    在汙點追蹤中,作者使用某一位元組在輸入中的偏移作為taint lable。為了減小taint lable的大小,該方案維護一個表,這個表的索引是taint lable,而它的值是一個二叉樹的結點,通過回溯二叉樹到根節點的路徑,可以得到一個由0和1組成的bitmap,這個bitmap所代表的資訊是相對於這個taint label對應的偏移而後第某位個位元組的汙染情況。通過這種汙點分析,可以得到輸入的使用情況,繼而判斷各個位元組作為變數的長度,以便進一步的進行資料突變等操作。

  • 3)使用梯度下降來求解條件語句

    位元組級別的汙點跟蹤輸入中的哪些位元組傳入條件表示式中進行計算。但是如何變異這些輸入來探索未探索到的區域? 目前很多fuzzer都是隨機地變異輸入或使用粗糙的啟發式方法。 如果使用符號執行的方法,成本太高。

    作者把探索區域的問題視為搜尋問題,選擇使用梯度下降的求解的方法來獲得滿足條件語句的值。假設有一個黑盒函式\(f(x)\),其中\(x\)是一個值的向量。對於\(f(x)\)有三種約束:

    1. \(f(x)<0\)
    2. \(f(x)≤0\)
    3. \(f(x)==0\)

    然後一些比較就可以轉換為上述三種約束了,如下表,作者將條件判斷語句轉換成誤差函式,而後利用梯度下降的方法求解誤差函式,進一步的可實現對條件語句的滿足。

    正好本學期金鑫老師的機器學習課程講解了梯度下降演算法,所以這部分理解起來不太困難。在機器學習中,梯度下降常常會陷入區域性最優, 但是在fuzzing中不存在這個問題。 若一個約束是\(f(x)< 0\),我們只需要找到滿足這個約束的 \(x\) 就行了,而不需要找到 \(f(x)\) 的全域性最小值。

    在神經網路中,求偏導可以得到\(f(x)\)的解析形式,但是在fuzzing時\(f(x)\)是黑盒的。對於這個問題採取使用數值近似的方法:

    對於這個問題採取使用數值近似的方法:
    \(\frac{\partial f(x)}{\partial x_i}=\frac{f(x+\delta v_i)-f(x)}{\delta}\) 其中\(v_i\) 是第 \(i\) 維的單元向量

    理論上梯度下降可以解決任何約束,在實際中,梯度下降的速度依賴於數學函式的複雜度:

    • 如果\(f(x)\)是單調或者凸函式,梯度下降可以很快的找到解,即使\(f(x)\)是一個複雜的解析形式
    • 若區域性最小值滿足約束,那麼找到解也是很快的
    • 若區域性最優找不到解,Angora會隨機取樣到其他的\(x′\),然後重新進行梯度下降來找到另一個滿足約束的區域性最優

    下圖演算法5即搜尋演算法,每次迭代從輸入 \(x\) 開始,然後計算\(f(x)\)\(x\)處的梯度\(\nabla_x f(x)\) ,然後進行梯度下降操作,\(x\leftarrow x - \epsilon\nabla_x f(x)\),其中\(\epsilon\)是學習率。

  • 4)變數大小與型別判斷

    變數在汙點分析時已經判斷過大小了,而型別(是否帶符號)可以通過語義判斷。當明確了變數的大小和型別後,基於條件語句構造的誤差函式中變數的定義會更準確。

  • 5)探測輸入的長度

    輸入太短沒有效果,太大會爆記憶體。因此需要得出合適的輸入長度。作者認為只有當增加長度能得到新分支時,才增加長度,也就是使得讀取長度儘可能滿足程式的需要。

    具體做法是,在汙點跟蹤時,Angora把read相關函式呼叫的目標記憶體地址和相關的位元組偏移相關聯,同時也記錄read呼叫的返回值,如果返回值在條件語句中使用且約束不滿足,則Angora增加輸入長度。

結果

4、復現

Angora的安裝

  • 下載 Angora ,其路徑為/home/zhangtuoning/ztn/Angora​,其百度雲連結如下。

    git clone https://github.com/AngoraFuzzer/Angora
    

    連結:https://pan.baidu.com/s/1y9D7aS9YW4Y4_z2m811uMw
    提取碼:951g

  • 安裝cmake、cargo,命令apt-get install cmakeapt-get install cargo

  • 安裝LLVM,在Angora目錄下新建資料夾llvm,執行:

    PREFIX=/home/zhangtuoning/ztn/Angora/llvm  ./build/install_llvm.sh
    

    由於網路原因可能會一直卡在 wget下載llvm的壓縮包的地方,可以點選該連結手動下載,將下載後的壓縮包放在llvm資料夾下,再將install_llvm.sh中的wget那一行語句刪除。llvm的壓縮包百度雲連結如下。

    連結:https://pan.baidu.com/s/1FPyo8dzKsvG2BjBYL77F9A
    提取碼:cjw1

    按照他的提示新增環境變數,命令如下:

    #export PATH=/home/zhangtuoning/ztn/Angora/llvm/clang+llvm/bin:$PATH
    #export LD_LIBRARY_PATH=/home/zhangtuoning/ztn/Angora/llvm/clang+llvm/lib:$LD_LIBRARY_PATH
    

    附tar.xz解壓方法

    # xz -d XXX.tar.xz
    # tar -xvf XXX.tar
    
  • 安裝Rust

    參考 https://rustup.rs/ 給出的命令curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh,不能一步到位的話參考 https://blog.csdn.net/sinat_37954989/article/details/82913413 中的方法手動安裝。

  • 安裝Angora,在Angora目錄下執行 ./build/build.sh即可。

  • CPU設定,echo core | sudo tee /proc/sys/kernel/core_pattern

  • 測試Angora,在Angora/tests/目錄下執行,./test.sh mini

LAVA-M測試集的安裝

  • LAVA是由Brendan Dolan-Gavitt等人提出的用於在程式中插入bug的技術方法,其相關論文《LAVA: Large-scale Automated Vulnerability Addition》發表在了2016年的S&P上。

    通過LAVA在uniq、who、md5sum、base64四個程式上進行bug插入而形成的測試集即為LAVA-M。

    LAVA-M被廣泛應用於fuzz領域的工具效果評估。

  • LAVA-M下載地址 http://panda.moyix.net/~moyix/lava_corpus.tar.xz ,百度雲連結如下。

    連結:https://pan.baidu.com/s/12QsrCUYY7R43Nrb-2qx2iA
    提取碼:636z

  • 以base64為例介紹安裝方法,進入base64資料夾,./validate.sh使用指令碼進行安裝。

    如果顯示Validated 0/44bugs,則可能的解決辦法為:

    • 缺少了libacl,通過命令sudo apt-get install libacl1-dev安裝即可。

    • validate.sh中的 --prefix要改為絕對路徑/你的路徑/lava_corpus/LAVA-M/base64,原指令碼中提供的命令為

      ./configure --prefix=`pwd`/lava-install LIBS="-lacl" &> /dev/null
      

使用AFL對LAVA-M及進行測試

  • LAVA-M安裝時為自動化指令碼安裝方法,安裝後的base64預設為gcc編譯,為了使用AFL系列工具對LAVA-M進行測試,我們需要將編譯選項進行調整,以生成被afl-gcc插樁後的base64。 設定環境如下。

    export CC=afl-gcc
    export CXX=afl-g++
    

    再執行指令碼./validate.sh,插入bug數量仍為44/44,即安裝成功。

使用Angora對LAVA-M及進行測試

5、總結