AFL Basics - 1

3 minute read


What is fuzzing?

Simply put, fuzzing is a technique for testing programs with randomly generated input. This randomization helps us find bugs by providing potentially invalid and corner-case inputs for the target program.

In ths post, we will be using American Fuzzy Lop (AFL) to find a simple buffer overflow bug.

Installing AFL

Luckily, installing AFL is pretty simple. It is currently maintained and available on Google’s Github page.

The repository has a great Readme file for getting started. There is even a QuickStartGuide.txt file if the Readme is too long. If even that is too long, you can build AFL with make.

After compiling with make, we can see afl-fuzz and afl-gcc in the repository (among other binaries).


Fuzzing Hello World

Just so that we start to get comfortable with AFL, let’s try our hand at fuzzing a simple Hello World program.

Consider the following program, hello.c:

We can easily induce a buffer overflow, but how do we find this with AFL?

1) Instrumentation

For this example, we will be adding instrumentation by compiling the source code with afl-gcc. AFL can add instrumentation to compiled binaries (no source code available), but we will explore that in a later post.

Because afl-gcc is a wrapper for gcc, we can use it as we would gcc. For example, compiling and adding instrumentation to hello.c, we would use the command ./afl-gcc -o hello hello.c.

If you are using a Makefile, afl-gcc must be defined directly as below.

2) Run afl-fuzz

We can use afl-fuzz with

afl-fuzz -i /path/to/input/dir -o /path/to/output/dir /path/to/binary/hello

The input directory must contain atleast one starting file that contains an example input to our target binary. Luckily, we can use a sample included in the AFL repository (located in testcases/others/text). The output directory is just a location to which AFL will write results. For this example, we will assume that there is a directory called output/ in our current working directory.

Assuming we are currently in the same directory as hello, we would run the command

afl-fuzz -i /path/to/afl/testcases/others/text -o output/ ./hello

After running this command, we will see the AFL UI. From here we can observe (or go do something else). Luckily, our program is very simple, and a crash will be found immediately.


When you see that a crash has been found (uniq crashes), you can quit AFL using CTRL-C. The input that resulted in the crash will be saved in output/crashes/, which can be used to recreate the crash outside of AFL. In my case, the output/crashes/ has a file named id:000000,sig:06,src:000000,op:havoc,rep:16. This name can tell us things about the fuzzing and the crash (for example, the crash was sig:06).

If we want more information about the crash, we can use an experimental crash triage script named (located in /path/to/afl/experimental/crash_triage/). This script will print out the crash data for each file in output/crashes/. We can run this script using

/path/to/afl/experimental/crash_triage/ output/ ./hello

As can be seen, the script needs the output directory and the target binary. After running the script, we’ll be met with the crash data.


The crash data tells us that our bug is at hello.c:6 in function main() (which is our horrible fgets() line). This, in combination with SIGNAL 06 that is also shown, tells us that we have a buffer overflow bug and where we can find it!


Using our simple example, we can see that fuzzing is a powerful tool for finding bugs. In later posts, I’ll discuss more AFL options, other fuzzing tools, and symbolic/concolic execution.