Proj THUDBFuzz: AFLplusplus
Cite
Andrea Fioraldi, Dominik Maier, Heiko Eißfeldt, and Marc Heuse. “AFL++: Combining incremental steps of fuzzing research”. In 14th USENIX Workshop on Offensive Technologies (WOOT 20). USENIX Association, Aug. 2020.
为了对比,应当使用fuzzbench aflplusplus setup或者afl-clang-fast
+ AFL_LLVM_COMLOG=1
插桩
插桩的编译器选择
- clang 11+:使用
afl-clang-lto
或者afl-clang-lto++
- clang 3.8+: 使用
afl-clang-fast
或者afl-clang-fast++
- gcc 5+: 使用
afl-gcc-fast
或者afl-g++-fast++
afl-gcc/afl-g++ or afl-clang/afl-clang++
Overview
framework的主要元素是:
- 带有多种变异方法和设置参数的fuzzer:
afl-fuzz
- 不同的源代码插桩模块:LLVM mode,
afl-as
, GCC plugin - 不同的二进制代码插桩模块: QEMU mode, Unicorn mode, QBDI mode
- testcase/corpus reduction:
afl-tmin
,afl-cmin
- 其他Helper libraries: libtokencap, libdislocator, libcompcov
Features
已经支持:
- llvm 11
- QEMU 5.1
- BSD and Android Support
- AFL fast的power schedule功能 https://github.com/mboehme/aflfast
- MOpt变异 https://github.com/puppet-meteor/MOpt-AFL
- InsTrim: 高效的CFG llvm_mode插桩实现 https://github.com/csienslab/instrim
- afl-fuzz Python mutator + llvm mode whitelist support: https://github.com/choller/afl
- Custom mutator by a library
- Unicorn mode: allows fuzzin of binaries from different platforms
- LAF-Intel/CompCov support for llvm_mode,qemu_mode and unicorn_mode
- NeverZero patch, 使得wrapping map值不为0
- Persistent mode and deferred forkserver for qemu_mode
- Win32 PE binary-only fuzzing with QEMU and wine
- Radamsa mutator
- QBDI mode(fuzz android native libs)
- CmpLog instrumentation for LLVM and QEMU(inspired by RedQueen)
Fuzzing binary-only programs with afl++ 如何对只有源码的程序进行模糊测试
afl-fuzz -n
是不插桩模式,但是效果并不好。- 官方建议按照以下优先顺序
- qemu_mode + persistent mode最快,不过要求程序足够稳定
- retrowrite
- afl-dyninst
- qemu_mode with AFL_ENTRYPOINT
QEMU
qemu支持对只有二进制的程序进行模糊测试,甚至还能跨平台测试。
qemu会把测试程序的速度减慢50%,但是下面的选项能够增加qemu测试的速度
- using AFL_ENTRYPOINT to move the forkserver entry to a later basic block in the binary (+5-10% speed)
- persistent mode(150-300% overall speed increase)
- using AFL_CODE_START/AFL_CODE_END to only instrument specific parts
honggfuzz也有qemu_mode,但是只能增进1.5%的速度
WINE+QEMU
wine + python3 + pefile(python package) + qemu可以在Linux上测win32
UNICORN
UNICORN是QEMU的fork,不过,UNICORN提供的不是整个系统或者用户界面仿真。用户需要从头写runtime environment 或者是loaders。此外,UNICORN中的区块链已经被删除了。
AFL Frida
frida-gum + utils/afl_frida
,此时使用afl-frida.c
作为模板来调用library中的目标函数
AFL Untracer
utils/afl_untracer
,这里用afl-untracer.c
做模板
DYNINST
类似Pintool和Dynamorio的二进制工具框架。不过Dyninst在加载时会对目标进行插桩(而Pintool和Dynamorio不会),然后再保存对应二进制。接着,用afl-fuzz
来模糊测修改后的二进制。一般来说,我们会用dyninst插桩并将必要的信息加入二进制。但这样做存在一个隐患,会改变进程空间中的地址,这导致待测程序很可能会崩溃。DYNINST会给速度带来15到35%的降低。
https://github.com/vanhauser-thc/afl-dyninst
Retrowrite
如果待测程序携带符号表,而且是编译时选择了位置无关,并且没有用大多数c++features,那就可以用retrowrite。只会降低15%到20%的性能
https://github.com/HexHive/retrowrite
MCSEMA
理论上可行
https://github.com/lifting-bits/mcsema
INTEL-PT
硬件需求: 新世代Intel CPU
效果: 利用intels processor trace
问题:缓存区很小,而且PT传回来的debug信息的解码非常麻烦需要大量CPU计算来解码。
As a result, the overall speed decrease is about 70-90%
https://github.com/junxzm1990/afl-pt
https://github.com/hunter-ht-2018/ptfuzzer
CoreSight
ARM,但是还没找到实现
Frida
动态插桩引擎,特殊在于其用python写的,然后用JS来跑具体执行。该软件通常用在逆向手机软件上。
https://github.com/andreafioraldi/frida-fuzzer
https://github.com/AFLplusplus/AFLplusplus/tree/frida
PIN & DYNAMORIO
动态插桩引擎,能够在运行时获取基本块信息。
会严重拖慢时间
Dynamorio solutions:
https://github.com/vanhauser-thc/afl-dynamorio
https://github.com/mxmssh/drAFL
https://github.com/googleprojectzero/winafl/ <= very good but windows only
Pintool solutions:
https://github.com/vanhauser-thc/afl-pin
https://github.com/mothran/aflpin
https://github.com/spinpx/afl_pin_mode <= only old Pintool version supported
ENV
为了避免出现ENV的笔误,Afl++中的一些工具可能会警告自己不用也不知道的环境变量,那时可以用AFL_IGNORE_UNKNOWN_ENVS
来避免这个问题
AFL custom mutators
该目录下一共有grammar mutator, honggfuzz, libfuzzer, libprotobuf-mutator-example, radamsa, rust, symcc这几个子目录。此外,还提到了superion这个项目。
grammar mutator
使用git submodule update --init
来初始化子项目
该项目是AFL++内部为了兼容高度结构化的测试样例而做的,其功能或者思想基本来自 F1 fuzzer and Nautilus
该项目能够做以下操作:
- Tree-based mutation: rules mutation, random mutation, random recursive mutation, splicing mutation
- Tree-based trimming: subtree trimming, recursive trimming
- An ANTLR4 shim for parsing fuzzing test cases during the runtime
Usage
static void usage(u8 *argv0, int more_help) {
SAYF(
"\n%s [ options ] -- /path/to/fuzzed_app [ ... ]\n\n"
"Required parameters:\n"
" -i dir - input directory with test cases\n"
" -o dir - output directory for fuzzer findings\n\n"
"Execution control settings:\n"
" -p schedule - power schedules compute a seed's performance score:\n"
" fast(default), explore, exploit, seek, rare, mmopt, "
"coe, lin\n"
" quad -- see docs/power_schedules.md\n"
" -f file - location read by the fuzzed program (default: stdin "
"or @@)\n"
" -t msec - timeout for each run (auto-scaled, default %u ms). "
"Add a '+'\n"
" to auto-calculate the timeout, the value being the "
"maximum.\n"
" -m megs - memory limit for child process (%u MB, 0 = no limit "
"[default])\n"
" -Q - use binary-only instrumentation (QEMU mode)\n"
" -U - use unicorn-based instrumentation (Unicorn mode)\n"
" -W - use qemu-based instrumentation with Wine (Wine "
"mode)\n\n"
"Mutator settings:\n"
" -D - enable deterministic fuzzing (once per queue entry)\n"
" -L minutes - use MOpt(imize) mode and set the time limit for "
"entering the\n"
" pacemaker mode (minutes of no new paths). 0 = "
"immediately,\n"
" -1 = immediately and together with normal mutation).\n"
" See docs/README.MOpt.md\n"
" -c program - enable CmpLog by specifying a binary compiled for "
"it.\n"
" if using QEMU, just use -c 0.\n"
" -l cmplog_opts - CmpLog configuration values (e.g. \"2AT\"):\n"
" 1=small files (default), 2=larger files, 3=all "
"files,\n"
" A=arithmetic solving, T=transformational solving.\n\n"
"Fuzzing behavior settings:\n"
" -Z - sequential queue selection instead of weighted "
"random\n"
" -N - do not unlink the fuzzing input file (for devices "
"etc.)\n"
" -n - fuzz without instrumentation (non-instrumented mode)\n"
" -x dict_file - fuzzer dictionary (see README.md, specify up to 4 "
"times)\n\n"
"Testing settings:\n"
" -s seed - use a fixed seed for the RNG\n"
" -V seconds - fuzz for a specified time then terminate\n"
" -E execs - fuzz for an approx. no. of total executions then "
"terminate\n"
" Note: not precise and can have several more "
"executions.\n\n"
"Other stuff:\n"
" -M/-S id - distributed mode (see docs/parallel_fuzzing.md)\n"
" -M auto-sets -D, -Z (use -d to disable -D) and no "
"trimming\n"
" -F path - sync to a foreign fuzzer queue directory (requires "
"-M, can\n"
" be specified up to %u times)\n"
" -d - skip deterministic fuzzing in -M mode\n"
" -T text - text banner to show on the screen\n"
" -I command - execute this command/script when a new crash is "
"found\n"
//" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap
//" "file\n"
" -C - crash exploration mode (the peruvian rabbit thing)\n"
" -b cpu_id - bind the fuzzing process to the specified CPU core "
"(0-...)\n"
" -e ext - file extension for the fuzz test input file (if "
"needed)\n\n",
argv0, EXEC_TIMEOUT, MEM_LIMIT, FOREIGN_SYNCS_MAX);
if (more_help > 1) {
#if defined USE_COLOR && !defined ALWAYS_COLORED
#define DYN_COLOR \
"AFL_NO_COLOR or AFL_NO_COLOUR: switch colored console output off\n"
#else
#define DYN_COLOR
#endif
SAYF(
"Environment variables used:\n"
"LD_BIND_LAZY: do not set LD_BIND_NOW env var for target\n"
"ASAN_OPTIONS: custom settings for ASAN\n"
" (must contain abort_on_error=1 and symbolize=0)\n"
"MSAN_OPTIONS: custom settings for MSAN\n"
" (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)\n"
"AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists\n"
"AFL_BENCH_JUST_ONE: run the target just once\n"
"AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found\n"
"AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)\n"
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash\n"
"AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs\n"
"AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators\n"
"AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule\n"
"AFL_DEBUG: extra debugging output for Python mode trimming\n"
"AFL_DEBUG_CHILD: do not suppress stdout/stderr from target\n"
"AFL_DISABLE_TRIM: disable the trimming of test cases\n"
"AFL_DUMB_FORKSRV: use fork server without feedback from target\n"
"AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found\n"
"AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)\n"
"AFL_FAST_CAL: limit the calibration stage to three cycles for speedup\n"
"AFL_FORCE_UI: force showing the status screen (for virtual consoles)\n"
"AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)\n"
"AFL_HANG_TMOUT: override timeout value (in milliseconds)\n"
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers\n"
"AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars\n"
"AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first\n"
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)\n"
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size\n"
" the target was compiled for\n"
"AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value\n"
" then they are randomly selected instead all of them being\n"
" used. Defaults to 200.\n"
"AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing\n"
"AFL_NO_ARITH: skip arithmetic mutations in deterministic stage\n"
"AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target\n"
"AFL_NO_CPU_RED: avoid red color for showing very high cpu usage\n"
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver\n"
"AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)\n"
"AFL_NO_UI: switch status screen off\n"
DYN_COLOR
"AFL_PATH: path to AFL support binaries\n"
"AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module\n"
"AFL_QUIET: suppress forkserver status messages\n"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target\n"
"AFL_TARGET_ENV: pass extra environment variables to target\n"
"AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup\n"
"AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable\n"
"AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking\n"
"AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs\n"
"AFL_STATSD: enables StatsD metrics collection\n"
"AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)\n"
"AFL_STATSD_PORT: change default statsd port (default: 8125)\n"
"AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)\n"
" Supported formats are: 'dogstatsd', 'librato',\n"
" 'signalfx' and 'influxdb'\n"
"AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)\n"
"AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)\n"
//"AFL_PERSISTENT: not supported anymore -> no effect, just a warning\n"
//"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning\n"
"\n"
);