AFL模糊测试

news2025/2/27 20:41:06

一、AFL简介

AFL(American Fuzzy Lop)号称是当前最高级的Fuzzing 测试工具之一 是由安全研究员Michał Zalewski(@lcamtuf)开发的一款 基于覆盖引导(Coverage-guided)的 模糊测试工具,它 通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率
AFL 采用新型的 编译时插桩遗传算法自动发现新的测试用例,这些用例会触发目标 二进制文件中的新内部状态。这大大改善了模糊测试的代码覆盖范围。
AFL既可以对源码进行编译时插桩,也可以使用AFL的QEMU mode对二进制文件进行插桩,但是前者的效率相对来说要高很多。
  
AFL的 优点是可以轻松部署,配置相对简单,测试效率相对较高。 原生的AFL仅适配于C/C++程序的测试,不过目前已经衍生出很多分支,用于适配其他语言的模糊测试,如针对JAVA程序的Kelinci等
   
工作流程大致如下:
    ①从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
    ②选择一些输入文件,作为初始测试集加入输入队列(queue);
    ③将队列中的文件按一定的策略进行“突变”;
    ④如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
    ⑤上述过程会一直循环进行,期间触发了crash的文件会被记录下来。
AFL变异的方式
AFL是采用遗传算法,基于变异生成的测试用例,变异的主要类型有下面这几种:
  • bitflip:按位翻转,1变为0,0变为1
  • arithmetic:整数加/减算术运算
  • interest:把一些特殊内容替换到原文件中。所谓的特殊内容是AFL预设的一些比较特殊的数,比如可能造成溢出的数。
  • dictionary:把自动生成或用户提供的token替换/插入到原文件中
  • havoc:“大破坏”,是前面几种变异的组合
  • splice:“连接”,此阶段会将两个文件拼接起来得到一个新的文件

二、AFL安装和使用

利用AFL进行模糊测试的大概 过程:①插桩编译;②设置种子测试用例;③开始fuzz 。模糊测试对于软件测试的帮助很大,其不受限于被测系统的内部实现细节和复杂程度。

2.1、安装

AFL 自带定制版本的 gcc 和 clang 编译器,建议选择 LLVM 的 clang 编译器,可以加快 fuzz 的速度。
下载AFL源码( GitHub - google/AFL: american fuzzy lop - a security-oriented fuzzer),解压后,编译安装:
AFL使用afl-gcc编译
make
sudo make install  安装到系统目录
  
AFL使用afl-clang编译
cd llvm_mode
make
cd ..
sudo make install 
 
查看路径可以看到afl安装的文件:
作用分别为
  • afl-gcc 和afl-g++ 分别对应的是gcc 和g++ 的封装
  • afl-clang 和afl-clang++ 分别对应clang 的c 和c++ 编译器封装À。
  • afl-fuzz 是AFL 的主体,用于对目标程序进行fuzz。
  • afl-analyze 可以对用例进行分析,通过分析给定的用例,看能否发现用例中有意义的字段。
  • afl-qemu-trace 用于qemu-mode ,默认不安装 ,需要手工执行qemu-mode 的编译脚本进行编译,后面会介绍。
  • afl-plot 生成测试任务的状态图
  • afl-tmin 和afl-cmin 对用例进行简化
  • afl-whatsup 用于查看fuzz 任务的状态
  • afl-gotcpu 用于查看当前CPU 状态
  • afl-showmap 用于对单个用例进行执行路径跟踪
  
afl-fuzz指令:
下面我们进行一个简单的AFL实验了解其使用流程。

2.2、准备被测程序

首先创建一个简单的test.c源文件,里面放入我们要测的c程序。
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <signal.h> 

int AFLTest(char *str)
{
    int len = strlen(str);
    if(str[0] == 'A' && len == 6)
    {
        raise(SIGSEGV);
        //如果输入的字符串的首字符为A并且长度为6,则异常退出
    }
    else if(str[0] == 'F' && len == 16)
    {
        raise(SIGSEGV);
        //如果输入的字符串的首字符为F并且长度为16,则异常退出
    }
    else if(str[0] == 'L' && len == 66)
    {
        raise(SIGSEGV);
        //如果输入的字符串的首字符为F并且长度为66,则异常退出
    }
    else
    {
        printf("it is good!\n");
    }
    return 0;
}

