AFL(american fuzzy lop)学习一
@sizaif
@2022-04-09
AFL 的模糊方法
- 基于改进的边缘覆盖
- 插桩法引导的遗传算法
流程:
插桩
-
从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
-
选择一些输入文件,作为初始测试集加入输入队列(queue);
-
将队列中的文件按一定的策略进行“突变”;
-
如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
-
上述过程会一直循环进行,期间触发了crash的文件会被记录下来。
二进制
改进边缘覆盖:
向目标程序注入以下工具来记录采用了哪些分支和次数
- 一条边表示为当前基本块(分配了一个随机常数)与前一个基本块(即元组 (prev_location, cur_location))之间的 XOR
- 覆盖信息存储在一个紧凑共享的64KB的哈希表中,称为跟踪位图(bitmap)
- 当访问一条边时,通过增加与该特定边的哈希相对应的位图中的值来记录命中
Instrumenting programs for use with AFL
源代码已知的情况下:编译插桩afl工具:
通用方法:
对于clang(afl-clang and afl-clang++) 也适用
测试(libraries)时, 需要找到或编写一个简单的程序,该程序从stdin或文件中读取数据,并将数据传递给测试库。
将此可执行文件链接到已检测库的静态版本,或者确保在运行时加载正确的**.so**文件(通常通过设置
LD_LIBRARY _PATH
)。最简单的选择是静态构建,通常通过
Instrumenting binary-only apps
无法获得源码时,针对二进制文件进行fuzzer, 使用QEMU
源码
- afl-analyze:用来对用例进行分析,发现其中有意义的字段。
- afl-clang++:相当于c++的编译器的封装。
- afl-fuzz:AFL主体,用于对目标程序进行fuzz。
- afl-gcc:相当于gcc的封装。
- afl-plot:生成模糊测试任务的状态图。
- afl-tmin:对用例进行简化。
- afl-clang:相当于c的编译器封装。
- afl-cmin:对用例进行简化。
- afl-g++:相当于g++的封装。
- afl-gotcpu:用于查看当前CPU状态。
- afl-showmap:用于对单个用例执行路径跟踪。
- afl-whatsup:用于查看fuzz任务当前的状态。
- alf-clang-fast: 基于LLVM高效clang针对c
- afl-clang-fast++: 基于LLVM高效clang针对c++
输出
解释输出
在输出目录中创建了三个子目录并实时更新
queue/
每个独特的执行路径的测试用例,加上用户给出的所有开始文件。这就是第2节提到的合成语料。在将这个语料库用于任何其他目的之前,您可以使用afl-cmin
工具将它缩小到更小的大小。该工具将找到提供同等边缘覆盖的较小文件子集。crashes/
导致被测试程序接收致命信号的唯一测试用例(例如SIGSEGV, SIGILL, SIGABRT)。这些条目按接收到的信号分组。hangs/
导致被测试程序超时的唯一测试用例。在某些东西被归类为挂起之前的默认时间限制大于1秒和-t
参数的值。可以通过设置AFL_HANG_TMOUT
来微调该值,但很少需要这样做。
运行含义
- process timing 指示了Fuzzer测试的时间消耗
- run time: 运行总时间
- last new path: path(触发新执行的测试用例的缩写),上次执行测试用例的时间,长时间未变化说明有程序有问题,过于简单无分支或内存太小会收到一个红色警告
- last uniq crash: 上次崩溃的时间
- last uniq hang: 上次挂起的时间
- cycle done: 表明fuzzer的轮数
- 颜色说明
- 品红色处于 the first pass,
- 如果有新发现,颜色会变成黄色,
- 所有子过程完成后将会变成蓝色,
- 最后变成绿色的话表明已经长时间没有新的动作了,此时也提示我们应该手动ctrl-c去关闭fuzzing。
- 颜色说明
- total paths: 目前为止执行的测试用例
- uniq crashes: 目前为止发现的崩溃
- uniq hang: 目前为止发现的挂起
- ow processing: 当前测试用例的进程的ID 因为fuzzer是开启另一个进程进行测试的 结果写回共享内存中
- paths timed out: 根据超时决定是否放弃
-
map density:多少个分支元组,命中,与位图的容量成比例.号码在左边描述当前输入;右边的是整体的值是输入语料库。绝对值低于二百说的是表明了以下三种情况之一
程序极其简单;它没有被正确地使用(例如,由于链接到目标的非工具副本图书馆);或者它过早地退出了您的输入测试用例。绒毛会尝试用粉红色标记,只是为了让你意识到。超过70%的百分比可能很少发生在非常复杂的程序大量使用模板生成的代码。将以红色标记高百分比。但是一般不会有红色标记除非你用的是非常复杂的软件(比如v8, perl,ffmpeg)。
-
count coverage:另一行处理元组命中次数的可变性二进制文件。本质上,如果每一个被取的分支总是取一个固定数量的对于我们尝试过的所有输入,这将是“1.00”。当我们管理为了触发每个分支的其他命中计数,指针将开始移动接近“8.00”(8位地图命中的每一个位),但可能永远不会达到这一极端。
-
now trying: 指明当前所用的变异输入的方法
参数含义
- calibration – 在fuzzing测试之前的阶段,主要是检查执行路径检测异常,建立基线执行速度。
- trim L/S – 同样也是在fuzzing测试之前的阶段,修建测试用例使其更短,但保证裁剪后仍能达到相同的执行路径。L表示length长度,S表示stepover步距,其值与文件大小是相关的。
- bitflip L/S – 确定性的比特位翻转。以S为增量,L长度的bit数被翻转。有以下几种变型模式:1/1, 2/1, 4/1, 8/8, 16/8, 32/8。
- arith L/8 – 确定性的算术运算。AFL会尝试去减去或者加上一些整数使其为8bit/16bit/32bit的值,步距永远是8bits。
- interest L/8 – 确定性的值覆盖。AFL自身保留了一些Interesting的8bit/16bit/32bit的值,用这些值去覆盖原有的测试用例,步距永远是8bits。
- extras – 确定性的字典注入。这里AFL自身有一个字典,当然也可以使用-x选项来指明使用用户提供的字典。
- havoc – 固定长度的堆叠随机扭曲。该阶段会尝试位翻转,用随机数或者Interesting的整数去覆盖,块删除,块复制,以及字典的相关操作。
- splice – 最后一种策略。在上述策略都执行完后将会执行该策略,它和havoc差不多,不过它会首先将队列中的两个随机输入先拼接在一起。
- sync – 这个是并行执行的策略选项,通过-M或者-S选项进行指定。该策略并不会涉及到真正的fuzzing,会导入从另一个fuzzer得到的输出和测试用例。
以上所有策略执行一遍也就是前文提到的the first pass。
-
stage execs: 当前阶段的进度指示
-
total execs: 全局的进度指示
-
exec speed: 执行速度但是基准测试应该理想地超过500次执行/秒大多数时候——如果它保持在100以下,这项工作可能会非常长。fuzzer也会明确地警告你缓慢的目标。如果发生这种情况,查看fuzzer中包含的perf_tips.txt文件,了解如何提高速度的事情了。
-
favored paths: 基于最小化算法产生新的更好的路径
-
new edges on: 基于更好路径产生的新边
-
total crashes: 基于更好路径产生的崩溃
-
total tmouts: 基于更好路径产生的超时 包括所有超时的超时 即使这些超时不足以分类到hangs.
- bit flips: 确定性的比特位翻转。以S为增量,L长度的bit数被翻转。有以下几种变型模式:1/1, 2/1, 4/1, 8/8, 16/8, 32/8。
- byte flips: 这个应该就是字节翻转了
- arithmetics: – 确定性的算术运算。AFL会尝试去减去或者加上一些整数使其为8bit/16bit/32bit的值,步距永远是8bits。
- known ints:
- dictionary:
- havoc:– 固定长度的堆叠随机扭曲。该阶段会尝试位翻转,用随机数或者Interesting的整数去覆盖,块删除,块复制,以及字典的相关操作。
- trim:– 同样也是在fuzzing测试之前的阶段,修建测试用例使其更短,但保证裁剪后仍能达到相同的执行路径。L表示length长度,S表示stepover步距,其值与文件大小是相关的。 这个修剪策略有一些不同/ 这一行中的第一个数字显示了从输入中删除的字节的比率,第二个数字表示实现这一目标需要的执行数,第三个数字显示了字节的比例,尽管不可能移除,被视为没有效果,在进行效果更好的fuzzer步骤的时候不是考虑的因素
-
levels: 表示测试等级
-
pending: 表示还没有经过fuzzing的输入数量
-
pend fav: 表明fuzzer感兴趣的输入数量
-
own finds: 表示在fuzzing过程中新找到的,或者是并行测试从另一个实例导入的
-
imported: n/a表明不可用,即没有导入
-
stability: 表明相同输入是否产生了相同的行为,一般结果都是100%,如果低于100%并且变红,则需要查阅官方文档寻找解决步骤。
1
c 编译插桩测试
编写alftestc代码
创建configure makefile等文件
使用afl-gcc
插桩
afl-gcc插桩
编译运行
创建 in
,crash
文件夹
in
中创建初始seed : hello.txt
afl-fuzz 运行
crash
当输入seed 突变为sizaif,且长度为6时,crash
plot
使用afl-clang-fast
插桩:
编译运行
创建 in
,crash
文件夹
in
中创建初始seed : hello.txt
afl-fuzz 运行
crash
plot
c++ 编译插桩测试
编写afltestc++
代码
使用afl-g++
插桩 :
运行
预览: