0%

AFL++的安装和基本使用

AFL++

简介

AFL++是在AFL基础上进行改进的模糊测试工具,其源码可以从github上得到。AFL++会将测试用例进行变异并作为输入交给目标程序,当这个测试用例能够触发新的路径时,就会被保存并进一步变异测试。

安装流程

AFL++的安装可以通过docker安装,也可以通过源码安装,本实验通过源码安装
根据readme进行安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 系统版本 Ubuntu20

sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
sudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev
sudo apt-get install -y ninja-build # for QEMU mode
sudo apt-get install -y cpio libcapstone-dev # for Nyx mode
sudo apt-get install -y wget curl # for Frida mode
sudo apt-get install python3-pip # for Unicorn mode
git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
make distrib
sudo make install

基本使用

利用afl-training中的vulnerable.c来进行实验

有源码

1
2
3
CC=afl-clang-fast AFL_HARDEN=1 make
echo core | sudo tee /proc/sys/kernel/core_pattern
afl-fuzz -i inputs -o out ./vulnerable

可以看到afl++能够成功运行这个目标程序,并且能够触发崩溃

无源码

测试整个程序

1
2
3
gcc -o vul vulnerable.c 
echo core | sudo tee /proc/sys/kernel/core_pattern
afl-fuzz -Q -i inputs -o out ./vul

由于这个程序没有利用afl++编译插桩过,因此需要利用qemu模式,即运行afl++的时候需要加上参数-Q

测试某个函数

vulnerable.c

假设要fuzz的目标函数是process,查阅AFL++的文档,了解到可以用AFL++的persistent mode。文档中提到,使用该模式需要确认目标函数的地址,并且编写persistent hook来传入对应的参数。因此实验流程如下

  1. 确认目标函数在程序中的偏移地址
1
2
3
4
5
6
# 确定process的地址
nm vul | grep "T process"
# 值为00000000000012c9
# AFL++在qemu模式的情况下进行fuzz的时候,如果程序启用了PIE,需要加上基地址0x4000000000
# 可以使用 checksec --file=vul 来确认是否启用了PIE
export AFL_QEMU_PERSISTENT_ADDR=0x40000012c9
  1. 参照样例编写persistent hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "/home/xu/lab-tools/git-lab/aflplusplus/AFLplusplus/qemu_mode/qemuafl/qemuafl/api.h"

#include <stdio.h>
#include <string.h>
#define g2h(x) ((void *)((unsigned long)(x) + guest_base))
#define h2g(x) ((uint64_t)(x) - guest_base)

void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
uint8_t *input_buf, uint32_t input_buf_len) {

// In this example the register RDI is pointing to the memory location
// of the target buffer, and the length of the input is in RSI.
// This can be seen with a debugger, e.g. gdb (and "disass main")

printf("Placing input into 0x%lx\n", regs->rdi);
printf("length of input_buf is %d\n",input_buf_len);
memcpy(g2h(regs->rdi), input_buf, input_buf_len);
}
#undef g2h
#undef h2g

int afl_persistent_hook_init(void) {

// 1 for shared memory input (faster), 0 for normal input (you have to use
// read(), input_buf will be NULL)
return 1;
}
  1. 运行AFL++来进行模糊测试
1
2
3
4
5
6
7
8
9
gcc -fPIC -shared read_into_rdi.c -o read_into_rdi.so
export AFL_QEMU_PERSISTENT_HOOK=./read_into_rdi.so
export AFL_QEMU_PERSISTENT_GPR=1
export AFL_QEMU_PERSISTENT_MEM=1
export AFL_QEMU_PERSISTENT_EXITS=1

mkdir outputs
export AFL_NO_FORKSRV=1
AFL_DEBUG=1 afl-fuzz -Q -i inputs -o outputs ./vul

可以看到afl++能够成功运行程序并且触发崩溃

由于无法从AFL++的界面中直观地看出是否进入persistent mode,因此可以通过在vulnerable.c中添加一些输入来确定是否达到我们的预期,比如在main函数中添加printf("function : main\n"),在process函数中添加printf("function : process\n"),然后在运行afl++的指令前加上AFL_DEBUG=1,如果看到main的输出只有一次,process函数反复输出,说明程序的运行达到了我们的预期。