一、实验环境
1、ubuntu 16.04
2、AFL,按照“小白初学AFL”中的步骤在机器上安装好AFL
二、目标程序
1、目标程序代码
将该代码复制下来,并保存命名为“test.c”
#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;
}
2、目标程序分析
在main函数中,定义了一个100字节大小的字符数组变量,用于存储数据。然后通过get()函数,从用户处获取字符串,然后将该字符串传入vuln()函数中。vuln()函数有三个if分支,当字符串的第一个字符是A,且字符串长度为66时,则抛出异常退出。当字符串的第一个字符是F且字符串长度为6时,同样抛出异常且退出。如果以上两个条件都不满足,则打印“it is good”。
首先,定义的100字节大小的字符数组变量,那么可能存在用户输入超过100字节的情况导致的缓冲区溢出。其次,存在字符串首字节为A且字符串长度为66的异常。最后,存在字符串首字节为F且字符串长度为6的异常。
三、模糊测试
1、对代码进行编译
注意此时的编译需要使用AFL的编译器,针对目标程序进行插桩,以便AFL计算代码覆盖率等
afl-gcc -g -o afl_test test.c
afl-gcc是AFL的编译器,能够在编译程序的同时对程序进行插桩。
-g是编译器参数,能够调用gdb,在编译的同时能够用于调试
-o是输出文件参数,后面跟的“afl_test”是输出文件名
该步骤将test.c通过AFL的编译器编译成了名为afl_test的可执行程序
编译完成后,能够看到目录下多出了一个名为afl_test的可执行程序
2、将coredump输出到文件
使系统将coredump输出到文件,而不是上报给系统的处理程序
echo core > /proc/sys/kernel/core_pattern
3、建立testcase文件夹
该文件夹中存放一些种子,AFL可以将种子进行变异,输入可执行程序中进行模糊测试
在目录下建立testcase文件夹,并在其中创建了一个写了“abc”的文件“test.txt”
4、对目标程序fuzz
输入命令对目标程序进行fuzz
afl-fuzz -i testcase -o output ./afl_test
-i参数指定输入种子文件夹
-o参数指定输出文件夹,里面保存程序的crash等信息
可以看到AFL开始对目标程序进行fuzz,目前已经得到3个uniq crash,可以进入output文件夹查看得到的crash具体情况
四、crash分析
cat crash_file ./afl_test
使用上述命令,将crash_file中的内容输入到afl_test中,就会发生crash
发生crash时的终端如下所示