Angora: Efficient Fuzzing by Principled Search

Posted poziiey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Angora: Efficient Fuzzing by Principled Search相关的知识,希望对你有一定的参考价值。

1、论文介绍

  • Source

    • IEEE Symposium on Security and Privacy,2018
  • Authors

    • Peng Chen ShanghaiTech University chenpeng@shanghaitech.edu.cn

    • Hao Chen University of California, Davis chen@ucdavis.edu

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)处的梯度( abla_x f(x)) ,然后进行梯度下降操作,(xleftarrow x - epsilon abla_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、总结





以上是关于Angora: Efficient Fuzzing by Principled Search的主要内容,如果未能解决你的问题,请参考以下文章

第一次ActiveX Fuzzing测试

资源:开源Fuzzers工具列表 (以及其它fuzzing工具)

Go Fuzzing已经进入Beta测试阶段

XSS Fuzzing初探

Kitty:Python语言编写的Fuzzing框架

Go中fuzzing系统的原理分析