时时勤拂拭,勿使惹尘埃

TOC

Categories

AFL(二)afl-qemu无源码fuzz


使用brew/apt方式安装的afl没有afl-qemu-trace(不支持使用QEMU模式),所以我们需要下载afl的源码自己编译。

0x1 安装配置

编译完成后,需要配置qemu环境。不过,afl提供了一个脚本,在qemu-mode文件夹下的build_qemu_support.sh。运行这个脚本来配置qemu环境,但qemu-mode只支持linux,macOS可以在docker上使用,docker使用参考macOS上使用kali-linux for docker
$ ./build_qemu_support.sh
=================================================
AFL binary-only instrumentation QEMU build script
=================================================

[*] Performing basic sanity checks...
[-] Error: QEMU instrumentation is supported only on Linux.
编译成功信息如下:
[+] Build process successful!
[*] Copying binary...
-rwxr-xr-x 1 root root 10956864 Dec 13 12:26 ../afl-qemu-trace
[+] Successfully created '../afl-qemu-trace'.
[*] Testing the build...
[+] Instrumentation tests passed.
[+] All set, you can now use the -Q mode in afl-fuzz!

遇到的坑

  1. 运行后会提示libtool等资源库没有安装,使用sudo apt install安装即可:
    $ ./build_qemu_support.sh
    =================================================
    AFL binary-only instrumentation QEMU build script
    =================================================
    [*] Performing basic sanity checks...
    [-] Error: 'libtool' not found, please install first.
    $ apt-get install libtool-bin
    
  2. 安装一些软件包时,有时会出现找不到glib2的错误:
    $ apt install glib2
    Reading package lists... Done
    Building dependency tree
    Reading state information... Done
    E: Unable to locate package glib2
    
    查看build_qemu_support.sh相关代码,需要在/usr/include/glib-2.0/或者/usr/local/include/glib-2.0/有相关库:
    if [ ! -d "/usr/include/glib-2.0/" -a ! -d "/usr/local/include/glib-2.0/" ]; then
    echo "[-] Error: devel version of 'glib2' not found, please install first."
    exit 1
    
    可通过安装以下工具来解决:
    sudo apt-get install libgtk2.0-dev
    
  3. qemu编译错误:
    util/memfd.c:40:12: error: static declaration of 'memfd_create' follows non-static declaration
    static int memfd_create(const char *name, unsigned int flags)
             ^~~~~~~~~~~~
    In file included from /usr/include/x86_64-linux-gnu/bits/mman-linux.h:115,
                  from /usr/include/x86_64-linux-gnu/bits/mman.h:45,
                  from /usr/include/x86_64-linux-gnu/sys/mman.h:41,
                  from /root/afl-2.52b/qemu_mode/qemu-2.10.0/include/sysemu/os-posix.h:29,
                  from /root/afl-2.52b/qemu_mode/qemu-2.10.0/include/qemu/osdep.h:104,
                  from util/memfd.c:28:
    
    afl默认qemu版本太老,官方已经patch
    ./configure
    @@ -3923,7 +3923,7 @@ fi
    # check if memfd is supported
    memfd=no
    cat > $TMPC << EOF
    -#include <sys/memfd.h>
    +#include <sys/mman.h>
    
    ./util/memfd.c
    @@ -31,9 +31,7 @@
    #include "qemu/memfd.h"
    -#ifdef CONFIG_MEMFD
    -#include <sys/memfd.h>
    -#elif defined CONFIG_LINUX
    +#if defined CONFIG_LINUX && !defined CONFIG_MEMFD
    
    修改完成后使用如下指令重打包,再修改build_qemu_support.sh里的QEMU_SHA384重新编译即可,SHA384值可以使用sha384sum获取:
    $ tar -Jcf qemu-2.10.0.tar.xz qemu-2.10.0/
    $ sha384sum qemu-2.10.0.tar.xz
    

更换qemu版本