int main(int argc, char *argv[])
{
    char buf[100]={0};
    gets(buf);
    //存在栈溢出漏洞
    printf(buf);
    //存在格式化字符串漏洞
    AFLTest(buf);
    
    return 0;
}

2.3、使用 afl-gcc 插桩编译

编译插桩是指在代码编译期间修改或新增代码

用 AFL 编译目标源码,其目的在于插桩,让编译得到的程序,反馈路径覆盖。AFL 自带定制版本的 gcc 和 clang 编译器,建议选择 LLVM 的 clang 编译器,可以加快 fuzz 的速度。
  
编译过程和普通gcc编译也是一样,除了使用的命令需要带上afl-前缀,因此
afl-gcc -g -o test  test.c   # 针对c文件, -g选项为使用gdb调试所需,如果不调试就不需加此选项。 在linux下编译链接程序,如果不加-o参数,生成的binary代码的名字都是默认的a.out
可以看到在编译过程中,编译器已经提示存在漏洞,不理会,用AFL去测试
编译后会有插桩符号,使用下面的命令可以验证这一点: readelf -s ./test | grep afl
注:
针对c++文件,则使用 afl-g++ -g -o test  test.cpp
注意在编译项目时,通常有Makefile,这是就需要在Makefile中添加内容: gcc/g++重新编译程序的方法是:
CC=/path/to/afl/afl-gcc ./configure
make clean all
对于一个C++程序,要设置:
CXX=/path/to/afl/afl-g++.
afl-clang和afl-clang++的使用方法类似。

2.4、准备种子语料库

2.4.1、搜集种子语料库

作为模糊测试,AFL需要提供初始的种子输入, 变异算法会根据此种子变异生成各种测试用例,喂给程序。但实际上,你完全可以提供任何无意义的输入作为种子,模糊测试也一般能达到效果,只不过效率会低一些而已,是否提供有意义种子?提供多少?无外乎在种子获取难度和测试的效率要求之间进行权衡而已。
这里,借用 Freebuf 提供的资料,给出一些开源的语料库
  • libav sample s
  • ffmpeg samples
  • fuzzdata
  • moonshine
  • afl generated image test sets
  • fuzzer-test-suite
事实上很多程序也会自带一些案例,也可以作为测试用例。
   
在这里我们生成一好一坏两个种子语料库:
mkdir good-seeds bad-seeds
echo '1+1' > good-seeds/any-seed
echo 'this is a bad seed' > bad-seeds/any-seed

2.4.2、精简语料库

找到语料库之后,最好能够进行修剪, 合并重复用例,裁剪体积afl 推荐的每个用例体积小于 1KB,不然会影响 fuzz 的效率。

2.4.2.1、去重

afl-cmin 可以精简语料库,去掉可能重复的测试用例,可大大减少无用的 fuzz 用例
afl-cmin -i input_dir -o output_dir -- /path/to/tested/program [params]
更多的时候,我们是从文件中获取输入,因此,往往使用 @@ 替代 params(参数),即
afl-cmin -i input_dir -o output_dir -- /path/to/tested/program @@

2.4.2.2、裁剪体积

afl-tmin  可以缩短文件体积,因为 afl 要求测试用例的大小最好小于 1KB ,因此最好将精简后的用例进一步缩小体积。afl-tmin 有两种工作模式, instrumented mode crash mode 。默认的工作方式是instrumented mode
afl-tmin -i input_file -o output_file -- /path/to/tested/program [params] @@
 
由于 afl-cmin 一次性只能精简单个文件,如果用例特别多,需要手动花费很长时间,其实一条简单的 shell 脚本即可完成
for i in *; do afl-tmin -i $i -o tmin-$i -- ~/path/to/tested/program [params] @@; done;

