VolatileMinds

Application Security and Software Consulting



More advanced usage of AFL with real world examples -- Fuzzing libraries

So far, we have covered basic AFL usage and some slightly advanced AFL usage fuzzing the tcpdump binary. Today, we will cover fuzzing libfreetype, a font library used on many operating systems. In order to fuzz libraries with no easy-to-use and minimal binaries, we must build our own test harness for fuzzing.

Without going over building the chroot for fuzzing, let’s start right off the bat grabbing the software we will be fuzzing. We are going to fuzz the latest (as of this writing) version of libfreetype 2.6.

wget http://download.savannah.gnu.org/releases/freetype/freetype-2.6.tar.gz
tar xzf freetype-2.6.tar.gz

Once we have the library source code downloaded, we need to build the library with AFL. In the last post discussing AFL, we covered building software using the afl-clang-fast wrapper. We will use this same wrapper to instrument libfreetype.

cd freetype-2.6
CC=afl-clang-fast ./configure
make

Once built and instrumented, the library itself isn’t very useful for us. We can’t pass anything we have just built to afl-fuzz, so we need to create a harness that we can pass a font to and parse with the library, with as minimal coding required.

When building test harnesses for use for fuzzing, it’s important to minimize the amount of code required to hit the instrumented paths in the library. With ten lines of code, we can initialize libfreetype to parse a small testcase, and pass a font to be parsed.

#include <ft2build.h>
#include FT_FREETYPE_H

int main(int argc, char *argv[]){
    FT_Library lib;
    FT_Face face;

    FT_Init_FreeType(&lib);
    FT_New_Face(lib, argv[1], 0, &face);
}

Basically, after initializing the library with FT_Init_FreeType, I pass the first argument to my harness (the path to the font) as the second argument to FT_New_Face. The FT_New_Face function will parse the font file passed in.

Now we need to build the binary, but we must statically link the instrumented libfreetype library with the binary during compilation so that AFL can receive feedback from the instrumnted library during fuzzing.

# afl-clang-fast  font_parser.c -I freetype-2.6/include/ \
> -L freetype-2.6/objs/.libs/ -lfreetype -lz -static -o font_parser
afl-clang-fast 1.83b by <lszekeres@google.com>
afl-llvm-pass 1.83b by <lszekeres@google.com>
[+] Instrumented 1 locations (non-hardened mode, ratio 100%).
#

If you notice, the size of the font_parser binary is quite large, despite the minimal amount of code we used to create the harness.

# du -sh font_parser
3.7M    font_parser
#

This size increase is because statically compiling the font_parser binary with libfreetype means libfreetype is contained in the whole binary, making it self-contained.

We have our binary that we will use to fuzz libfreetype with, but we need a testcase. The best testcase is the smallest testcase you can use, and luckily I found a very small TTF file on the magical Internet.

AAEAAAAKAIAAAwAgT1MvMgAAAAAAAAEoAAAAVmNtYXAAAAAAAAABiAAAACxnbHlmAAAAAAAAAbwA
AAAkaGVhZAAAAAAAAACsAAAAOGhoZWEAAAAAAAAA5AAAACRobXR4AAAAAAAAAYAAAAAGbG9jYQAA
AAAAAAG0AAAABm1heHAAAAAAAAABCAAAACBuYW1lAAAAAAAAAeAAAAAgcG9zdAAAAAAAAAIAAAAA
EAABAAAAAQAAAkgTY18PPPUACwAgAAAAALSRooAAAAAAyld0xgAAAAAAAQABAAAAAAAAAAAAAAAA
AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAAACAAIAAQAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAACMAIwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAMA
AQAAAAwABAAgAAAABAAEAAEAAABB//8AAABB////wAABAAAAAAAAAAgAEgAAAAEAAAAAAAAAAAAA
AAAxAAABAAAAAAABAAEAAQAAMTcBAQAAAAAAAgAeAAMAAQQJAAEAAAAAAAMAAQQJAAIAAgAAAAAA
AQAAAAAAAAAAAAAAAAAA

The above block of text is a small base64-encoded TTF file with a single font glyph and all of the metadata for the font stripped out.

# echo "AAEAAAAKAIAAAwAgT1MvMgAAAAAAAAEoAAAAVmNtYXAAAAAAAAABiAAAACxnbHlmAAAAAAAAAbwA
> AAAkaGVhZAAAAAAAAACsAAAAOGhoZWEAAAAAAAAA5AAAACRobXR4AAAAAAAAAYAAAAAGbG9jYQAA
> AAAAAAG0AAAABm1heHAAAAAAAAABCAAAACBuYW1lAAAAAAAAAeAAAAAgcG9zdAAAAAAAAAIAAAAA
> EAABAAAAAQAAAkgTY18PPPUACwAgAAAAALSRooAAAAAAyld0xgAAAAAAAQABAAAAAAAAAAAAAAAA
> AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAAACAAIAAQAAAAAAAAAAAAAA
> AAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
> AAAAAAAAAAAAAAAAAAAAAAAAACMAIwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAMA
> AQAAAAwABAAgAAAABAAEAAEAAABB//8AAABB////wAABAAAAAAAAAAgAEgAAAAEAAAAAAAAAAAAA
> AAAxAAABAAAAAAABAAEAAQAAMTcBAQAAAAAAAgAeAAMAAQQJAAEAAAAAAAMAAQQJAAIAAgAAAAAA
> AQAAAAAAAAAAAAAAAAAA" | base64 --decode > small.ttf
# du -sh small.ttf
4.0K    small.ttf
# file small.ttf
small.ttf: TrueType font data
#

In the past, we have simply created a directory called testcases and used this directory to hold the files we want to pass to the binary we are fuzzing. In order to get a bit more speed out of fuzzing, this time we will mount a small RAM disk into the testcases directory so that reading the testcases both saves our hard drives from spinning/wasting reads and so that access is super fast.

# mkdir testcases syncdir
# mount -t ramfs -o size=512m ramfs testcases/
# cp small.ttf testcases

All we need to do now is start afl-fuzz as we did in the previous writeups, utilizing the master/slave functionality to spin up multiple instances which perform both deterministic and random fuzzing with lightning quickness.

Next post will cover fuzzing the same library, libfreetype, but using the new persistent mode to eek out even more speed. Until next time!