如果想使用更新版本qemu,可以直接将build_qemu_support.sh设置的版本换成官方的较新版本,但更换版本后问题较多:
#VERSION="2.10.0"
VERSION="2.12.1"
#QEMU_SHA384="68216c935487bc8c0596ac309e1e3ee75c2c4ce898aab796faa321db5740609ced365fedda025678d0"
QEMU_SHA384="92957551a3a21b1ed48dc70d9dd91905859a5565ec98492ed709a3b64daf7c5a0265d670030ee7e6d16da96436795435"
  1. patch错误:
    patching file linux-user/elfload.c
    Hunk #2 succeeded at 2233 (offset 146 lines).
    Hunk #3 succeeded at 2268 (offset 146 lines).
    patching file accel/tcg/cpu-exec.c
    Hunk #1 succeeded at 37 (offset 1 line).
    Hunk #2 succeeded at 147 with fuzz 2 (offset 1 line).
    Hunk #3 FAILED at 369.
    1 out of 3 hunks FAILED -- saving rejects to file accel/tcg/cpu-exec.c.rej
    
    patch针对的是上层路径的文件更新,可以直接注释掉
    #patch -p1 <../patches/cpu-exec.diff || exit 1
    
  2. 缺少pixman,安装即可
    apt-get install libpixman*
    
  3. LINK错误:
    LINK    x86_64-linux-user/qemu-x86_64
    linux-user/syscall.o: In function `do_syscall':
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/syscall.c:11983: undefined reference to `afl_forksrv_pid'
    linux-user/elfload.o: In function `load_elf_image':
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2236: undefined reference to `afl_entry_point'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2271: undefined reference to `afl_start_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    /root/afl-2.52b/qemu_mode/qemu-2.12.0/linux-user/elfload.c:2275: undefined reference to `afl_end_code'
    collect2: error: ld returned 1 exit status
    Makefile:193: recipe for target 'qemu-x86_64' failed
    make[1]: *** [qemu-x86_64] Error 1
    Makefile:478: recipe for target 'subdir-x86_64-linux-user' failed
    make: *** [subdir-x86_64-linux-user] Error 2
    
    上层patch中对源码文件做了修改,导致部分外部变量没有导入,注释掉相关patch即可:
    # patch -p1 <../patches/elfload.diff || exit 1
    # patch -p1 <../patches/syscall.diff || exit 1
    
  4. afl-qemu-trace测试失败
    [+] Successfully created '../afl-qemu-trace'.
    [*] Testing the build...
    [-] Error: afl-qemu-trace instrumentation doesn't seem to work!
    
    应该是用64位afl-qemu-trace工具测试32位程序引起的,忽略即可,或者通过如下指令指定32位架构:
    $ CPU_TARGET=i386 ./build_qemu_support.sh
    
    结果如下,实际也是忽略了测试:
    [+] Successfully created '../afl-qemu-trace'.
    [!] Note: can't test instrumentation when CPU_TARGET set.
    [+] All set, you can now (hopefully) use the -Q mode in afl-fuzz!
    

0x2 使用afl-qemu fuzz

0x21 同cpu架构程序

以系统中wget指令为例,使用如下指令执行fuzz,-Q参数表示使用qemu模式;-m参数设置使用内存大小,不设置则默认200MB:
$ afl-fuzz -i fuzz_in -o fuzz_out -m 200 -Q wget @@
运行成功界面如下(ubuntu for docker):

0x22 不同cpu架构程序

0x221 获取目标文件

Android adbd程序为例,获取adbd文件:
$ file /system/bin/adbd
/system/bin/adbd: ELF executable, 64-bit LSB arm64, static, for Android 28, BuildID=2ef781f7497eaad0b8ba145996afd9a1, not stripped
$ adb pull /system/bin/adbd ./

0x222 编译目标架构qemu模式

如果fuzz的程序与qemu架构不同,则可能出现如下错误,需要用之前方式指定正确架构进行编译qemu模式:
$ afl-fuzz -i fuzz_in -o fuzz_out/ -Q ./adbd @@
...
[-] Hmm, looks like the target binary terminated before we could complete a
    handshake with the injected code. There are two probable explanations:

    - The current memory limit (200 MB) is too restrictive, causing an OOM
      fault in the dynamic linker. This can be fixed with the -m option. A
      simple way to confirm the diagnosis may be:

      ( ulimit -Sv $[199 << 10]; /path/to/fuzzed_app )

      Tip: you can use http://jwilk.net/software/recidivm to quickly
      estimate the required amount of virtual memory for the binary.

    - Less likely, there is a horrible bug in the fuzzer. If other options
      fail, poke <[email protected]> for troubleshooting tips.