2.5、开始测试

在执行afl-fuzz前,如果系统配置为将核心转储文件(core)通知发送到外部程序,将导致将崩溃信息发送到Fuzzer之间的延迟增大,进而可能将崩溃被误报为超时,所以我们得临时修改core_pattern文件:  echo core >/proc/sys/kernel/core_pattern
  
对于可以直接从stdin读取输入的目标程序来说,执行命令, afl-fuzz  -i good-seeds/ -o good-outputs -- ./test
对从文件读取输入的目标程序来说,要用“@@”: afl-fuzz -i ./in/ -o ./out/ -Q ./readelf -a @@    # 开始fuzz, @@表示从in 文件夹中找elf作为输入 ,实际上是在执行readelf -a 文件名
ctrl-C结束fuzz,共找到了6个crash,可以看到当前目录下已经多出了good-outputs目录,这是本次模糊测试的结果。
 
process timing:展示的是fuzzer的运行时间,也可以看到最近一次发现新路径的时间,最近一次崩溃的时间和最近一次挂起的时间。
overall result:运行的总周期数,总路径数,崩溃次数和挂起次数
cycle progress:fuzzer正在处理的测试样例的编号和由于超时放弃的的输入数量,从而得知fuzzer处理了多少样例,队列中还剩多少
map coverage
第一行显示的是已经命中了多少分支元组与位图可以容纳的数量的比例。左边的是当前输入,右边的是整个输入语料库的值。
第二行显示的是二进制文件中元组命中计数的变化,如果对于所有尝试的输入,每个采用的分支都采用固定的次数。读数显示为1.00, 当设法触发每个分支的其他命中计数时,读数向8.00移动(8位映射命中每一位),但可能永远不会到达极限。
这些值可以用于比较不同的模糊测试对于同一检测二进制文件的覆盖范围。
stage progress:显示了正在测试的策略(AFL变异方式),进度,总执行次数和执行速度。一般正常的执行速度应该在500以上。
findings in depth
这一部分的数据对一般的使用者用处不大,包括基于应用到代码的最小化算法获得的fuzzer最喜欢的路径数和真正获取更好的边缘覆盖率的测试用例数,还有超时和崩溃的计数器,注意这里的timeout和hang有所不同,timeout计数器包括了所有超过超时的测试用例,即使它们没有超过超时足够的幅度而分类为hangs(挂起)
fuzzing strategy yields:用于追踪各种fuzzing策略所获得的路径和尝试执行的次数的比例,用于验证各种方法的有效性。
path geometry
第一个数据指的是fuzzing过程中达到的路径深度,通常用户提供的测试用例视为1级,传统模糊测试从中获得的测试用例视为2级,通过它们作为后续模糊测试回合的输入得到的结果为3级,以此类推。
下一个数据pending指的是还有多少输入数据没有经过任何测试。pend fav时指fuzzer在这个队列中真正想到达的条目。接下来的是这个fuzzing部分找到的新路径数量和在进行并行化fuzzing时从其他fuzzer实例导入的路径数量 。
最后一个数据可以衡量观测到痕迹的一致性,如果程序对相同的输入始终表现相同,则为100%,若数值较低但仍然显示为紫色,测试过程不太可能受到负面影响,而显示红色说明出现了问题,afl可能难以区分调整输入文件的有意义的效果和幻象效果

2.6、fuzz结果分析

crashes:导致目标接收致命signal而崩溃的独特测试用例
queue:存放所有具有独特执行路径的测试用例。
AFL输出文件:
  • crashes/README.txt:保存了目标执行这些crashes文件的命令行参数。
  • hangs:导致目标超时的独特测试用例。
  • fuzzer_stats:afl-fuzz的运行状态。
  • plot_data:用于afl-plot绘图。
  
