1 从一个test程序开始

1.1 安装AFL

首先,检查本地的clang和llvm是否安装,若未安装则安装一下

sudo apt-get install clang
sudo apt-get install llvm

以上环境确认好后,下载并安装AFL

wget http://lcamtuf.coredump.cx/afl/releases/afl-2.52b.tgz
tar -zxvf afl-2.52b.tgz
cd afl-2.52b
make
sudo make install

以上操作做完,AFL就算是安装完毕啦。现在来看看AFL安装前后代码目录中文件的差别。以下先是安装前的目录:

drwxr-xr-x 10  500  500   4096 Nov  5  2017 .
drwxr-xr-x  3 root root   4096 Jun  2 09:49 ..
-rw-r--r--  1  500  500  23755 Nov  5  2017 afl-analyze.c
-rw-r--r--  1  500  500  15273 Nov  5  2017 afl-as.c
-rw-r--r--  1  500  500  21090 Nov  5  2017 afl-as.h
-rwxr-xr-x  1  500  500  11392 Nov  5  2017 afl-cmin
-rw-r--r--  1  500  500 206076 Nov  5  2017 afl-fuzz.c
-rw-r--r--  1  500  500   8597 Nov  5  2017 afl-gcc.c
-rw-r--r--  1  500  500   5336 Nov  5  2017 afl-gotcpu.c
-rwxr-xr-x  1  500  500   4913 Nov  5  2017 afl-plot
-rw-r--r--  1  500  500  16527 Nov  5  2017 afl-showmap.c
-rw-r--r--  1  500  500  25235 Nov  5  2017 afl-tmin.c
-rwxr-xr-x  1  500  500   3655 Nov  5  2017 afl-whatsup
-rw-r--r--  1  500  500  12565 Nov  5  2017 alloc-inl.h
-rw-r--r--  1  500  500  11216 Nov  5  2017 config.h
-rw-r--r--  1  500  500   6574 Nov  5  2017 debug.h
drwxr-xr-x  2  500  500   4096 Nov  5  2017 dictionaries
drwxr-xr-x  4  500  500   4096 Nov  5  2017 docs
drwxr-xr-x 12  500  500   4096 Nov  5  2017 experimental
-rw-r--r--  1  500  500   2065 Nov  5  2017 hash.h
drwxr-xr-x  2  500  500   4096 Nov  5  2017 libdislocator
drwxr-xr-x  2  500  500   4096 Nov  5  2017 libtokencap
drwxr-xr-x  2  500  500   4096 Nov  5  2017 llvm_mode
-rw-r--r--  1  500  500   6987 Nov  5  2017 Makefile
drwxr-xr-x  3  500  500   4096 Nov  5  2017 qemu_mode
lrwxrwxrwx  1  500  500     24 Nov  5  2017 QuickStartGuide.txt -> docs/QuickStartGuide.txt
lrwxrwxrwx  1  500  500     11 Nov  5  2017 README -> docs/README
drwxr-xr-x  6  500  500   4096 Nov  5  2017 testcases
-rw-r--r--  1  500  500    789 Nov  5  2017 test-instr.c
-rw-r--r--  1  500  500   2292 Nov  5  2017 types.h

安装完成后,代码目录中的文件

