AFL介绍(README.txt)

AFL介绍

参考:/docs/readme.txt
参考博客:http://blog.csdn.net/abcdyzhang/article/details/53727221

1.guided fuzzing的挑战

Fuzzing是漏洞挖掘领域最有效的方法之一,可以用来发现大量的远程代码执行和提权的漏洞。然而,fuzzing中随机变异的策略使得我们很难实现达到测试程序特定的代码路径。这就使得测试的代码覆盖率很低。

有很多人试图去解决这个问题,Tavis Ormandy曾经提出一种:语料精馏法。这个方法根据代码覆盖率,从大量高质量的输入文件语料中选取一个子集,然后按照传统方法去fuzz。这种方法很有效,但前提是需要一个这样的语料。另一方面,代码覆盖率只提供了一个很简单的对程序状态的描述,当Fuzzing测试到了一定的程度,代码覆盖率就没什么作用了。

所以,大家都在探索更复杂的技术,比如:程序控制流分析,符号执行或静态分析。这些技术在实验中是很有前途的,但是在实际应用中显得效率低下、缺乏可靠性。

2.afl-fuzz方法

AFL是一个暴力方法的fuzzer,其算法主要包括几下几个步骤:

1)将用户提供的初始测试用例装载进队列中。

2)从队列中获取下一个输入文件。

3)对测试用例进行精简。

4)使用一种传统的模糊策略对文件进行变异。

5)如果变异导致了插桩状态的变化,则将这种变异作为一个新的条目记录到队列中。

6)返回步骤2.。

3.使用AFL对程序进行插桩(有源码)

当有源码是,我们可以在编译期间进行插桩:

CC=/path/to/afl/afl-gcc ./configure
CXX=/path/to/afl/afl-g++  (C++程序需要)
make clean all

当测试libraries的时候,我们需要写一个小程序,从stdin中或者一个文件中读取数据,然后传到被测试的libraries中。这种情况下,我们必须把插桩好的库的静态版本与可执行文件链接起来,或者确保运行时加载正确的.so文件。最简单的方法是静态构建,方法如下:

CC=/path/to/afl/afl-gcc ./configure --disable-shared
4.对二进制进行插桩(无源码)

当没有源码时,AFL对黑盒二进制的插桩提供实验性支持。这主要是通过qemu的一种知名度较小的“用户空间仿真”模式来提供。

qemu是一个独立于AFL的项目,但在AFL中也提供了方法:

cd qemu_mode
./build_qemu_support.sh

这种模式比在编译是进行插桩慢2-5倍,不利于并行处理,而且还有可能有一些其他的问题。

5.选择初始测试集

为了能够正确的操作,fuzzer要求能有一个或多个能够被目标应用所接受的好的测试用例。有以下2个基本的原则:

1)最好保证文件小于1KB。

2)选择的样例尽可能在功能上不相同,比如:fuzz照片程序时,没有必要用50个不同的风景照。

如果有大量数据可用于筛选,可以使用afl-cmin功能来识别在目标程序中执行不同代码路径的功能不同的文件的子集。

6.模糊测试二进制文件

fuzzing过程由afl-fuzz部分来负责。这个过程要求一个有测试集的只读文件目录,一个单独的空间来存储findings,以及进行测试的二进制程序的路径。

如果测试程序是从命令行接受输入,则命令如下:

 ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]

如果测试程序是从文件中获取输入,则使用“@@”来代替命令行中的文件名。命令如下:

./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@

对于没有被instrument的二进制目标程序,可以用qemu模式(-Q)进行fuzz,或者直接用传统blind-fuzzer模式(-n)。使用-t和-m选项,修改fuzz过程的默认timeout和memory limit。

一般afl-fuzz在开始时会执行一系列的确定性fuzzing步骤,这会花费几天的时间,但是会生成更neat的testcase。如果你想要快速运行、马上生成dirty的结果—-类似于zzuf和其他传统fuzzer—-可以使用-d选项,跳过确定性fuzzing步骤。

7.输出文件的解释

在输出文件夹中会有3个子文件夹,并会实时更新:

-queue/文件夹:存放每个独立执行路径的测试用例以及用户最开始所给定的文件。这些组成之前第2部分提到过的合成语义库。

-crashes/文件夹:存储那些使测试程序接受到错误信号的测试用例。它们按所收到的信号进行分组。

-hangs/文件夹:存储那些使测试程序超时的测试用例。

8.并行fuzzing

每一个afl-fuzz进程都需要占用一个内核。这意味着在多核系统中,为了充分利用硬件资源,并行化是必须的。并行fuzzing模式提供了一个为AFL接口与其他模糊器,符号执行或者concolic混合符号执行引擎等连接的简单方法。详见parallel_fuzzing.txt

9.Fuzzer字典

默认的。afl-fuzz变异引擎针对紧凑型数据格式,即图像,多媒体,压缩数据,正则表达式语法或shell脚本进行了优化。它有点不太适合特别冗长和冗余的语言,尤其包括HTML,SQL或JavaScript。

为了避免构建语法感知工具的麻烦,afl-fuzz提供了一种使用语言关键字,magic header或其他与目标数据类型相关联的特殊表示的可选字典为模糊过程提供种子的方法 - 并且使用它来重建底层语法,参考:

