0%

AFL试用笔记

AFL

1
2
make
make install

程序插桩&编译

有问题程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>

int main(void)
{
char login[32];
char passwd[32];

printf("Login: \n");
gets(login);
printf("Password: \n");
gets(passwd);

if (strcmp(login, "root") == 0) {
if (strcmp(passwd, "1qazxsw2") == 0) {
printf("Access Granted.\n");
return 0;
}
}

printf("Access Denied.\n");
return 1;
}

编译

1
afl-gcc -fno-stack-protector -z execstack vuln1.c -o vuln1

如果用make需要设置环境变量:

1
2
3
git clone --depth=1 https://github.com/gittup/binutils
export CC=afl-gcc
export CXX=afl-g++

准备输入种子

创建输入输出目录:

1
mkdir testcases results

在testcases下建立以下三个文件,AFL会读取每个文件的内容,将每一行输入到vuln1的标准输入中。

1
2
3
4
| test1.txt | test2.txt | test3.txt |
| --------- | --------- | --------- |
| a | root | root |
| a | a | 1qazxsw2 |

种子精简

afl-cmin工具需要一个给定的包含可能的(potential)test case的文件夹,然后运行每一个并将收到的反馈与所有其他的test case进行对比,找到最有效地表示最unique的代码路径的最好的test case。最好的test case被保存到一个新的目录。

afl-tmin工具只用于一个指定的文件。当我们进行fuzzing时,我们不想浪费CPU来处理一些相对于test case表示代码路径来说无用的bit或byte。为了使每一个test case达到表示与原始测试用例相同的代码路径所需的最小值,afl-tmin遍历test case的实际字节,逐步删除很小的数据块,直到删除任意字节都会影响到代码路径表示。

启动AFL

1
2
echo core > /proc/sys/kernel/core_pattern # WSL不需要
afl-fuzz -i ./testcases/ -o ./results/ ./vuln1

如果程序通过参数读取文件,可以用:

1
afl-fuzz -i afl_in -o afl_out ./binutils/readelf -a @@

出现以下界面

1567758115844

查看crash

1
2
3
4
5
6
7
$ ls results/crashes/ -al
total 4
drwx------ 1 root root 512 Sep 6 16:22 .
drwxrwxrwx 1 root root 512 Sep 6 16:21 ..
-rw------- 1 root root 611 Sep 6 16:21 fuzz-AFL试用笔记.txt
-rw------- 1 root root 137 Sep 6 16:21 id:000000,sig:11,src:000000,op:havoc,rep:64
-rw------- 1 root root 65 Sep 6 16:22 id:000001,sig:11,src:000002,op:havoc,rep:128

查看一个测试用例:

1
cat results/crashes/id:000000,sig:11,src:000000,op:havoc,rep:64

kelinci

AFL端

一个C程序作为接口,它与afl-gcc/g++编译出来的程序相同,将AFL的输入通过TCP发送给Java,再将执行结果返回返回给afl。在fuzzerside/下用make编译

JAVA端

用于包装fuzz对象,建立一个TCP服务器,返回退出代码和路径信息。它在instrumentor/目录下用gradle build构建