drwxr-xr-x 10  500  500   4096 Jun  2 09:47 .
drwxr-xr-x  7 root root   4096 Jun  1 18:42 ..
-rwxr-xr-x  1 root root  90792 Jun  2 09:46 afl-analyze
-rw-r--r--  1  500  500  23755 Nov  5  2017 afl-analyze.c
-rwxr-xr-x  1 root root  50808 Jun  2 09:46 afl-as
-rw-r--r--  1  500  500  15273 Nov  5  2017 afl-as.c
-rw-r--r--  1  500  500  21090 Nov  5  2017 afl-as.h
lrwxrwxrwx  1 root root      7 Jun  2 09:46 afl-clang -> afl-gcc
lrwxrwxrwx  1 root root      7 Jun  2 09:46 afl-clang++ -> afl-gcc
-rwxr-xr-x  1  500  500  11392 Nov  5  2017 afl-cmin
-rwxr-xr-x  1 root root 582872 Jun  2 09:46 afl-fuzz
-rw-r--r--  1  500  500 206076 Nov  5  2017 afl-fuzz.c
lrwxrwxrwx  1 root root      7 Jun  2 09:46 afl-g++ -> afl-gcc
-rwxr-xr-x  1 root root  35376 Jun  2 09:46 afl-gcc
-rw-r--r--  1  500  500   8597 Nov  5  2017 afl-gcc.c
-rwxr-xr-x  1 root root  32240 Jun  2 09:46 afl-gotcpu
-rw-r--r--  1  500  500   5336 Nov  5  2017 afl-gotcpu.c
-rwxr-xr-x  1  500  500   4913 Nov  5  2017 afl-plot
-rwxr-xr-x  1 root root  76840 Jun  2 09:46 afl-showmap
-rw-r--r--  1  500  500  16527 Nov  5  2017 afl-showmap.c
-rwxr-xr-x  1 root root 106280 Jun  2 09:46 afl-tmin
-rw-r--r--  1  500  500  25235 Nov  5  2017 afl-tmin.c
-rwxr-xr-x  1  500  500   3655 Nov  5  2017 afl-whatsup
-rw-r--r--  1  500  500  12565 Nov  5  2017 alloc-inl.h
lrwxrwxrwx  1 root root      6 Jun  2 09:46 as -> afl-as
-rw-r--r--  1  500  500  11216 Nov  5  2017 config.h
-rw-r--r--  1  500  500   6574 Nov  5  2017 debug.h
drwxr-xr-x  2  500  500   4096 Nov  5  2017 dictionaries
drwxr-xr-x  4  500  500   4096 Nov  5  2017 docs
drwxr-xr-x 12  500  500   4096 Nov  5  2017 experimental
-rw-r--r--  1  500  500   2065 Nov  5  2017 hash.h
drwxr-xr-x  2  500  500   4096 Nov  5  2017 libdislocator
drwxr-xr-x  2  500  500   4096 Nov  5  2017 libtokencap
drwxr-xr-x  2  500  500   4096 Nov  5  2017 llvm_mode
-rw-r--r--  1  500  500   6987 Nov  5  2017 Makefile
drwxr-xr-x  3  500  500   4096 Nov  5  2017 qemu_mode
lrwxrwxrwx  1  500  500     24 Nov  5  2017 QuickStartGuide.txt -> docs/QuickStartGuide.txt
lrwxrwxrwx  1  500  500     11 Nov  5  2017 README -> docs/README
drwxr-xr-x  6  500  500   4096 Nov  5  2017 testcases
-rw-r--r--  1  500  500    789 Nov  5  2017 test-instr.c
-rw-r--r--  1  500  500   2292 Nov  5  2017 types.h

可以看到afl安装后,文件夹下多了些二进制文件,是不是很简单。但是,在使用AFL进行fuzz测试前,一定要设置一下core_pattern,否则会报错哦。

root@x:~/afl-test# cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport %p %s %c %d %P
root@x:~/afl-test# echo core > /proc/sys/kernel/core_pattern
root@x:~/afl-test# cat /proc/sys/kernel/core_pattern
core

1.2 跑起来看看

测试程序的代码使用了先知社区的示例代码,如下。平时我们编译c代码使用的是gcc,现在为了使用AFL进行fuzz,我们必须使用1.1节中编译出来的afl-gcc对目标代码进行插桩编译。

#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;
}

接下来,需要做以下三件事:

(1)使用afl-gcc编译目标代码(即插桩)

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

(2)新建测试用例(测试用例即待测程序的输入,可以随意给个文件作为起始用例。AFL会在此用例的基础上进行变异,好的测试用例有助于更快发现更多bug)

$ mkdir testcase
$ cd testcase
$ vim file123
	123
	qwe

(3)输出文件夹(发现的bug等信息会输出到该目录下)

$ mkdir output

最后,指定好以上三项的位置,执行如下命令,就会进入AFL主面板开始fuzz啦

afl-fuzz -i testcase/ -o output/ ./test

这里给一下我fuzz完成后(ctrl +c)的情况:

root@x:~/afl-test# afl-fuzz -i testcase/ -o output/ ./test
afl-fuzz 2.52b by <lcamtuf@google.com>
[+] You have 8 CPU cores and 1 runnable tasks (utilization: 12%).
[+] Try parallel jobs - see /usr/local/share/doc/afl/parallel_fuzzing.txt.
[*] Checking CPU core loadout...
[+] Found a free CPU core, binding to #0.
[*] Checking core_pattern...
[*] Setting up output directories...
[+] Output directory exists but deemed OK to reuse.
[*] Deleting old session data...
[+] Output dir cleanup successful.
[*] Scanning 'testcase/'...
[+] No auto-generated dictionary tokens to reuse.
[*] Creating hard links for all input files...
[*] Validating target binary...
[*] Attempting dry run with 'id:000000,orig:file1'...
[*] Spinning up the fork server...
[+] All right - fork server is up.
    len = 8, map size = 5, exec speed = 306 us
[+] All test cases processed.

