0x00 引言
软件与信息系统的漏洞长久以来一直是网络空间安全威胁的重要源头,特别是0day attack往往导致严重的破坏,因此针对软件与信息系统的漏洞挖掘技术在长时间的发展中提出了以两大特性为基础的技术。
第一是基于静态分析。静态分析是没有实质执行程序的情况下进行的程序分析技术。静态分析通常在源代码上的基础上进行,包括对词法、语法、语义特征、元数据等等进行分析,以快速检查出代码中的bug。静态分析技术中的一个著名代表便是符号执行。符号执行通过符号化程序的输入,并为执行过程中遇见的每一个分支路径维护一组约束,使用约束求解器来求解建立的一系列约束条件,最终确定什么输入导致了目标分支的执行。然而,符号执行毕竟不是在真实环境中运行,
第二是基于动态分析。与静态分析相比,动态分析需要在真实的环境或者高模拟仿真的环境中运行程序,并通过监控程序的运行状态来判断 程序是否出现bug。动态分析技术的另一个典型代表就是fuzzing技术。fuzzing技术从概念上来说十分简单,即准备一组输入,将这组输入喂给程序,并实时监控程序的状态,根据程序给出的反馈判定是否出现bug。在实际的运用过程中,fuzzer往往会遇到一个问题,即fuzzing通常是通过变异输入来测试程序,试图达到不同的代码分支,然而当fuzzer测试的代码比较深、分支条件较为复杂时,fuzzer难以通过变异输入猜测到新的分支,这时fuzzer会在当前分支“停住”。
没错,你的反应是对的。既然fuzzer会在某些分支“停住”,那么我们是否可以利用符号执行技术,对这些分支建立符号约束求解呢?答是可以的,并且效果还相当不错。
发表在2016年上NDSS的《Driller:Agumenting Fuzzing Through Selective Symbolic Execution》便是基于这种思路提出了一种混合符号执行和传统fuzzing的fuzzer,Driller。
0x01 Driller
Driller是一种引导式白盒模糊测试工具(Guided whitebox fuzzer),它构建在现有的先进模糊测试器与符号执行上,具体的是基于AFL和Angr项目之上。通过Angr增加concolic技术以实现有效的漏洞挖掘。
相关概述
根据NDSS上的论文我们讲解一下Drller的工作。相比于其他Fuzzer,Driller做出了如下改进或者说贡献:
- 提出一种新的方法,选择性使用现代符号执行技术concolic(混合执行,concrete和symbolic的组合词)来抵达更深层次的程序代码,以提高fuzzing的有效性。同时通过使用fuzzing技术来缓解符号执行过程中存在的路径爆炸问题,以提高conolic执行的可扩展性。
- 基于上诉理论实现了一种工具——Driller,用于验证上诉方法。
- 使用与在Cyber Grand Challenge上胜出队伍相同的数据集,寻找到相近数量的漏洞,证明Driller的有效性。
Driller设计的核心思想,即将用户输入视作两种类型:一般输入和特定输入。一般输入表示一系列可能 有效的值,而特定输入则表示必须采取少数几个可能值的输入才能使得程序在一个基本块跳转到特定值引导的基本块。因此,应用程序对特定值输入的检查,将应用程序分成了不同的区域。
组成部分
Driller由四个部分组成,我们将介绍Drller的每个部分。
- Input test cases:得益于符号执行技术,Driller可以在无需任何输入测试案例的情况下启动。Driller自身可通过符号执行来寻找到一些初始化的输入,但Driller的模糊器部分毕竟基于AFL,提供良好的种子输入对Driller的fuzzing仍然十分有用。
- Fuzzing:当Driller工作时,它首先启动Fuzzing引擎,并探索应用程序的第一部分,直到抵达Driller认为的一个特定输入值的地方,此时Driller会“卡住“,难以搜索到新的路径。
- Conolic Execution:当Driller的Fuzzing引擎卡住时,Driller将调用concolic混合执行引擎。该组件将会分析之前fuzzing获得的输入,通过约束输入范围防止路径爆炸。然后基于fuzzing的输入,conolic将利用器约束求解器试图求解和探索出新的路径。
- Repeat:一旦conlic引擎求解出新的输入,这些输入立刻回被送往模糊测试引擎,由模糊测试器继续对新的分支路径进行测试、变异。而Driller正是在conolic引擎和模糊测试引擎之前循环执行,直到发现crash为止。
工作流程
在Driller的论文中展示了一个例子来说明Driller是如何工作的,该例子的提供的代码如下图所示。
该代码是正在读取一份配置文件,而其中有实际开发中常见的校验,这里体现为对配置文件magic值的校验。校验通过后,程序将根据配置文件的内容,在几个分支间跳转到对应的处理,处理完毕后程序退出,而其中的某一个分支存在一个bug。
对上图的代码的测试,Driller具体的工作过程可以兑现为下图。Driller的模糊器最初将遭遇第一个分支猜测节点,并且遭遇对magic的检查。Driller的模糊测试引擎最终在对magic值的检查中“卡住”,而此时Driller将会切好至Conolic混合执行引擎,求解出magic的值,并且将这个值返回给模糊测试引擎。
模糊测试引擎根据新的值继续进行fuzzing,但Driller的模糊测试引擎将在后续的fuzzing中没有找到除了default分支外的另外两个新分支。此时Driller再次卡住,这时Driller将第二次调用conolic混合执行引擎,寻找到bug和others两个新的分支。
Driller与传统Fuzzer的比较
传统的Fuzzer基于遗传变异算法,根据遗传启发规则对输入进行突变,然而这种突变通常是大范围的,因此传统的Fuzzer在搜索仅需要一般值输入即可进行分支跳转的路径而言,效率十分高。但如果是对于需要特定输入的分支跳转的情况,传统fuzzer的效果就十分的不理想了,具体的如下图所示。
对于下面的程序而言,程序需要x等于一个特定的值才能触发其中隐含的bug路径。对于像AFL这样的Fuzzer进行fuzzing可能是十分困难的,尽管AFL的确拥有优秀的输入变异算法,但是大范围的值变异,想要命中一个特定的值,仍然是一件十分困难的事情,尤其是x的值范围是2^32,对于随机变异来说,搜索到x等于的值甚至是一件不可能的事情。
Driller内置的conolic混合执行引擎,弥补了传统fuzzer的这个致命的缺陷。当Driller的模糊测试引擎已经进行足够多数量(具体的输入与输入的长度成一个正比关系)的突变,仍然未能找到新的分支时,Driller将认为模糊测试引擎“卡住”了。Driller将切换至conolic混合执行引擎,对当前分支进行约束求解,对于特定值的求解正是符号执行所擅长的,因此Driller能很快抵达传统Fuzzer难以抵达的分支。
0x02 搭建
我们使用容器搭建Driller的使用环境,具体的由于Driller基于AFL和angr,我们选择在AFL++官方提供的容器上搭建Driller,这样我们只需要安装angr就行,不需要理会复杂的依赖关系。
如果你的机器上没有afl++的容器,可以执行下面命令拉取。
docker pull aflplusplus/aflplusplus
启动容器后,我们检查一下pip和python版本。
[AFL++ e177b910f23d] /AFLplusplus # pip --version
pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
[AFL++ e177b910f23d] /AFLplusplus # pip list
Package Version
---------- -----------
meson 0.61.2
pip 22.0.2
Pygments 2.11.2
PyYAML 5.4.1
setuptools 59.6.0
six 1.16.0
unicorn 2.0.1.post1
unicornafl 2.0.2
wheel 0.37.1
较新版本的python我们可以直接安装angr,执行下列命令即可,我这里使用清华源加速。
pip install angr -i https://pypi.tuna.tsinghua.edu.cn/simple
随后我们开始安装Driller,注意如你的机器访问github的资源需要代理,那么你最好开启全局代理的情况下执行下面命令,否则很有可能失败。
pip install git+https://github.com/angr/tracer
pip install git+https://github.com/shellphish/driller
我们检查一下Driller是否安装成功,直接在python终端import driller
即可。
[AFL++ e177b910f23d] /home/cxing/driller # python
Python 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import driller
>>>
官方集成了一个工具叫做shellphuzz,可以通过命令行方便的使用Driller,而不用写python代码调用Driller,具体的地址如下,https://github.com/shellphish/fuzzer/tree/master
官方库中提供了一些例子使用Driller,可以参考一下。