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
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(
"
%s [ options ] -- /path/to/fuzzed_app [ ... ]
"
"Required parameters:
"
" -i dir - input directory with test cases
"
" -o dir - output directory for fuzzer findings
"
"Execution control settings:
"
" -p schedule - power schedules compute a seed's performance score:
"
" fast(default), explore, exploit, seek, rare, mmopt, "
"coe, lin
"
" quad -- see docs/power_schedules.md
"
" -f file - location read by the fuzzed program (default: stdin "
"or @@)
"
" -t msec - timeout for each run (auto-scaled, default %u ms). "
"Add a '+'
"
" to auto-calculate the timeout, the value being the "
"maximum.
"
" -m megs - memory limit for child process (%u MB, 0 = no limit "
"[default])
"
" -Q - use binary-only instrumentation (QEMU mode)
"
" -U - use unicorn-based instrumentation (Unicorn mode)
"
" -W - use qemu-based instrumentation with Wine (Wine "
"mode)
"
"Mutator settings:
"
" -D - enable deterministic fuzzing (once per queue entry)
"
" -L minutes - use MOpt(imize) mode and set the time limit for "
"entering the
"
" pacemaker mode (minutes of no new paths). 0 = "
"immediately,
"
" -1 = immediately and together with normal mutation).
"
" See docs/README.MOpt.md
"
" -c program - enable CmpLog by specifying a binary compiled for "
"it.
"
" if using QEMU, just use -c 0.
"
" -l cmplog_opts - CmpLog configuration values (e.g. "2AT"):
"
" 1=small files (default), 2=larger files, 3=all "
"files,
"
" A=arithmetic solving, T=transformational solving.
"
"Fuzzing behavior settings:
"
" -Z - sequential queue selection instead of weighted "
"random
"
" -N - do not unlink the fuzzing input file (for devices "
"etc.)
"
" -n - fuzz without instrumentation (non-instrumented mode)
"
" -x dict_file - fuzzer dictionary (see README.md, specify up to 4 "
"times)
"
"Testing settings:
"
" -s seed - use a fixed seed for the RNG
"
" -V seconds - fuzz for a specified time then terminate
"
" -E execs - fuzz for an approx. no. of total executions then "
"terminate
"
" Note: not precise and can have several more "
"executions.
"
"Other stuff:
"
" -M/-S id - distributed mode (see docs/parallel_fuzzing.md)
"
" -M auto-sets -D, -Z (use -d to disable -D) and no "
"trimming
"
" -F path - sync to a foreign fuzzer queue directory (requires "
"-M, can
"
" be specified up to %u times)
"
" -d - skip deterministic fuzzing in -M mode
"
" -T text - text banner to show on the screen
"
" -I command - execute this command/script when a new crash is "
"found
"
//" -B bitmap.txt - mutate a specific test case, use the out/fuzz_bitmap
//" "file
"
" -C - crash exploration mode (the peruvian rabbit thing)
"
" -b cpu_id - bind the fuzzing process to the specified CPU core "
"(0-...)
"
" -e ext - file extension for the fuzz test input file (if "
"needed)
",
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
"
#else
#define DYN_COLOR
#endif
SAYF(
"Environment variables used:
"
"LD_BIND_LAZY: do not set LD_BIND_NOW env var for target
"
"ASAN_OPTIONS: custom settings for ASAN
"
" (must contain abort_on_error=1 and symbolize=0)
"
"MSAN_OPTIONS: custom settings for MSAN
"
" (must contain exitcode="STRINGIFY(MSAN_ERROR)" and symbolize=0)
"
"AFL_AUTORESUME: resume fuzzing if directory specified by -o already exists
"
"AFL_BENCH_JUST_ONE: run the target just once
"
"AFL_BENCH_UNTIL_CRASH: exit soon when the first crashing input has been found
"
"AFL_CMPLOG_ONLY_NEW: do not run cmplog on initial testcases (good for resumes!)
"
"AFL_CRASH_EXITCODE: optional child exit code to be interpreted as crash
"
"AFL_CUSTOM_MUTATOR_LIBRARY: lib with afl_custom_fuzz() to mutate inputs
"
"AFL_CUSTOM_MUTATOR_ONLY: avoid AFL++'s internal mutators
"
"AFL_CYCLE_SCHEDULES: after completing a cycle, switch to a different -p schedule
"
"AFL_DEBUG: extra debugging output for Python mode trimming
"
"AFL_DEBUG_CHILD: do not suppress stdout/stderr from target
"
"AFL_DISABLE_TRIM: disable the trimming of test cases
"
"AFL_DUMB_FORKSRV: use fork server without feedback from target
"
"AFL_EXIT_WHEN_DONE: exit when all inputs are run and no new finds are found
"
"AFL_EXPAND_HAVOC_NOW: immediately enable expand havoc mode (default: after 60 minutes and a cycle without finds)
"
"AFL_FAST_CAL: limit the calibration stage to three cycles for speedup
"
"AFL_FORCE_UI: force showing the status screen (for virtual consoles)
"
"AFL_FORKSRV_INIT_TMOUT: time spent waiting for forkserver during startup (in milliseconds)
"
"AFL_HANG_TMOUT: override timeout value (in milliseconds)
"
"AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: don't warn about core dump handlers
"
"AFL_IGNORE_UNKNOWN_ENVS: don't warn on unknown env vars
"
"AFL_IMPORT_FIRST: sync and import test cases from other fuzzer instances first
"
"AFL_KILL_SIGNAL: Signal ID delivered to child processes on timeout, etc. (default: SIGKILL)
"
"AFL_MAP_SIZE: the shared memory size for that target. must be >= the size
"
" the target was compiled for
"
"AFL_MAX_DET_EXTRAS: if more entries are in the dictionary list than this value
"
" then they are randomly selected instead all of them being
"
" used. Defaults to 200.
"
"AFL_NO_AFFINITY: do not check for an unused cpu core to use for fuzzing
"
"AFL_NO_ARITH: skip arithmetic mutations in deterministic stage
"
"AFL_NO_AUTODICT: do not load an offered auto dictionary compiled into a target
"
"AFL_NO_CPU_RED: avoid red color for showing very high cpu usage
"
"AFL_NO_FORKSRV: run target via execve instead of using the forkserver
"
"AFL_NO_SNAPSHOT: do not use the snapshot feature (if the snapshot lkm is loaded)
"
"AFL_NO_UI: switch status screen off
"
DYN_COLOR
"AFL_PATH: path to AFL support binaries
"
"AFL_PYTHON_MODULE: mutate and trim inputs with the specified Python module
"
"AFL_QUIET: suppress forkserver status messages
"
"AFL_PRELOAD: LD_PRELOAD / DYLD_INSERT_LIBRARIES settings for target
"
"AFL_TARGET_ENV: pass extra environment variables to target
"
"AFL_SHUFFLE_QUEUE: reorder the input queue randomly on startup
"
"AFL_SKIP_BIN_CHECK: skip the check, if the target is an executable
"
"AFL_SKIP_CPUFREQ: do not warn about variable cpu clocking
"
"AFL_SKIP_CRASHES: during initial dry run do not terminate for crashing inputs
"
"AFL_STATSD: enables StatsD metrics collection
"
"AFL_STATSD_HOST: change default statsd host (default 127.0.0.1)
"
"AFL_STATSD_PORT: change default statsd port (default: 8125)
"
"AFL_STATSD_TAGS_FLAVOR: set statsd tags format (default: disable tags)
"
" Supported formats are: 'dogstatsd', 'librato',
"
" 'signalfx' and 'influxdb'
"
"AFL_TESTCACHE_SIZE: use a cache for testcases, improves performance (in MB)
"
"AFL_TMPDIR: directory to use for input file generation (ramdisk recommended)
"
//"AFL_PERSISTENT: not supported anymore -> no effect, just a warning
"
//"AFL_DEFER_FORKSRV: not supported anymore -> no effect, just a warning
"
"
"
);