[+] Here are some useful stats:

    Test case count : 1 favored, 0 variable, 1 total
       Bitmap range : 5 to 5 bits (average: 5.00 bits)
        Exec timing : 306 to 306 us (average: 306 us)

[*] No -t option specified, so I'll use exec timeout of 20 ms.
[+] All set and ready to roll!

                        american fuzzy lop 2.52b (test)

┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐
│        run time : 0 days, 0 hrs, 24 min, 9 sec       │  cycles done : 2574   │
│   last new path : 0 days, 0 hrs, 24 min, 9 sec       │  total paths : 3      │
│ last uniq crash : 0 days, 0 hrs, 14 min, 10 sec      │ uniq crashes : 6      │
│  last uniq hang : none seen yet                      │   uniq hangs : 0      │
├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤
│  now processing : 2 (66.67%)        │    map density : 0.01% / 0.01%         │
│ paths timed out : 0 (0.00%)         │ count coverage : 1.00 bits/tuple       │
├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤
│  now trying : splice 9              │ favored paths : 3 (100.00%)            │
│ stage execs : 47/48 (97.92%)        │  new edges on : 3 (100.00%)            │
│ total execs : 5.41M                 │ total crashes : 385k (6 unique)        │
│  exec speed : 3330/sec              │  total tmouts : 1 (1 unique)           │
├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤
│   bit flips : 0/128, 0/125, 0/119                   │    levels : 2          │
│  byte flips : 0/16, 0/13, 0/7                       │   pending : 0          │
│ arithmetics : 2/896, 0/129, 0/9                     │  pend fav : 0          │
│  known ints : 0/78, 0/339, 0/308                    │ own finds : 2          │
│  dictionary : 0/0, 0/0, 0/0                         │  imported : n/a        │
│       havoc : 4/2.31M, 2/3.09M                      │ stability : 100.00%    │
│        trim : 83.10%/16, 0.00%                      ├────────────────────────┘
^C────────────────────────────────────────────────────┘          [cpu000: 23%]

+++ Testing aborted by user +++
[+] We're done here. Have a nice day!

1.3 分析结果

fuzz完成后,根据AFL面板的信息我们知道有6个crash,因此我们进入output目录去分析

root@x:~/afl-test/output# ls -al
total 108
drwxr-xr-x 5 root root  4096 Jun  2 10:15 .
drwxr-xr-x 4 root root  4096 Jun  2 10:08 ..
drwx------ 2 root root  4096 Jun  2 10:25 crashes
-rw------- 1 root root     1 Jun  2 10:39 .cur_input
-rw------- 1 root root 65536 Jun  2 10:16 fuzz_bitmap
-rw------- 1 root root   739 Jun  2 10:39 fuzzer_stats
drwx------ 2 root root  4096 Jun  2 10:15 hangs
-rw------- 1 root root 15250 Jun  2 10:39 plot_data
drwx------ 3 root root  4096 Jun  2 10:15 queue
  • crashes/ 文件夹中存放了fuzz过程中产生的crash样例

  • hangs/ 文件夹中存放了fuzz过程中产生的超时样例

  • queue/ 文件夹中存放了fuzz过程中不同执行路径的测试用例

使用xxd命令查看产生的6个crash样例,然后可以将样例作为输入用gdb调试test程序。