查看第1个crash,发现符合栈溢出漏洞的crash情况(最长输入100字符):
查看第2个crash:发现符合首字符为A且字符串长度为6的异常退出情况:
查看第3个crash:发现符合首字符为F且字符串长度为16的异常退出情况:
查看第4个crash,发现符合栈溢出漏洞的crash情况(最长输入100字符):
查看第5个crash,发现符合栈溢出漏洞的crash情况(最长输入100字符):
查看第6个crash:发现符合输入的字符串的首字符为L并且长度为66的异常退出情况:
   
AFL编译后的是一个普通的可执行文件,可以直接在命令行使用 ./test启动运行,然后此时你需要在终端进行输入,然后又会在终端获得输出:
   
afl-plot 可以绘制更加直观的结果,利用的就是 fuzzer 生成的 plot_data 文件。当然,要使用 afl-plot ,需要先安装 apt-get install gnuplot

三、并行fuzz测试

在使用afl-fuzz时,每个进程只会占用一个CPU核心。如果我们的机器是多核处理器,我们可以通过进行分布式fuzz来提高fuzz速度。
首先先看自己有多少内核: afl-gotcpu
以上可以看出可以运行6~8个实例。
  
首先指定主实例 -M 用于主实例,将 -S 添加到所有从属实例。注意, 这里-o跟的参数一定要保持一致,这个是用来同步各个fuzz进程的
  • 主实例:   afl-fuzz  -M master  -i  good-seeds/   -o good-outputs   -m none  --   ./test
  • 从实例1 afl-fuzz  -S slave1   -i  good-seeds/   -o good-outputs   -m none  --   ./test
  • 从实例2: afl-fuzz  -S slave2   -i  good-seeds/   -o good-outputs   -m none  --   ./test
若分布式意外退出可以使用以下命令继续fuzz任务:
  • 主实例:   afl-fuzz  -M master   -i-   -o good-outputs  -m none  --   ./test
  • 从实例1 afl-fuzz  -S slave1    -i-   -o good-outputs   -m none  --   ./test
  • 从实例2: afl-fuzz  -S slave2   -i-   -o good-outputs  -m none  --   ./test
运行后会在good_outputs文件夹会多出三个不同的文件夹master、slave1、slave2:

四、黑盒 fuzz(无源码AFL测试)

