基于AFLFast的fuzz自动化漏洞挖掘(2)

发布于:2025-07-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

基于AFLFast的fuzz自动化漏洞挖掘(2)

这里主要讲讲实验的环境配置和论文中主要涉及到的环境复现。还是看到我们的项目地址:mboehme/aflfast: AFLFast (extends AFL with Power Schedules)

在开始之前呢,这里有个快速上手指南挺重要的,也简单放这里看一下(究极省流版本,还是看看吧):

=====================
AFL quick start guide(AFL快速开始指南)
=====================

You should read docs/README. It's pretty short. If you really can't, here's
how to hit the ground running:
建议先阅读docs/README文档(内容很简短)。如果实在来不及,以下是快速上手指南:

1) Compile AFL with 'make'. If build fails, see docs/INSTALL for tips.
使用'make'命令编译AFL。若构建失败,请查阅docs/INSTALL中的解决方案。
2) Find or write a reasonably fast and simple program that takes data from
   a file or stdin, processes it in a test-worthy way, then exits cleanly.
   If testing a network service, modify it to run in the foreground and read
   from stdin. When fuzzing a format that uses checksums, comment out the
   checksum verification code, too.
准备一个处理速度较快、逻辑简单的测试程序,要求:
从文件或stdin读取数据
对数据进行可测试的处理
处理完成后正常退出
若测试网络服务,需修改为前台运行并从stdin读取
若测试含校验码的格式,建议先注释掉校验验证代码
   The program must crash properly when a fault is encountered. Watch out for
   custom SIGSEGV or SIGABRT handlers and background processes. For tips on
   detecting non-crashing flaws, see section 11 in docs/README.
程序遇到错误时必须能正常崩溃。注意排查自定义的SIGSEGV/SIGABRT处理程序及后台进程。非崩溃类缺陷检测技巧见docs/README第11节。
3) Compile the program / library to be fuzzed using afl-gcc. A common way to
   do this would be:
使用afl-gcc编译目标程序/库,典型编译方式:
   CC=/path/to/afl-gcc CXX=/path/to/afl-g++ ./configure --disable-shared
   make clean all

   If program build fails, ping <afl-users@googlegroups.com>.
若编译失败,请联系afl-users@googlegroups.com

4) Get a small but valid input file that makes sense to the program. When
   fuzzing verbose syntax (SQL, HTTP, etc), create a dictionary as described in
   dictionaries/README.dictionaries, too.
准备:
小而有效的输入样本
对于复杂语法(SQL/HTTP等),建议dictionaries/README.dictionaries创建字典

5) If the program reads from stdin, run 'afl-fuzz' like so:

   ./afl-fuzz -i testcase_dir -o findings_dir -- \
     /path/to/tested/program [...program's cmdline...]

   If the program takes input from a file, you can put @@ in the program's
   command line; AFL will put an auto-generated file name in there for you.
运行afl-fuzz:
从stdin读取的程序:
./afl-fuzz -i 测试用例目录 -o 输出目录 --
/path/to/测试程序 [程序参数...]
从文件读取的程序:
在命令行中用@@占位符,AFL会自动替换为生成的文件名

6) Investigate anything shown in red in the fuzzer UI by promptly consulting
   docs/status_screen.txt.
实时关注fuzzer界面中的红色警告,具体含义参考docs/status_screen.txt
That's it. Sit back, relax, and - time permitting - try to skim through the
following files:

  - docs/README               - A general introduction to AFL,
  - docs/perf_tips.txt        - Simple tips on how to fuzz more quickly,
  - docs/status_screen.txt    - An explanation of the tidbits shown in the UI,
  - docs/parallel_fuzzing.txt - Advice on running AFL on multiple cores.
基础操作就是这些。后续建议抽空阅读:
docs/README AFL基础介绍
docs/perf_tips.txt 性能优化技巧
docs/status_screen.txt 界面元素说明
docs/parallel_fuzzing.txt 多核并行fuzzing方案

基础环境设置:

先简单说说本地环境:ubuntu22.04,用的vmware启的虚拟机。