http://lcamtuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html

要使用这个功能,首先需要创建在dictionaries/README.dictionaries中讨论的两种格式之一的字典;然后在命令行中使用-x选项将fuzzer指向它。(在该目录的子目录下已经提供了几个常用字典)

没有办法提供更多结构化的底层语法的描述,但是fuzzer可能会根据单独的插桩反馈来找出其中的一些。 参考:

http://lcamtuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html

PS:

即使当没有给出明确的字典时,afl-fuzz将通过在确定性字节翻转期间非常仔细地观察插桩来尝试提取输入语料库中的现有语法表示(token-表示,记号)。 这适用于一些类型的解析器和语法,但不像-x模式那么好。

如果字典真的很难得到,另一个选择是让AFL运行一段时间,然后使用AFL自带的表示捕获库。 有关详细信息,请参阅libtokencap/README.tokencap。

在http://volatileminds.net/2015/08/20/advanced-afl-usage-preeny.html中有关于字典的使用和简单介绍。

10.崩溃分类

基于覆盖的崩溃分组通常生成一个小数据集,可以手动或使用非常简单的GDB或Valgrind脚本快速分类。每个崩溃也可追溯到其在queue中的非崩溃的parent测试用例,从而更容易诊断故障。

afl-fuzz的支持一个崩溃探索模式,使用-C标志。https://lcamtuf.blogspot.com/2014/11/afl-fuzz-crash-exploration-mode.html

在这种模式下,模糊器需要一个或多个崩溃的测试用例作为输入,并使用其反馈驱动的fuzzing策略来快速枚举程序中可以到达的所有代码路径,同时保持其处于崩溃状态。

不导致崩溃的变异被拒绝; 任何不影响执行路径的更改也一样。

输出是一个小文件库,可以快速检查攻击者对故障地址的控制程度,或者是否有可能通过一个初始的越界读取,以及查看下面的内容 。

afl-tmin工具适用于crash和non-crash的test case。用于test case 最小化!注意:afl-tmin是对一个文件的无用字节、bit的删减以达到最小化。

 ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]

afl-analyze工具。它需要一个输入文件,尝试顺序翻转字节,并观察被测试程序的行为。然后对输入进行颜色编码,基于哪些部分看起来是关键的,哪些部分不是;虽然不是防弹,它通常可以提供对复杂文件格式的快速洞察。

11.除了crashes以外

模糊化是发现非崩溃的设计和存在错误的一个很好的和未充分利用的技术。通过修改目标程序来调用abort(),发现了一些有趣的错误,例如:

  • 当给fuzzer-generated相同的输入时,两个bignum库产生不同的输出,

  • 一个图像库产生不同的输出,当被要求对同一输入图像连续解码多次时,

  • 当对fuzzer-supplied数据进行迭代序列化和反序列化操作时,序列化/反序列化库无法产生稳定的输出,

  • 当一个库先压缩,再解压一个特定的部分时,进行测试,相同的输入会产生不同的输出。

实施这些或类似的理智检查通常需要很少的时间;如果您是特定包的维护者,则可以使用这些代码#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION(也与libfuzzer共享的标志)或#ifdef __AFL_COMPILER(这仅适用于AFL)

12.常识性风险

请记住,像许多其他密集计算机型任务相比,模糊测试可能会对你的系统以及硬件造成压力:

  • CPU将会运行过热,需要充分的降温。

  • 目标程序的运行可能会无规律的停止, 从而造成内存的泄漏和垃圾文件。

  • 模糊测试需要对文件系统进行数十亿的读写操作。在现代系统中,这通常会造成过度的缓存。

    在Linux系统上,’iostat’命令可以很好的监控磁盘上的I/O:

    iostat -d 3 -x -k [...optional disk ID...]
13.已知的局限性以及需要改进的地方
  • AFL通过检查由于信号(*SIGSEGV**SIGABRT*等)导致的第一个引起的进程死亡来检测故障**。为这些信号安装自定义处理程序的程序可能需要注释掉相关代码。同样,由目标产生的子处理中的错误可能会逃避检测,除非您手动添加一些代码来捕获它。
  • 与任何其他强力工具一样,如果使用加密,校验和,加密签名或压缩来完全包装要测试的实际数据格式,则模糊器提供有限的覆盖率。要解决这个问题,你可以注释掉相关的检查;如果这是不可能的,你也可以写一个后处理器postprocessor,参考experimental/post_library/。

  • 有一些不幸的折衷对于ASAN和64位二进制。这不是由于afl-fuzz的任何特定故障;请参阅notes_for_asan.txt提示。

  • 没有直接支持fuzzing网络服务,后台守护进程或需要UI交互工作的交互式应用程序。您可能需要进行简单的代码更改,以使它们以更传统的方式运行。Preeny可以提供一个相对简单的选项,看看:https://github.com/zardus/preeny有关修改基于网络的服务的一些有用的提示还可以在以下网址找到:https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop

  • AFL不输出人类可读的覆盖数据。如果你想监视覆盖,使用afl-cov从Michael Rash:https://github.com/mrash/afl-cov

  • 偶尔,有意识的机器反对他们的创造者。如果发生这种情况,请参阅http://lcamtuf.coredump.cx/prep/。

你可能感兴趣的:(AFL)