[-] PROGRAM ABORT : Fork server handshake failed
         Location : init_forkserver(), afl-fuzz.c:2253
编译arm64版本工具,qemu支持的架构类型见./qemu-2.10.0/linux-user/host/目录,其中arm为32位arm,aarch64为64位arm:
$ ls  ./qemu-2.10.0/linux-user/host/
aarch64  arm  i386  ia64  mips  ppc  ppc64  s390  s390x  sparc  sparc64  x32  x86_64
以64位arm为例,编译指令如下:
$ CPU_TARGET=aarch64 ./build_qemu_support.sh
编译aarch64版本后,继续执行fuzz:
$ ../afl-2.52b/afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./adbd
依然错误:
[-] PROGRAM ABORT : Test case 'id:000000,orig:testcase' results in a crash
         Location : perform_dry_run(), afl-fuzz.c:2852

0x223 qemu user mode检查

由于可执行文件的架构不一样,所以需要按照QEMU user mode仿真做一下检查,运行成功后再进行fuzz,详情参考:IoT(七)通过qemu调试IoT固件和程序里的用户模式调试程序。
在该文中,adbd程序由于缺少外部资源依然执行失败(如需对有外部资源依赖的程序进行fuzz,可在该平台编译对应版本的fuzz工具),故重新选取能够执行成功的静态编译工具,如Android上的三方工具busybox:
$ file busybox
busybox: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped
$ qemu-aarch64 busybox
BusyBox v1.25.0-NetHunter (2016-03-19 19:36:31 EDT) multi-call binary.
BusyBox is copyrighted by many authors between 1998-2015.
Licensed under GPLv2. See source distribution for detailed
copyright notices.

Usage: busybox [function [arguments]...]
   or: busybox --list[-full]
   or: busybox --install [-s] [DIR]
   or: function [arguments]...
....
此处可成功对busybox进行fuzz:
../afl-2.52b/afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./busybox @@

3 条评论:

  1. 大佬您好!我最近想尝试使用afl对路由器固件中的一些程序进行fuzz。我已经参照您的这个博客编译好了mipsel架构的工具,也参照https://github.com/shellphish/afl-other-arch配好了环境变量。但是很奇怪的是,我用afl-showmap -Q -m none -o out ./bin/busybox是可以运行的,但是在afl-fuzz -Q -m none -i in/ -o out/ ./busybox就会报Fork server handshake failed,在指定了AFL_NO_FORK=1之后,虽然不报Fork server handshake failed的错了,但是就会报no instrumentation detected的错。正常来说,afl的qemu模式不就是针对闭源二进制程序的吗?就算没有instrumentation应该也可以运行啊。您说应该去测试静态链接的工具,可是我这个路由器固件里就算busybox都是动态链接的,我在想这会是个原因吗?您文中提到“如需对有外部资源依赖的程序进行fuzz,可在该平台编译对应版本的fuzz工具”我并不是太明白您的意思,能有个具体的例子吗?总体来说,您对于如何使用afl对于mipsel架构,动态链接的固件二进制程序进行模糊测试有什么高见吗?若大佬能略微指点一二,感激不尽!

    回复删除
  2. mipsel架构没试过,不过AFL编译链接可执行文件和库文件时,可以使用static link静态链接libxxx.a文件,对于动态链接库,需要将动态链接库(如当前目录)加到环境变量中:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

    回复删除
  3. 感谢大佬的回复!我这个问题已经解决了。说来惭愧,这是个很蠢的问题,让大佬见笑了……我在怎么尝试都不行的时候,感觉大家的环境都是Ubuntu。而我之前用的环境一直是kali,而且我看所有关于AFL的文档都没有说环境一定要是Ubuntu。我换了Ubuntu之后,啥都好使了orz另外我还有个小小的疑惑。我之前也看到有人提出想要加动态链接库的话,需要export LD_LIBRARY_PATH。但是https://github.com/shellphish/afl-other-arch中说要加动态链接库的话,需要export QEMU_LD_PREFIX。在我这里,export QEMU_LD_PREFIX好使,而export LD_LIBRARY_PATH不好使。我没有研究过AFL的源码,个人猜测https://github.com/shellphish/afl-other-arch不仅仅简化了构建其他CPU架构下的qemu mode的过程,可能还改了其他的东西。最后再次感谢大佬的回复!

    回复删除