安装依赖项:

sudo apt update
sudo apt install -y build-essential clang llvm-dev libtool automake bison flex

(本地提前也是有python3/git这些的,缺啥自己单独再去拉就ok)

zip包拉下来,在项目文件夹里面直接:

make

即可。

检查结果:

./afl-fuzz

会显示有相关的help文档:

image-20250726144929187

这里为了方便,所以我在~/.bashrc里面创建了符号链接,这个看个人需要吧,把路径写进去更新就可以了。记得重新关闭一下终端。

同样这里方便起见,需要重新导一下as路径:

sudo vim ~/.bashrc

export PATH="$PATH:/home/linux/Desktop/AFLFast/aflfast-master"
export AFL_PATH=/home/linux/Desktop/AFLFast/aflfast-master

关闭终端就可以生效了。

构建clang-fast:

(这个是基于llvm的插桩,简单来说就是更快一点)

cd llvm_mode/
make

但是这里直接执行会产生报错,本地llvm版本是14,会导致版本太高了。变动也有两种方式,一个是直接改源代码适应llvm14;另一个是直接启用虚拟环境的形式安装需要的llvm6。

这里暂时先搁置一下,先用gcc演示完了再说,ubuntu对llvm的支持不太好后面再补充。

最后简单说下项目结构:

aflfast-master/
├── afl-fuzz, afl-gcc, afl-cmin, afl-analyze ...   ← 核心工具编译产物
├── docs/                                          ← 📘 官方文档说明
├── testcases/                                     ← 📂 样例输入文件(fuzz 测试用)
├── experimental/                                  ← 🧪 实验性调度器、脚本、原型
├── llvm_mode/                                     ← 🧠 支持 LLVM 编译器的插桩逻辑
├── qemu_mode/                                     ← ⚙️ QEMU 模式源码(支持二进制fuzz)
├── libdislocator/                                 ← 🧨 自定义 malloc 实现(便于检测溢出)
├── libtokencap/                                   ← 📚 Token 捕捉器(自动构建字典)
├── dictionaries/                                  ← 📖 输入格式字典样例
├── Makefile                                       ← 构建入口
├── README / Readme.md / QuickStartGuide.txt       ← 🔰 快速上手 / 介绍文档
└── test-instr.c, *.c, *.h                         ← 工具源码
(感谢gpt)

实验复现:

test-instr.c编译实现最小功能测试:

这里使用的是基于gcc的,编译首页项目文件里的test-instr.c:

afl-gcc test-instr.c -o test-instr

(一般来说按照上面的流程走完了不会报错)

image-20250727215913180

编译完成。

这个东西是AFL程序自带的一个微型测试样例:

  • 读取输入文件内容(通过 argv[1]);
  • 判断输入是否包含特定字符组合,比如:
    "0" -> "1" -> "2" -> "3",模拟一个“深路径”。

目的:

  • 验证 AFL 是否能通过变异探索更多路径
  • 模拟真实程序中的“逻辑触发路径”,以观测 AFL 的覆盖率追踪效果;
  • 是 AFL 的最简使用示例,复现实验从它开始非常合适。
跑通最小demo:

创建输入种子:

mkdir in
echo "0" > in/input.txt

创建输出并运行:

mkdir out
./afl-fuzz -i in -o out -- ./test-instr @@

接下来执行即可:

image-20250728163208181

image-20250728163241462

然后简单说说这个功能模块,这里因为没有特定的停止时间,所以可以手动终止。

test-instr说明:

