AFL模糊测试

一、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    (20131020164859)    (Detached)
2428.pts-3.linux    (20131020164805)    (Detached)
2284.pts-3.linux    (20131020161455)    (Detached)
2276.pts-3.linux    (20131020161318)    (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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值