Assignment: FUZZ
Changelog:
- 22 March 2017: Initial version.
- 4 April 2017: Revised to add option of using AddressSanitizer only for diagnosis, don’t ask people to
really diagnose debug
indent
. - 13 April 2017: Some formatting cleanup.
In this assignment, you will use a “whitebox fuzzing” tool to find memory errors in software in combination with a generic memory error detector.
Assignment resources
-
Documentation for the two tools we will be using:
- AddressSanitizer: a memory error detector
integrated with recent versions of the C compilers
GCC
andClang
. - american fuzzy lop: a whitebox fuzzing tool written by Michał Zalewski.
- AddressSanitizer: a memory error detector
integrated with recent versions of the C compilers
-
Neystadt, “Automated Penetration Testing with White-Box Fuzzing”.
Assignment details
-
We will be testing a slighly modified version of FreeBSD’s indent program. Download the source code for it here.
This is this code from FreeBSD’s git repository with these changes applied to it. The changes let the program compile on Linux and makes it ignore non-ASCII input.
-
Download american fuzzy lop from here or a local copy.
Build american fuzzy lop
-
Extract the tar file you downloaded with a command like:
tar -zxvf afl-latest.tgz
(where
afl-latest.tgz
is the file you downloaded). This will create a directory named something likeafl-2.39b
. In this directory run:make
Build indent
with AFL and AddressSanitizer support
-
Unpack
fbsd-indent.tar.gz
using a command like:tar -zxvf fbsd-indent.tar.gz
Then in the
fbsd-indent
directory run:AFL_USE_ASAN=1 /path/to/afl-2.39b/afl-gcc -m32 -fno-omit-frame-pointer -fsanitize=address -g -O *.c -o indent
where
/path/to/afl-2.39b/afl-gcc
is theafl-gcc
program in the directory you ranmake
in when building american fuzzy lop. You can find the path to the directory using thepwd
command while inside it.-
The option
-m32
builds a 32-bit binary instead of a 64-bit one. This is necessary because AFL has a hard time limiting the memory usage of a 64-bit program that uses AddressSanitizer. See also the documentation indocs/notes_for_asan.txt
in the AFL directory. -
The options
AFL_USE_ASAN=1
and-fsanitize=address
make this program use AddressSanitizer, which is a memory error detector.-fsanitize=address
is the option that GCC uses to enable sanitization. We also tell AFL we are building with ASAN support so it can adjust how it instruments the application. -
The
-fno-omit-frame-pointer
option makes GCC use a frame pointer in the generated code. This will allow AddressSanitizer to generate good stack traces. -
Building with
afl-gcc
instead ofgcc
adds tracking of what path is taken through the program that AFL can use later on to generate test cases.
Alternately, you may also build
indent
without AddressSanitizer support to perform the fuzzing, then rebuild it with AddressSanitizer to diagnose the memory errors found by the fuzzing. See the instructions under the Hints below. This will allow you to build without-m32
, if that is a problem on your system. This option makes fuzzing faster, but identifies memory errors less consistently. -
-
Verify that
indent
works by running it on a simple C file. For example, you can try running it on theio.c
that comes withindent
using:indent <io.c >io.c.indented
then compare the two files (
io.c
andio.c.indented
) in a text editor.Note that if
indent
is given a filename as an argument it modifies that file.
Setup to run AFL
-
By default Ubuntu runs a special program to handle applications that crash. We want to disable this feature to run american fuzzy lop, because it wants to monitor how the program it tests crashes accurately. To do this run
sudo bash -c 'echo core >/proc/sys/kernel/core_pattern'
Note that you will need to rerun this command if you reboot your VM.
Run the AFL fuzzer on indent
-
american fuzzy lop will take an initial test case and make random changes to it. It will combine different random changes based on which changes seem to cause the program to execute more different paths.
-
You need to first supply one or more initial test cases. Create a directory called
testcases
. Inside it create one or more small C files to use to testindent
. american fuzzy lop will be able to find more useful test cases if your C files include things that a C indenting program will need to handle. Otherwise, the fuzzer will need to “discover” whatindent
will recognize by testing randomly. -
Now that you have the initial input. Run
afl-fuzz
onindent
with a command like/path/to/afl-2.39b/afl-fuzz -m 700 -i testcases -o findings ./indent
The options here:
- The
-m 700
says to make up to 700 MB of memory available toindent
. It won’t actually use that much memory, but AddressSanitizer will reserve a lot of memory. - The
-o findings
option specifies the directory to write output to and to use to store temporary files. - The
-i testcases
option specifies where to find the initial test cases.
This memory AddressSanitizer reserves is use store a table with one entry for every 8 bytes of memory indicating whether the memory is valid to access. AddressSanitizer reserves space for all possible addresses in this table, but leaves parts of the table which correspond to invalid addresses as invalid rather actually requiring that space to be allocated by the operating system.
- The
-
As the fuzzer runs, you will see a status screen which is described here.
-
Wait until the fuzzer reports finding at least two unique crashes (and preferably at least three or four). In my testing, this took substantially less than 15 minutes. If it does not, then you should consider whether your initial cases are too simple (don’t include enough C syntax that
indent
will need to process) or too long (take up many kilobytes, making each test run slower and giving the tool more “boring” variations to try). If you still have difficulty after adjusting your test cases, please contact the instructor. -
After the fuzzer generates some crashing test cases, stop it with control-C. If we were doing this for real, we’d keep running this until AFL completed a complete “cycle”, but we will not make you wait that long.
-
Make a copy of the status screen (screenshot or text copy) and save it to a file for later.
Examine a Crashing Test Case
-
Look in the
findings
directory that was generated by running AFL. Among this folder there are directories calledcrashes
andhangs
. These contain the test cases that match the unique crashes and/or hangs reported in the status screen. -
View one of the crashing test cases. Note that the test case may contain some non-ASCII characters, so you may wish to check using a hex editor or
od -c file
rather than just viewing the test case in a text editor. -
Make a copy of the crashing test case and trying running the
indent
program on it was./indent <testcase.dat
. You should notice a crash. Most likely this crash will be from a memory error. Since we built our program with AddressSanitizer, rather than being a normal segfault, the memory error will have been caught by extra code added by AddressSanitizer. See below under “Hints” for how to interpret this output. -
Rather than working with this complex test case, we will run an utility
afl-tmin
that comes with american fuzzy lop to minimize it. It will try to “fuzz” the given test case slightly without changing what path it takes through the program in order to make it shorter. Run this utility with/path/to/afl-2.39b/afl-tmin -m 700 -i input-file -o output-file ./indent
to produce a minimized version of the testcase in
input-file
inoutput-file
. -
Verify that the minimized test case produces a similar crash.
-
Examine the program around where AddressSanitizer places the crash.
Questions
Answer the following questions in file called answers.txt
:
-
Where did the crash occur in our first test case? If the cause was accessing an array out of bounds, what variable did the program neglect to keep in bounds? If the cause was something else, explain briefly.
-
Examine some other crashing test cases and try running them with the program. Do they represent different bugs? Explain briefly how you determined this.
Submission
-
Submit the following on Collab:
-
The file
answers.txt
. -
A screenshot or text copy of the AFL fuzzer status message in a file called status.txt or status.png
-
An unminimized crashing test case in a file called
testcase.dat
. -
A minimized version of that test case in a file called
testcase.dat.min
. -
One of the other crashing test cases you used to answer question 2 in a file called
testcase2.dat
. -
Your initial test cases that you used to generate
testcase.dat
.
-
Hints and Debugging and Alternatives
Possible error messages
-
If you get an error like
args.c:42:23: fatal error: sys/cdefs.h: No such file or directory
then run
sudo apt install libc6-dev-i386
Understanding AddressSanitizer output
-
AddressSanitizer output includes the following information:
-
A stack trace indicating where the memory error occured. This is the location of the bad read or write, which may not be where the bug needs to be fixed in the program.
-
If the memory error was because of trying to access the heap, the location of the code that most recently freed or malloc’d something near that the location that was accessed.
-
If the memory error was because of trying to access the stack or global data, the variables that are closest in memory to the location that was accessed.
-
“Shadow bytes”, which show AddressSanitizer’s internal data structure for keeping track of the state of memory around the accessed address. This has one byte of data for every eight bytes of program memory, indicating whether that part of program memory is valid or invalid and, if it is invalid, why it is invalid. In this data structure “red zones” are memory regions allocated around objects to catch accesses just outside of an object.
-
Fuzzing Without AddressSanitizer
-
It’s also possible to disable AddressSanitizer when fuzzing, but use it only to diagnose the crashes the fuzzer finds. This will make fuzzing faster at the cost finding a few less crashes.
In this case, compile without
-m32
or-fsanitize=address
orAFL_USE_ASAN=1
. Then, after getting fuzzing results, buildindent
again with AddressSanitizer by compiling withgcc
instead of afl-gccand adding back the
-fsanitize=addressoption. Then, take the crashing test cases the fuzzer finds --- or other test cases in the
queue` subdirectory, which might also crash but only under AddressSanitizer.