/*
   american fuzzy lop - a trivial program to test the build
   --------------------------------------------------------

   Written and maintained by Michal Zalewski <lcamtuf@google.com>

   Copyright 2014 Google Inc. All rights reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at:

     http://www.apache.org/licenses/LICENSE-2.0

 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv) {

  char buf[8];

  if (read(0, buf, 8) < 1) {
    printf("Hum?\n");
    exit(1);
  }

  if (buf[0] == '0')
    printf("Looks like a zero to me!\n");
  else
    printf("A non-zero value? How quaint!\n");

  exit(0);

}

为什么要看这个呢,主要是为了看最小测试样例,通过简单的代码审计,可以清晰的看出来这里变异成0的时候会出现不一样的结果。

所以前面seed给一个0就有问题:(

这里正确做法是:seed给一个非0的数字,然后执行:

afl-fuzz -i in -o out -- ./test-instr

在out里面会记录这个产生异常的点:

image-20250728175050370

到这里最最简单的测试demo就跑完了。

仪盘表功能说明:

顶部信息栏
scss


复制编辑
american fuzzy lop (fast) 2.51b (test-instr)
  • fast:当前使用的 power schedule(如:fast / explore / exploit)
  • 2.51b:AFLFast 版本
  • test-instr:被 fuzz 的程序名
🔵 左上角:Process Timing(执行过程时间)
字段 含义
run time 当前模糊测试已运行的总时长
last new path 上次发现新路径的时间。如果一直是 none yet,说明没找到新路径
last uniq crash 上次发现“唯一崩溃”的时间
last uniq hang 上次发现“唯一 hang”输入的时间(超时)

⚠️ check syntax!:这通常提示测试程序没有正确处理输入,或种子文件不符合格式。

🔵 中上:Cycle Progress(测试周期进展)
字段 含义
now processing 当前正在处理的路径在整个输入队列中的进度
paths timed out 超时的路径占比
🔵 中部:Stage Progress(执行阶段进度)
字段 含义
now trying 当前使用的变异策略阶段,如 havoc
stage execs 当前阶段已完成的执行次数和百分比
execs done 累计总执行次数(对测试程序的调用次数)
exec speed 每秒执行次数(越高越好)
🔵 右上角:Overall Results(测试总体结果)
字段 含义
cycles done 输入队列轮询的次数
total paths 发现的不同路径数量(路径覆盖)
uniq crashes 发现的唯一崩溃输入数量
uniq hangs 发现的唯一超时输入数量
🔵 右中:Map Coverage(覆盖信息)
字段 含义
map density 覆盖图中活跃位的密度,越高说明代码覆盖越多
count coverage 每个路径平均 hit 的覆盖位数量(越高越复杂)
🔵 中右:Findings in Depth(发现的输入信息)
字段 含义
favored paths 被 AFL 判定为优先变异的路径数量
new edges 找到的新边数量(控制流路径点)
total crashes 总共发现的崩溃数
total timeouts 超时的总数
🔵 左下:Fuzzing Strategy Yields(各策略效果统计)

AFL 会用多种策略(bit flip、arith、havoc 等)变异输入,这里显示每种策略找到多少新路径:

类型 说明(每列是不同位宽)
bit flips 单比特、双比特、四比特翻转
byte flips 单字节翻转
arith 算术操作变异(+/- 数值)
known ints 插入已知“魔法数”整数(如 0x100)
dictionary 使用字典替换
havoc 混合乱序变异(主要方式)
trim 剪枝优化率
🔵 右下角:Path Geometry(路径结构)
字段 含义
levels 当前队列中的最大路径深度
pending 待处理的输入数量
pending fav 优先处理的输入数量
own finds 当前 fuzzer 发现的新路径数量(多 fuzzer 场景下可比较)
imported 从其他 AFL 实例导入的路径数量(用于 parallel 模式)
stability 目标程序行为是否稳定(波动大时不可信)
🟩 右下角小方块:CPU 使用情况
  • 类似 [cpu000:108%]:表示当前绑定 CPU 负载状态
  • 如果跑多个实例,每个都可以绑定不同核心以充分利用多核

问题解决:

问题1:系统崩溃模式导致错误

image-20250728161548123

大概就是fuzz会收集crush报错,但是ubuntu原生的报错把这个给拦截了。

解决方法:把这个对应的模块关掉就行:

sudo sh -c 'echo core > /proc/sys/kernel/core_pattern'
cat /proc/sys/kernel/core_pattern
#用来检查模式

临时设置,在系统重新启动后会还原。


网站公告

今日签到

点亮在社区的每一天
去签到