使用AFL进行Fuzz

1、AFL下载和安装

1
2
3
4
5
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar xvf afl-latest.tgz
cd afl-2.52b
# make && make install
sudo make && sudo make install

安装好后,修改.bashrc加入环境变量export PATH=/usr/home:$PATH后即可使用。PATH为AFL的目录。

2、AFL Fuzzing步骤

1、使用afl-gcc编译项目代码,将编译脚本中的CC=afl-gcc/CXX=afl-g++;
2、新建两个文件夹,如fuzz_in/fuzz_out,文件夹名随意;
3、将初始化testcase放到fuzz_in目录下;
4、执行afl-fuzz -i fuzz_in -o fuzz_out ./xxx @@ xxx为可执行程序名,@@表示从文件中读入
5、观察fuzzing结果,如有crash,定位问题。

3、示例代码

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
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

void vuln(char *buf) {
int n = 0;
if(buf[0] == 'a') n++;
if(buf[1] == 'f') n++;
if(buf[2] == 'l') n++;
if(buf[3] == '!') n++;

if(n == 4) {
printf("awesome!\n");
raise(SIGSEGV);
}else{
printf("wrong!\n");
}
}

int main(int argc, char *argv[])
{
char buf[40] = {0};
FILE *input = NULL;
input = fopen(argv[1], "r");
if (input != 0)
{
fscanf(input, "%s", &buf);
printf("buf is %s\n", buf);
vuln(buf);
fclose(input);
}
else
{
printf("bad file!");
}
return 0;
}

1、有源码AFL测试

编译代码:

1
afl-gcc afl.c -o afl_test

准备环境
新建输入、输出文件夹:

1
mkdir fuzz_in fuzz_out

准备初始化testcase, 将testcase内容随意写成aaa:

1
echo aaa > fuzz_in/testcase

开始Fuzzing

1
afl-fuzz -i fuzz_in -o fuzz_out ./afl_test @@

65GOfO.png

我们可以在fuzz_out/crashes中看到得到了三个crash。
65a70H.png

65diAs.png

前两个crash发生了栈溢出。
65wQsS.png

2、无源码AFL测试

上面是对进简单的有源码的程序进行测试的,但是实际情况下不可能每次都有源码进行插桩编译测试,afl使用了qemu模式进行测试,只要在之前的命令的基础上加上-Q的参数即可,但是要先进行安装,在afl的根目录打开终端执行以下命令。

1
2
3
4
5
6
cd qemu_mode

./build_qemu_support.sh

cd ..
make install

在执行./build_qemu_support.sh命令时有两个报错

1
2
3
static declaration of ‘memfd_create’ follows non-static declaration

qemu_mode/qemu-2.10.0/rules.mak:66: util/memfd.o] 错误 1

解决方法:
在patches目录下,将syscall.diff文件内容改成:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
--- qemu-2.10.0-clean/linux-user/syscall.c	2020-03-12 18:47:47.898592169 +0100
+++ qemu-2.10.0/linux-user/syscall.c 2020-03-12 19:16:41.563074307 +0100
@@ -34,6 +34,7 @@
#include <sys/resource.h>
#include <sys/swap.h>
#include <linux/capability.h>
+#include <linux/sockios.h> // https://lkml.org/lkml/2019/6/3/988
#include <sched.h>
#include <sys/timex.h>
#ifdef __ia64__
@@ -116,6 +117,8 @@ int __clone2(int (*fn)(void *), void *ch
#include "qemu.h"

+extern unsigned int afl_forksrv_pid;
+
#ifndef CLONE_IO
#define CLONE_IO 0x80000000 /* Clone io context */
#endif

@@ -256,7 +259,9 @@ static type name (type1 arg1,type2 arg2,
#endif

#ifdef __NR_gettid
-_syscall0(int, gettid)
+// taken from https://patchwork.kernel.org/patch/10862231/
+#define __NR_sys_gettid __NR_gettid
+_syscall0(int, sys_gettid)
#else
/* This is a replacement for the host gettid() and must return a host
errno. */
@@ -6219,7 +6224,8 @@ static void *clone_func(void *arg)
cpu = ENV_GET_CPU(env);
thread_cpu = cpu;
ts = (TaskState *)cpu->opaque;
- info->tid = gettid();
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ info->tid = sys_gettid();
task_settid(ts);
if (info->child_tidptr)
put_user_u32(info->tid, info->child_tidptr);
@@ -6363,9 +6369,11 @@ static int do_fork(CPUArchState *env, un
mapping. We can't repeat the spinlock hack used above because
the child process gets its own copy of the lock. */
if (flags & CLONE_CHILD_SETTID)
- put_user_u32(gettid(), child_tidptr);
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ put_user_u32(sys_gettid(), child_tidptr);
if (flags & CLONE_PARENT_SETTID)
- put_user_u32(gettid(), parent_tidptr);
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ put_user_u32(sys_gettid(), parent_tidptr);
ts = (TaskState *)cpu->opaque;
if (flags & CLONE_SETTLS)
cpu_set_tls (env, newtls);
@@ -11402,7 +11410,8 @@ abi_long do_syscall(void *cpu_env, int n
break;
#endif
case TARGET_NR_gettid:
- ret = get_errno(gettid());
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ ret = get_errno(sys_gettid());
break;
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:

创建memfd_create.diff文件,写入:

1
2
3
4
5
6
7
8
9
10
11
12
diff -ru qemu-2.10.0-clean/util/memfd.c qemu-2.10.0/util/memfd.c
--- qemu-2.10.0-clean/util/memfd.c 2018-11-20 18:11:00.170271506 +0100
+++ qemu-2.10.0/util/memfd.c 2018-11-20 18:11:13.398423613 +0100
@@ -37,7 +37,7 @@
#include <sys/syscall.h>
#include <asm/unistd.h>

-static int memfd_create(const char *name, unsigned int flags)
+int memfd_create(const char *name, unsigned int flags)
{
#ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);

最后在加上build_qemu_support.sh文件中加上

1
patch -p1 <../patches/memfd_create.diff || exit 1

完成之后再执行

1
2
3
./build_qemu_support.sh
cd ..
make install

然后用gcc对测试文件进行编译

1
gcc -g -o afl_test2 afl.c

执行命令:

1
afl-fuzz -i fuzz_in -o fuzz_out -Q ./afl_test2 @@

65zL9g.png

6IYkTI.png