Zafl (Zipr-based AFL)
Welcome to Zafl, a project to fuzz X86 64-bit binary programs.
Key features of Zafl:
- Uses Zipr, a fast, space-efficient binary rewriter to inline AFL-style instrumentations. Preliminary overhead:
- 20% slower than afl/source code
- 15% faster than afl/dyninst
- a lot faster than afl/QEMU
- Platform for experimenting with other instrumentation to guide afl, e.g., add calling context to the edge-profile function (a la Angora)
Installation
Note that you will need sudo privileges to get and install all the required packages.
Getting packages and compiling Zafl
git clone --recursive git@git.zephyr-software.com:allnp/zafl_umbrella.git
cd zafl_umbrella
. set_env_vars
./get_packages.sh
./build-all.sh
Note:
- Zafl automatically downloads and builds AFL and AFL/QEMU
- Building Zafl takes approximately 15 minutes
Setting up local postgres database
Next we need to setup a local copy of the postgres database
cd $ZAFL_HOME/zipr_umbrella
./postgres_setup.sh
If all goes well with the postgres setup, you should be able to login into the database by typing: psql
The output of psql should look something like this:
psql (9.3.22)
SSL connection (cipher: DHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.
peasoup_XXX=>
Setting up IDA with your license key
The standard Zipr toolchain configuration uses IDA Pro as part of its analysis phases.
Once you get a license from IDA, put your license key file in: $IDAROOT/
Then you must run IDA once in interactive mode and accept the licensing terms:
cd $IDAROOT
./idat64
Testing Zafl
Before running Zafl, always make sure to have your environment variable set
cd $ZAFL_HOME
. set_env_vars
Testing Zipr
Test that the binary rewriting infrastructure by rewriting /bin/ls
cd /tmp
$PSZ /bin/ls ls.zipr
Your terminal's output should look like this:
Using Zipr backend.
Detected ELF file.
Performing step gather_libraries [dependencies=mandatory] ...Done. Successful.
Performing step meds_static [dependencies=mandatory] ...Done. Successful.
Performing step pdb_register [dependencies=mandatory] ...Done. Successful.
Performing step fill_in_cfg [dependencies=mandatory] ...Done. Successful.
Performing step fill_in_indtargs [dependencies=mandatory] ...Done. Successful.
Performing step clone [dependencies=mandatory] ...Done. Successful.
Performing step fix_calls [dependencies=mandatory] ...Done. Successful.
Program not detected in signature database.
Performing step zipr [dependencies=clone,fill_in_indtargs,fill_in_cfg,pdb_register] ...Done. Successful.
Invoke the rewritten version of /bin/ls and make sure it runs normally:
./ls.zipr
Testing Zafl
Make sure afl itself is setup properly
cd /tmp
mkdir in
echo "1" > in/1
afl-fuzz -i in -o out -Q -- /bin/ls @@
You may see afl error messages such as this one that will need to be fixed:
afl-fuzz 2.52b by <lcamtuf@google.com>
[+] You have 24 CPU cores and 1 runnable tasks (utilization: 4%).
[+] Try parallel jobs - see docs/parallel_fuzzing.txt.
[*] Checking CPU core loadout...
[+] Found a free CPU core, binding to #0.
[*] Checking core_pattern...
[-] Hmm, your system is configured to send core dump notifications to an
external utility. This will cause issues: there will be an extended delay
between stumbling upon a crash and having this information relayed to the
fuzzer via the standard waitpid() API.
To avoid having crashes misinterpreted as timeouts, please log in as root
and temporarily modify /proc/sys/kernel/core_pattern, like so:
echo core >/proc/sys/kernel/core_pattern
[-] PROGRAM ABORT : Pipe at the beginning of 'core_pattern'
Location : check_crash_handling(), afl-fuzz.c:7275
or:
[-] Whoops, your system uses on-demand CPU frequency scaling, adjusted
between 1558 and 2338 MHz. Unfortunately, the scaling algorithm in the
kernel is imperfect and can miss the short-lived processes spawned by
afl-fuzz. To keep things moving, run these commands as root:
cd /sys/devices/system/cpu
echo performance | tee cpu*/cpufreq/scaling_governor
You can later go back to the original state by replacing 'performance' with
'ondemand'. If you don't want to change the settings, set AFL_SKIP_CPUFREQ
to make afl-fuzz skip this check - but expect some performance drop.
Fix any afl-related errors until you can run:
afl-fuzz -i in -o out -Q -- /bin/ls @@
Running Zafl smoke tests
cd $ZAFL_HOME/zfuzz/test/gzip
./test_gzip.sh
The test will run afl on gzip instrumented with the proper instrumentation inlined. The output should end with:
execs_since_crash : 77855
exec_timeout : 20
afl_banner : gzip.stars.zafl
afl_version : 2.52b
target_mode : default
command_line : afl-fuzz -i zafl_in -o zafl_out -- ./gzip.stars.zafl -f
TEST PASS: ./gzip.stars.zafl: ran zafl binary: execs_per_sec : 2000.00
TEST PASS: all tests passed: zafl instrumentation operational on gzip
You should also run and make sure these tests pass:
cd $ZAFL_HOME/zfuzz/test/bc
./test_bc.sh
Final sanity check
cd /tmp
zafl.sh /bin/ls ls.zafl
zafl.sh is the primary script for adding afl instrumentation to binaries. You should see:
zafl.sh /bin/ls ls.zafl
Zafl: Transforming input binary /bin/ls into ls.zafl
Zafl: Issuing command: /home/zafl_guest/zafl_umbrella/install/zipr_umbrella/peasoup_examples/tools/ps_zipr.sh /bin/ls ls.zafl -c move_globals=on -c zafl=on -o move_globals:--elftables -o zipr:--traceplacement:on -o zipr:true -o zafl:--stars
Using Zipr backend.
Detected ELF file.
Performing step gather_libraries [dependencies=mandatory] ...Done. Successful.
Performing step meds_static [dependencies=mandatory] ...Done. Successful.
Performing step pdb_register [dependencies=mandatory] ...Done. Successful.
Performing step fill_in_cfg [dependencies=mandatory] ...Done. Successful.
Performing step fill_in_indtargs [dependencies=mandatory] ...Done. Successful.
Performing step clone [dependencies=mandatory] ...Done. Successful.
Performing step fix_calls [dependencies=mandatory] ...Done. Successful.
Program not detected in signature database.
Performing step move_globals [dependencies=none] ...Done. Successful.
Performing step zafl [dependencies=none] ...Done. Successful.
Performing step zipr [dependencies=clone,fill_in_indtargs,fill_in_cfg,pdb_register] ...Done. Successful.
You can run ls.zafl as you would ls: ./ls.zafl
Zafl'd binaries can be run normally. There is no extra output.
To make sure the binary has been instrumented properly: ZAFL_DEBUG=1 ./ls.zafl
The output should start with:
Error getting shm environment variable - fake allocate AFL trace map
Error getting shm environment variable - fake allocate AFL trace map
zafl_initAflForkServer(): Bad file descriptor
Let's now run the Zafl'd binary with afl:
afl-fuzz -i in -o out -- ./ls.zafl @@
You can also run the usual afl utities, e.g:
afl-showmap -o map.out -- ./ls.zafl
afl-cmin -i out/queue/ -o out.cmin -- ./ls.zafl @@
Et voila!
Options to zafl.sh
To specify the entry point for the fork server (by default, zafl.sh will look for main()):
zafl.sh <target_binary> <zafl_output_binary> -o zafl:--entrypoint {<functionName> | 0xaddress}
To specify exit points:
zafl.sh <target_binary> <zafl_output_binary> -o zafl:--exitpoint {<functionName> | 0xaddress}
To specify multiple exit points:
zafl.sh <target_binary> <zafl_output_binary> -o zafl:--exitpoint {<functionName> | 0xaddress} ... -o zafl:--exitpoint {<functionName> | 0xaddress}
To specify a whitelist of functions to instrument:
zafl.sh <target_binary> <zafl_output_binary> -o zafl:--whitelist <whitelistFile>
To specify a blacklist of functions to skip:
zafl.sh <target_binary> <zafl_output_binary> -o zafl:--blacklist <blacklistFile>
The white and black list files contain one entry per line.
TL;DR
Once everything is installed properly:
zafl.sh <target_binary> <zafl_output_binary> [options]