Tutorial - Beginner's Guide to Fuzzing

Part 2: Find more Bugs with Address Sanitizer

Part 1: zzuf    Part 2: Address Sanitizer    Part 3: american fuzzy lop

The most common bugs that can be found with fuzzing are memory access bugs. In a nutshell this means an application is reading or writing memory that it shouldn't. However not all memory access errors result in crashes.

Take this simple C code which we will save as test.c as an example:

int main() {
    int a[2] = {1, 0};
    int b=a[2];
}

This is a classic off-by-one-error. An array a with two elements is defined. The elements of an array are accessed with indices starting with 0, so this array consists of the elements a[0] and a[1]. The code then sets b to the value a[2]. However a[2] is an invalid value, it is not part of the array. The variable b will end up containing an arbitrary value. It just reads "some" memory from the stack and it is undefined what that memory contains. However unlike other memory access bugs this does not crash. Even the mighty valgrind won't be able to tell us that something is wrong.

Recent versions of the compilers llvm and gcc got a powerful tool to spot such memory access bugs. It is called Address Sanitizer (ASan) and it can be enabled at compile time. To use Address Sanitizer we need to add the parameter -fsanitize=address to our compiler flags. To make debugging easier we will also add -ggdb.

gcc -fsanitize=address -ggdb -o test test.c

When we now run our little example program we get a colorful error message:

==7402==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff2971ab88 at pc 0x400904 bp 0x7fff2971ab40 sp 0x7fff2971ab30
READ of size 4 at 0x7fff2971ab88 thread T0
    #0 0x400903 in main /tmp/test.c:3
    #1 0x7fd7e2601f9f in __libc_start_main (/lib64/libc.so.6+0x1ff9f)
    #2 0x400778 (/tmp/a.out+0x400778)

Address 0x7fff2971ab88 is located in stack of thread T0 at offset 40 in frame
    #0 0x400855 in main /tmp/test.c:1

  This frame has 1 object(s):
    [32, 40) 'a' <== Memory access at offset 40 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/test.c:3 main

I cut the less interesting parts of the output, this already tells you pretty well what is going on. In line 3 of our test code we have a stack overflow caused by an invalid read of size 4 (size of an integer).

It should be noted here that older versions of Address Sanitizer (gcc before 4.9) produced a less readable output.

Compiling Software with Address Sanitizer

The software we want to fuzz usually doesn't come as a simple .c file, so we have to add the address sanitizer to the compiler flags. For software using normal configure scripts this can be done like this:

./configure --disable-shared CFLAGS="-fsanitize=address -ggdb" CXXFLAGS="-fsanitize=address -ggdb" LDFLAGS="-fsanitize=address"
make

Again we add -ggdb for more debugging information, if possible we disable shared libraries and we set the flags for both the C and the C++ compiler.

We now can run our software against malformed inputs like in part 1. When redirecting the output to a log file we have to consider that now we can't grep for Segmentation Fault any more. Instead we need to grep for Address Sanitizer's messages:

grep AddressSanitizer fuzzing.log

There are some things to consider when using Address Sanitizer. Even if ASan finds a memory access violation it doesn't automatically crash the application. This is a problem when using automated fuzzing tools, because they usually try to detect segfaults by checking the return code. We can however force ASan to crash software when an error happens with the environment variable ASAN_OPTIONS like this before fuzzing:

export ASAN_OPTIONS='abort_on_error=1'

Another problem is that ASan requires an insane amount of virtual memory (around 20 terabytes). This is only virtual memory, so you can still run the application. But fuzzing tools like american fuzzy lop limit the amount of memory for a fuzzed software. You can sometimes workaround this by disabling memory limits completely (-m none for american fuzzy lop). However this can be risky: A fuzzed sample may cause an application to allocate vast amounts of memory which in return may cause your system to get unstable and other applications to crash. So you shouldn't do anything important while running a fuzzer without a memory limit on the same system.

zzuf right now does not work with ASan, upstream is aware of it and considering workarounds. You can still manually create fuzzing samples with zzuf and feed them into an ASan-compiled software.

Please also be aware that ASan will slow down execution significantly. The bugs found by ASan are usually less severe than the ones found without it (however as always there are exceptions). But it will find much more bugs.

Part 1: zzuf    Part 2: Address Sanitizer    Part 3: american fuzzy lop

CC0
The Fuzzing Project is run by Hanno Böck