FUZZ(1):初探AFL
196082 慢慢好起来

前言

本篇文章主要是为网鼎杯线下赛做准备,不会涉及到AFL的原理,主要的方向为AFL的使用。

AFL-FUZZ介绍

AFL则是fuzzing的一个很好用的工具,全称是American Fuzzy Lop,由Google安全工程师Michał Zalewski开发的一款开源fuzzing测试工具,可以高效地对二进制程序进行fuzzing,挖掘可能存在的内存安全漏洞,如栈溢出、堆溢出、UAF、double free等。由于需要在相关代码处插桩,因此AFL主要用于对开源软件进行测试。当然配合QEMU等工具,也可对闭源二进制代码进行fuzzing,但执行效率会受到影响。

白盒下的FUZZ

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
#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

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

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

return 0;
}

大概流程:

首先是用afl-gcc编译源代码进行插桩,然后以测试文件为输入,然后启动afl-fuzz程序,将testcase作为程序的输入执行程序,afl会在这个testcase的基础上进行自动变异输入,使得程序产生crash,产生了crash就会被记录起来

编译插桩

首先是面对上面这类小文件时采取的方法就是直接进行编译

1
afl-gcc -g -o test test.c

但是面对编译项目时,大多会使用到Makefile。如果存在configure的话肯定是可以在里面直接进行修改,如果没有的话可以直接修改Makefile或者添加以下内容:

1
2
CC=/path/to/afl/afl-gcc
CXX=/path/to/afl/afl-g++

当然面对clang或者clang++的话也是一样的。

开始FUZZ

对那些可以直接从stdin读取输入的目标程序来说,语法如下:

1
./afl-fuzz -i testcase_dir -o findings_dir /path/to/program […params…]

对从文件读取输入的目标程序来说,要用“@@”,语法如下:

1
./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@

而对于这里命令为:afl-fuzz -i input_dir -o output_dir ./test

其中-i选项时输入测试文件的目录,-o选项时输出结果文件的目录。

对于这个简单的例子,测试文件只需要随便输入点东西就行,这里选择输入hello

image-20230318133051229一般会出现上图中的问题,解决办法就是

1
2
sudo su
echo core >/proc/sys/kernel/core_pattern

image-20230318134324644

上图就是AFL的界面,上面的内容根据前面的注释基本可以猜出来一二,不过跑了十分钟只跑出了五处crash就挺离谱的,这个跑的结果与testcase和电脑性能以及运气都有一定关系。

分析crashes

id:000000,sig:06,src:000001,op:havoc,rep:128样例

1
2
3
4
5
6
7
8
9
➜  crashes xxd id:000000,sig:06,src:000001,op:havoc,rep:128 
00000000: 8001 8001 92a8 c3e4 c3c3 c3fa c3ea e4c3 ................
00000010: c3c3 fac3 ea04 2310 0423 1000 1010 0000 ......#..#......
00000020: 9b00 1000 009b 0100 f5f5 0423 1000 1010 ...........#....
00000030: 0000 9b00 1000 009b 0100 f5f5 f5f5 f5f5 ................
00000040: f5f5 f5f5 f5f5 f5f5 f5f5 00fa 0423 1000 .............#..
00000050: 1000 0000 20f5 0064 1000 009b 0010 0000 .... ..d........
00000060: 9b01 0001 92fa 0023 1000 1000 0003 2300 .......#......#.
00000070: 6410 0000 9b15 d.....

可以猜测为栈溢出

id:000001,sig:06,src:000001,op:havoc,rep:128样例

1
2
3
4
5
6
7
8
➜  crashes xxd id:000001,sig:06,src:000001,op:havoc,rep:128 
00000000: 7fb8 b7b8 207f 0001 7fb7 f9ff ffff f9ff .... ...........
00000010: ffff ff17 ffff ffff 7f7f 7fb7 f9ff ffff ................
00000020: f9ff ffff ff17 ffff ffff e0ff b8ff ffdd ................
00000030: b8b7 b8da 8008 00f9 fa00 00f9 f087 8080 ................
00000040: 80ff f9ff ffff ff17 ffff ffff 7f7f 7fb7 ................
00000050: f9ff ffff f9ff ffff ff17 ffff ffff ff80 ................
00000060: 69b8 00d8 bdda 80b8 00d8 bdda ff7f 7fb8 i...............

一样的应该也是栈溢出导致的

id:000002,sig:11,src:000000,op:flip1,pos:1样例

1
2
➜  crashes xxd id:000002,sig:11,src:000000,op:flip1,pos:1  
00000000: 7425 7374 t%st

这里应该是格式化字符串漏洞引起的crash

id:000003,sig:11,src:000000,op:havoc,rep:64样例

1
2
3
4
5
➜  crashes xxd id:000003,sig:11,src:000000,op:havoc,rep:64 
00000000: 4662 4062 4040 0000 8040 7f40 1000 4040 Fb@b@@...@.@..@@
00000010: 6240 4000 7740 407f 4040 8040 403f 6565 b@@.w@@.@@.@@?ee
00000020: 6565 4044 4040 3340 403f 6565 6565 4040 ee@D@@3@@?eeee@@
00000030: 4040 3340 4040 4040 40 @@3@@@@@@

这里应该是以F开头且长度为6引起的crash

id:000004,sig:11,src:000002,op:havoc,rep:8样例

1
2
3
4
5
6
➜  crashes xxd id:000004,sig:11,src:000002,op:havoc,rep:8  
00000000: 4141 4141 4141 4141 4141 4141 4141 4141 AAAAAAAAAAAAAAAA
00000010: 4141 4141 1e41 4141 4141 4141 4141 4132 AAAA.AAAAAAAAAA2
00000020: 4141 4141 4141 1e41 4141 4141 4141 4141 AAAAAA.AAAAAAAAA
00000030: 4141 4141 4141 4141 4141 4141 412a 4134 AAAAAAAAAAAAA*A4
00000040: 41b8 A.

这里应该是以A开头并且长度为66引起的crash

总结

可以看到上面五个样例基本覆盖了源码中存在的漏洞。

黑盒下的FUZZ

黑盒模式需要使用qemu_mode,所以最好从开始就使用源码进行编译,这里可能会出现很多问题,大多都可以在网上搜到解决办法

这里直接使用以下命令进行编译。

1
gcc -g ./test.c -o test

开始FUZZ

这里的FUZZ选项略有不同,需要加上-Q选项。

1
afl-fuzz -i ./input_dir -o ./output_dir -Q ./test

image-20230318141300808

虽然可以看到这里被保存的crashes有6个,比上面的还多一个,但是看total crashes远远少于上面的。可以看出来用qemu的性能远远小于插桩的。

这里就不再分析输出的crashes文件了。

文件读取输入的情况

首先做以下准备

1
2
3
➜  input_dir mkdir file
➜ input_dir cp ../../testcases/others/elf/small_exec.elf ./file
➜ afl_test cp /usr/bin/readelf ./

随后直接进行fuzz

1
../afl-fuzz -i ./input_dir/file -o ./output_dir -Q ./readelf -a @@

image-20230318142936126跑了十分钟一次crash都没有的,当然跑再久应该也是一样的,这里就不再继续跑了。


参考资料:
https://xz.aliyun.com/t/4314
https://www.cjovi.icu/fuzzing/1138.html

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量