一、AFL
基于覆盖率, qemu llvm等多项技术的fuzzing工具
https://github.com/google/AFL
二、使用方法
2.1安装
make
make install
2.2编译目标程序
2.2.1 程序是用autoconf构建,那么此时只需要执行如下即可
CC=/usr/local/bin/afl-gcc CXX=/usr/local/bin/afl-g++ ./configure --disable-shared
make clean
make
2.2.2 程序不是用autoconf构建,那么直接修改Makefile文件中的编译器为afl-gcc/g++
为了后期更好的分析crash,在此处可以开启Address Sanitizer(ASAN)这个内存检测工具,此工具可以更好的检测出缓存区溢出、UAF 等内存漏洞,开启方法如下:
AFL_USE_ASAN=1 ./configure CC=afl-gcc CXX=afl-g++ LD=afl-gcc--disable-shared
AFL_USE_ASAN=1 make
2.2.3 llvm 模式
普通模式可能造成部分可执行文件编译后运行异常。所以可以 apt install llvm clang afl 增加 afl-clang-fast/afl-clang-fast++。
编译方式
$ cd llvm_mode
$ apt-get install clang
$ export LLVM_CONFIG=`which llvm-config` && make && cd ..
$ ./configure --disable-shared CC="afl-clang-fast" CXX="afl-clang-fast++"
2.2.4 screen多实例启动
echo core >/proc/sys/kernel/core_pattern
screen afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
screen [-AmRvx -ls -wipe][-d <作业名称>][-h <行数>][-r <作业名称>][-s <shell>][-S <作业名称>]
# screen -ls //显示已创建的screen终端
There are screens on:
2433.pts-3.linux (2013年10月20日 16时48分59秒) (Detached)
2428.pts-3.linux (2013年10月20日 16时48分05秒) (Detached)
2284.pts-3.linux (2013年10月20日 16时14分55秒) (Detached)
2276.pts-3.linux (2013年10月20日 16时13分18秒) (Detached)
4 Sockets in /var/run/screen/S-root.
# screen -r 2276 //连接 screen_id 为 2276 的 screen终端
并行测试
cat /proc/cpuinfo\| grep "cpu cores"\| uniq
afl-fuzz并行Fuzzing,一般的做法是通过-M参数指定一个主Fuzzer(Master Fuzzer)、通过-S参数指定多个从Fuzzer(Slave Fuzzer)。
$ screen afl-fuzz -i testcases/ -o sync_dir/ -M fuzzer1 -- ./program
$ screen afl-fuzz -i testcases/ -o sync_dir/ -S fuzzer2 -- ./program
$ screen afl-fuzz -i testcases/ -o sync_dir/ -S fuzzer3 -- ./program
...
afl-whatsup工具可以查看每个fuzzer的运行状态和总体运行概况,加上-s选项只显示概况,其中的数据都是所有fuzzer的总和。
afl-whatsup -s syncdir
还afl-gotcpu工具可以查看每个核心使用状态。
语料输入改进
经过试用,发现@@参数会把变异后的语料路径传递给argv, 所以采用文件读取的方式可以重构参数。
#define AFL_INIT_ARGV(p) do { argv = afl_init_argv_f(&argc, argv, p); } while (0)
#define AFL_INIT_SET0(_p) do { \
argv = afl_init_argv(&argc); \
argv[0] = (_p); \
if (!argc) argc = 1; \
} while (0)
#define MAX_CMDLINE_LEN 100000
#define MAX_CMDLINE_PAR 1000
static char** afl_init_argv_f(int* argc, char *argv[], char *redch) {
static char in_buf[MAX_CMDLINE_LEN] = {0};
static char* ret[MAX_CMDLINE_PAR];
char buffer[1024];
char* ptr = in_buf, *p;
int rc = 0;
int len = 0, flag = 0;
char * tmp = redch;
/*打开重定向文件,获取输入变异字节*/
int in;
if(argv[1]==NULL)
return 0;
in = open(argv[1], O_RDONLY, S_IRUSR);
if (-1 == in) // 打开文件失败,则异常返回
{
printf("corpus open failed \n");
return NULL;
}
printf("open file success %s\n", argv[1]);
/*改变第一参数 edit*/
len = strlen(argv[0]);
memcpy(ptr, argv[0], len);
ptr = ptr+len;
*ptr = 0x0;
ptr++;
if(tmp){
len = strlen(tmp);
memcpy(ptr, tmp, len);
ptr = ptr+len+1;
}
p = ptr;
printf("first \n");
/*缓存文件内容*/
if ((flag = read(in, buffer, 1024)) > 0){ memcpy(p, buffer, flag);
p = p + flag + 1;
}
close(in);
printf("读取文件成功 \n");
/*重构参数 argv */
ptr = in_buf;
while (*ptr) {
ret[rc] = ptr;
if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++;
rc++;
while(*ptr){
if(*ptr == 0x20){
*ptr == 0x0;
break;
}
//if(*ptr==0xa){
// *ptr = 0x0;
// goto error;
//}
ptr++;
}
ptr++;
}
*argc = rc;
for(int j = 0; j< rc; j++){
if(strstr(ret[j], "-")&&strlen(ret[j]>2)){
goto error;
}
}
return ret;
error:
return NULL;
}
经过上述参数构造,可以方便的获取语料变异数据。
这块很容易影响整个fuzzer的效率,还是最好不变不加@@参数。
#define AFL_INIT_ARGV() do { argv = afl_init_argv(&argc); } while (0)
#define AFL_INIT_SET0(_p) do { \
argv = afl_init_argv(&argc); \
argv[0] = (_p); \
if (!argc) argc = 1; \
} while (0)
#define MAX_CMDLINE_LEN 100000
#define MAX_CMDLINE_PAR 1000
static char** afl_init_argv(int* argc) {
static char in_buf[MAX_CMDLINE_LEN];
static char* ret[MAX_CMDLINE_PAR];
char* ptr = in_buf;
int rc = 0;
if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0);
while (*ptr) {
ret[rc] = ptr;
if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++;
rc++;
while (*ptr) ptr++;
ptr++;
}
*argc = rc;
return ret;
}
#undef MAX_CMDLINE_LEN
#undef MAX_CMDLINE_PAR
#define AFL_INIT_ARGV() do { argv = afl_init_argv(&argc, argv); } while (0)
#define AFL_INIT_SET0(_p) do { \
argv = afl_init_argv(&argc); \
argv[0] = (_p); \
if (!argc) argc = 1; \
} while (0)
#define MAX_CMDLINE_LEN 100000
#define MAX_CMDLINE_PAR 1000
static char** afl_init_argv(int* argc, char * argv[]) {
static char in_buf[MAX_CMDLINE_LEN];
static char* ret[MAX_CMDLINE_PAR];
char * arg0 = argv[0];
char* ptr = in_buf;
int rc = 1;
int len;
ret[0] = ptr;
len = strlen(arg0);
memcpy(ptr, arg0, len);
ptr = ptr + len;
memcpy(ptr, "\0", 1);
ptr = ptr + 1;
if (read(0, ptr, MAX_CMDLINE_LEN - 2 - len -1) < 0);
while (*ptr) {
ret[rc] = ptr;
if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++;
rc++;
while (*ptr) {
if(*ptr == 0x20){
*ptr = 0x0;
break;
}
if(*ptr == 0xa){
*ptr = 0x0;
break;
}
ptr++;
}
ptr++;
}
*ptr = 0x0;
*argc = rc;
return ret;
}
#undef MAX_CMDLINE_LEN
#undef MAX_CMDLINE_PAR
int writetag(int argc, char *argv[]){
char *s;//”Linux Programmer!\n”;
int i, fd;
fd=open("/tmp/temp",O_WRONLY|O_APPEND);
for(i = 0; i < argc; i++){
s = argv[i];
printf("arg %d %s \n", i, s, strlen(s));
write(fd, s, strlen(s));
write(fd, " ", 1);
}
write(fd, "\n", 1);
close(fd);
}
fuzz造成的僵尸进程,清理下
# -*- coding:utf-8 -*-
import os
import re
import psutil
import time
print('------')
# os.popen() 调用 read() 方法有返回值
while(1):
procs = psutil.pids()
for i in procs:
if(i in psutil.pids()):
p = psutil.Process(i)
t = p.cpu_times().user
if(p.name()=="vi" and t > 0.30):
print(p.name() + " pid: %d"%i)
print(p.cpu_times().user)
os.system('kill -9 %d'%i)
time.sleep(10)
解析crash结果
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
void main()
{
int fd,size,rc=0,i=0,j=0;
char *ret[50],*ptr;
char s[]="Linux Programmer!\n", buffer[50];
fd=open("/tmp/temp",O_RDONLY);
size=read(fd, buffer, sizeof(buffer));
ptr = buffer;
for(i = 0;i < size;i++){
printf("(%d)%02x", i, buffer[i]&0xff);
}
printf("\n");
while (*ptr) {
ret[rc] = ptr;
rc++;
for (;*ptr;ptr++) {
if(*ptr == 0x20){
*ptr = 0x0;
break;
}
if(*ptr == 0xa){
*ptr = 0x0;
break;
}
}
ptr++;
}
ptr= buffer;
for(i = 0; i < rc;i++){
printf("arg %d %s \n", i, ret[i]);
for(j=0;j<strlen(ret[i]);j++){
printf("%02x ",*(ret[i]+j));
}
printf("\n");
}
close(fd);
}
参考:https://paper.seebug.org/841/