以上 fuzz 过程,依赖于我们有程序的源码,并且在编译过程中进行了插桩,但很多时候,我们并 没有源码,这时候就要靠 afl 提供的 qemu_mode 模式了( 只要在之前的命令的基础上加上-Q的参数即可  。原版本的 afl qemu 模式由于版本过老,已不能正常运行,推荐使用 github 上的   AFLplusplus  或者  afl-unicorn。AFLplusplus 更容易安装,而 afl-unicorn 针对 qemu 模式更加友好。

4.1、qemu_mode模式安装

无论是下载的哪个版本的 afl,根目录下都会有 qemu_mode 文件夹,进入此目录,运行以下脚本,如果没有出错,就代表 qemu_mode 成功了:
cd qemu_mode
sudo ./build_qemu_support.sh
cd ..
make install
  
安装注意事项:
  • 修改build_qemu_support.sh文件中的 QEMU_URL=" https://download.qemu.org/qemu- ${VERSION}.tar.xz",不然提示404
  • 参考 我的AFL入门之路 - 知乎 (zhihu.com) 更改一些配置:

4.2、示例

>>> 继续使用上面简单c代码进行测试,但 这次采用gcc进行编译,而不是afl-gcc。将test.c编译为test2
执行命令: afl-fuzz -Q -i good-seeds/ -o good-outputs  --  ./test2
可以看出:同样的程序,在qemu 模式下比在源码编译插桩的模式下会慢很多。(通过观察stage progress下的exec speed)
      
>>> 一个Fuzz实例
测试readelf。 由于readelf的输入其实就是elf文件,因此需要在in目录下放一个输入elf。 按照流程创建文件夹和测试用的elf。 
cd AFL/testcases/mytest
mkdir in out
cd in
cp ../../others/elf/small_exec.elf  .    # afl目录中自带一些常用文件的testcase
cd ..
cp /usr/bin/readelf .    # 将readelf复制到mytest 目录下
afl-fuzz -i ./in/ -o ./out/ -Q ./readelf -a @@    开始fuzz,@@表示从in文件夹中找elf作为输入,实际上是在执行readelf -a 文件名

五、参考

google/AFL: american fuzzy lop - a security-oriented fuzzer (github.com)

入门AFL | I'm dev (i-m.dev)

我的AFL入门之路 - 知乎 (zhihu.com)

AFL实战_Elwood Ying的博客-CSDN博客

经典 Fuzzer 工具 AFL 模糊测试指南_swift fuzzer工具_江下枫的博客-CSDN博客

模糊测试技术线上分享_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/990315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS系统环境搭建(十九)——CentOS7安装chat GPT

centos系统环境搭建专栏&#x1f517;点击跳转 CentOS7安装chat GPT Welcome to the AI era! 基于上一篇文章CentOS系统环境搭建&#xff08;十八&#xff09;——CentOS7安装Docker20.10.12和docker compose v2&#xff0c;你已经安装了docker20以上的版本。那么&#xff0…

代码随想录第30天 | ● 860.柠檬水找零 ● 406.根据身高重建队列 ● 452. 用最少数量的箭引爆气球

860.柠檬水找零 /*** param {number[]} bills* return {boolean}*/ var lemonadeChange function(bills) {let d50let d100let d200for(let i0;i<bills.length;i){if(bills[i]5){d51}else if(bills[i]10){if(d5>1){d5--d10}elsereturn false}else{if(d5>1&&…

圣杯交易系统选股公式,ADX指标和EMA均线配合使用

圣杯交易系统是由琳达布拉德福德拉希克(Linda Bradford Raschke)开发的&#xff0c;她是杰克施瓦格《新金融怪杰》书中的访谈对象。圣杯策略的目标是发现强劲趋势确立后的首次回撤&#xff0c;依据平均趋向指标ADX和指数移动平均线EMA的协同作用来确定趋势强度和合适的交易区域…

Vue中extend基本用法

1.Vue.extend(options) 参数: {Object} options用法&#xff1a; 使用基础Vue构造器&#xff0c;创建一个"子类"。参数是一个包含组件选项的对象。 data选项是特例&#xff0c;需要注意&#xff0c;在Vue.extend()中它必须是函数。 <html><head><tit…

武汉凯迪正大—直读激光盐密灰密测试仪

一、凯迪正大—绝缘子灰密盐密测试仪产品概述 凯迪正大绝缘子灰密盐密测试仪采用的检测技术将灰密测试与盐密测试合二为一&#xff0c;可同时检测出被测绝缘子的灰密度和盐密度&#xff0c;简化了绝缘子污秽检测的流程&#xff0c;适合在巡检现场和实验室使用。 二、凯迪正大…

JDK17:未来已来,你准备好了吗?

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

一元多项式的乘法与加法运算

设计函数分别求两个一元多项式的乘积与和。 输入格式: 输入分2行&#xff0c;每行分别先给出多项式非零项的个数&#xff0c;再以指数递降方式输入一个多项式非零项系数和指数&#xff08;绝对值均为不超过1000的整数&#xff09;。数字间以空格分隔。 输出格式: 输出分2行…

正中优配:散户怎么实现T+0?散户在股市上怎么变相T+0?

T0是指当天买入的标的物&#xff0c;在当天就能卖出的买卖方式&#xff0c;其中&#xff0c;在a股市场上&#xff0c;散户能够通过一些办法直接地完成T0买卖方式&#xff0c;接下来&#xff0c;正中优配为大家预备了相关内容&#xff0c;以供参阅。 散户在股票市场上&#xff0…

Linear Decryption: Rate-1 FHE TLP

参考文献&#xff1a; [ILL89] Russell Impagliazzo, Leonid A. Levin, and Michael Luby. Pseudo-random generation from oneway functions (extended abstracts). In 21st Annual ACM Symposium on Theory of Computing, pages 12–24, Seattle, WA, USA, May 15–17, 1989.…

苹果macOS13Ventura更新体验:新功能带来的全新体验

macOS 13 Ventura 是一款功能强大、界面美观的操作系统。它为用户提供了更好的使用体验&#xff0c;加强了与其他设备的互联互通&#xff0c;提高了隐私和安全性。无论是日常办公还是娱乐&#xff0c;macOS 13 Ventura 都能满足用户的需求&#xff0c;并带来更多的便利和乐趣。…

Linux之history、tab、alias、命令执行顺序、管道符以及exit

目录 Linux之history、tab、alias、命令执行顺序、管道符以及exit history历史命令 格式 参数 修改默认记录历史命令条数 案例 案例1 --- 显示history历史记录中出现次数最高的top10 案例2 --- 增加history显示的时间信息 命令与文件名补全 --- tab 命令别名 格式 案…

线性代数的学习和整理22:矩阵的点乘(草稿)

4 矩阵乘法 A,B两个同阶同秩N阵&#xff0c;看上去结构一样&#xff0c;但两厢相乘&#xff0c;在做在右&#xff0c;地位差别巨大。 在左&#xff0c;你就是基&#xff0c;是空间的根本&#xff0c;是坐标系&#xff0c;是往哪去、能到哪的定海神针&#xff0c;是如来佛手&a…

编程语言排行榜

以下是2023年的编程语言排行榜&#xff08;按照流行度排序&#xff09;&#xff1a; Python&#xff1a;Python一直以来都是非常受欢迎的编程语言&#xff0c;它简洁、易读且功能强大。在数据科学、机器学习、人工智能等领域有广泛应用。 JavaScript&#xff1a;作为前端开发…

seatunnel win idea 本地调试

调试FakeSource&#xff0c;LocalFile # Set the basic configuration of the task to be performed env {execution.parallelism 1job.mode "BATCH" }# Create a source to connect to Mongodb source {# This is a example source plugin **only for test and d…

穷举深搜暴搜回溯剪枝(4)

一)单词搜索: 直接在矩阵中依次找到特定字符串 79. 单词搜索 - 力扣&#xff08;LeetCode&#xff09; 画出决策树&#xff0c;只需要做一个深度优先遍历: 1)设计dfs函数:只需要关心每一层在做什么即可&#xff0c;从这个节点开始&#xff0c;开始去尝试匹配字符串的下一个字符…

如何配置视频直播点播平台EasyDSS视频服务平台参数,使同一直播间实现重复推流?

EasyDSS视频直播点播平台集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频等功能&#xff0c;分发的视频流可覆盖全终端、全平台。 EasyDSS已创建的直播间可支持重复推流&#xff0c;但为了保证直…

使用Idea导入mybatis dependence时爆红解决方法

<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.5</version></dependency>如上在pom.xml中配置mybatis的dependence1时出现爆红的情况。 解决方法 找到idea编辑器右侧的maven按钮…

Linux 中的 chroot 命令及示例

Linux/Unix系统中的chroot命令用于更改根目录。Linux/Unix 类系统中的每个进程/命令都有一个称为root 目录的当前工作目录。它更改当前正在运行的进程及其子进程的根目录。 在此类修改的环境中运行的进程/命令无法访问根目录之外的文件。这种修改后的环境称为“ chroot监狱”或…

maven依赖找不到的解决:手动下载、多镜像导入。

maven中央仓库&#xff0c;远在国外&#xff0c;没下载&#xff0c;因为网络原因迟迟下载不下来&#xff0c;所以我们就需要配置一些国内的镜像仓库&#xff0c;来进行jar包的下载。但是阿里的仓库并没有收录中央仓库的全部jar包&#xff0c;因此导致我们有些jar包下载不下来&a…

什么是数据中台,关于数据中台的6问6答6方法

在大数据/数字孪生时代&#xff0c;数据中台已经成为企业治理数据的核心平台。数据中台不仅处理和整合大量数据&#xff0c;还负责数据的存储、管理和保护工作&#xff0c;确保数据的准确性和可用性。数据中台的特点在于其能够提高业务效率&#xff0c;降低成本&#xff0c;增加…