root@x:~/afl-test/output/crashes# xxd id:000000,sig:06,src:000000,op:havoc,rep:64
00000000: 1f20 0820 fe1f 2004 0020 200c 0064 1ffe  . . .. ..  ..d..
00000010: 017f 2033 ff20 2000 180b 2020 2008 20e2  .. 3.  ...   . .
00000020: 1f20 e21f ff01 00ff ff20 2020 2002 0024  . .......    ..$
00000030: ff00 0000 0000 0100 203a 200c 20e2 1f01  ........ : . ...
00000040: 2020 0019 0110 510b 2020 2002 0020 2020    ....Q.   ..
00000050: 2020 2020 2020 2020 2020 2020 2020 2020
00000060: 2020 2020 2020 2020 2020 2020 2020 0820                .
00000070: 2020 0c20 e21f 2002 7f80 2020 6b20 0101    . .. ...  k ..
00000080: 0101 0101 1d01 0101 0101 0101 0101 0101  ................
00000090: 0101 0101 0101 0101 0101 0101 40ff 2080  ............@. .
root@x:~/afl-test/output/crashes# xxd id:000001,sig:11,src:000002,op:arith8,pos:0,val:-28
00000000: 4662 6262 627f 001a                      Fbbbb...
root@x:~/afl-test/output/crashes# xxd id:000002,sig:06,src:000002,op:havoc,rep:128
00000000: 3685 c13e 3e3e 002f 3e3e 203e 3e6a 3e3e  6..>>>./>> >>j>>
00000010: 3ee4 343e 3e3e 3e53 3d5b 146a 3e3e 2e3e  >.4>>>>S=[.j>>.>
00000020: 3e3e 3e4e 3e6a 3e5c 273e 3e3e 3e3e 6a3e  >>>N>j>\'>>>>>j>
00000030: 3e8c 3e3e 3e3e 4a3e 6a3e 3e27 d9c5 a8c5  >.>>>>J>j>>'....
00000040: 5314 533e 3e1d 3e3e 3e3e 3e3e 3e3e 3e3e  S.S>>.>>>>>>>>>>
00000050: 3e3e 3e3e 3e3e 3e3e 3e3e 3e3e 3e3e 3e3e  >>>>>>>>>>>>>>>>
00000060: 3e3e 3e3e 3e2f 3e3e 343e 3e6a 3e3e 0080  >>>>>/>>4>>j>>..
00000070: 3e3e 3e27 fb9c 9c9c 9c9c 9c9c 9c9c c5c5  >>>'............
00000080: c5a6 c5e0 7fff                           ......
root@x:~/afl-test/output/crashes# xxd id:000003,sig:06,src:000001+000002,op:splice,rep:4
00000000: 2532 6e5a 627f fc19                      %2nZb...
root@x:~/afl-test/output/crashes# xxd id:000004,sig:06,src:000001,op:havoc,rep:128
00000000: 4106 00f2 5fdf 03a0 c5f6 dfdf 40a0 dff4  A..._.......@...
00000010: f5ff e5f4 f4fe 04f5 f4f4 f4f4 f4f4 f4f4  ................
00000020: 0ef5 f4ff 800d f4b4 f400 f4f4 0edb eadf  ................
00000030: f8df dfe7 f400 eadd dfe7 df02 dff8 00d7  ................
00000040: dfdf 01df dfdf ecdb eadf f8df dfe7 dfdf  ................
00000050: dff8 1ad7 dfdf 01da dfdf dedf dfdf dfdf  ................
00000060: dfdf dfdf dfdf dfdf dfdf dfde dfdf dfde  ................
00000070: ed00 e3e3 e316 0d00 0000 1000 00c0 dfe7  ................
00000080: dfdf df00 d701 dfd6 0e                   .........
root@x:~/afl-test/output/crashes# xxd id:000005,sig:11,src:000002+000001,op:splice,rep:16
00000000: 4132 800f fe0f 0f0f 0f0f 220f 0f0f 0f0f  A2........".....
00000010: 0f0f 0f0f 0f0f 0f0f 0f0f 0ef0 0f22 0f0f  ............."..
00000020: 0f0f 0f0f 0f0f 0f0f 0f0f 0f0f 0f0f 0f0f  ................
00000030: 0f0f 0f0f 0f0f 0f0f 0f0f 0f7f ff64 0f0f  .............d..
00000040: fc0e 000f 0f0f 0f0f 0fff                 ..........

1.4 继续已停止的fuzz测试

afl-fuzz -i- -o output ./test

1.5 结合ASAN的fuzz

ASAN只推荐在32位机器上使用。如果在64位机器上使用,也需将AFL编译成32位的。

Method 1. set AFL_USE_ASAN=1 before calling ‘make clean all’
Method 2. add -fsanitize=address option into makefile

1.6 文件fuzz

1.2和1.3节讲的是从stdin读取输入的目标程序,这里补充一下从文件读取输入的目标程序,以 readelf -a xxfile 为例。

有源码插桩:

afl-fuzz -i in/ -o out/ ./readelf -a @@

无源码插桩:

afl-fuzz -i in/ -o out/ -Q ./readelf -a @@

2 无源码AFL fuzz

无源码时,即无法对源码使用功能afl-gcc/afl-g++进行插桩编译时,整个fuzz过程会变得异常缓慢。但确实没有源码的情况下可使用该方法。

步骤1:编译支持无源码fuzz的AFL

在afl源码目录下:

cd qemu_mode
./build_qemu_support.sh
cd ..
make install 

步骤2:跟1.2和1.3节一样,只不过有两点差异:

  • 待测程序是未经afl-gcc插桩编译过的,如使用gcc编译

  • 执行afl-fuzz命令时多加了一个-Q参数

afl-fuzz -i testcase/ -o output/ -Q ./test

3 AFL文档学习

afl面板介绍:

https://lcamtuf.coredump.cx/afl/status_screen.txt

4 参考文档

初探AFL-Fuzz

小白初学AFL(American Fuzzy Lop)

AFL文件变异一览

AFL(American Fuzzy Lop) Cautions