top of page
Writer's pictureotw

Exploit Development: Fuzzing with American Fuzzy Lop++ (AFL++) to Find Zero-Day Vulnerabilities

Welcome back, my aspiring cyberwarriors!


Finding vulnerabilities in applications and services is the first step toward developing your own zero-day exploit. Vulnerability scanners are great for finding KNOWN vulnerabilities, but to find unknown or zero-day vulnerabilities a fuzzer is an excellent tool that you should be familiar with. As you know, fuzzing is the process of sending random or pseudo-random input into an application to find out if they break the application. If the application breaks, you can examine and analyze why it broke and then determine whether this vulnerability is exploitable. If nothing else, if you find an input that breaks an application, it can lead to a denial of service (dos) attack.


Secure coding is a very difficult practice. Usually, when programmers develop software, their goal is to make the software work rather than break. In this process, vulnerabilities may develop in cases where a legacy function was used instead of a newer, more secure one. Unfortunately, this is often the case to speed up development. Consequently, legacy software is especially vulnerable. This means that if the programmer is well aware of secure coding practices, their software will likely be secure. On the other hand, for the less sophisticated programmer, legacy functions often make up a significant portion of their code.


Examining the entire source code of a program to identify vulnerabilities such as buffer overflows can be an effective but time-consuming process. This approach, while thorough, is not always the most practical way to identify critical yet straightforward vulnerabilities. Addressing such issues quickly is essential, and one of the most efficient methods for uncovering them is a technique known as Fuzzing.

In this article I want to share a step-by-step guide on how to run American Fuzzy Lop ++ (AFL++) to fuzz an open source target.


Why AFL++?


AFL++ (American Fuzzy Lop Plus Plus) builds upon the foundation of AFL, a highly regarded fuzzing tool for C, C++, and Objective-C programs. AFL++ enhances AFL’s renowned speed and intelligent test case selection with additional features and optimizations that make it even more effective at uncovering vulnerabilities. Like its predecessor, AFL++ remains user-friendly, making it accessible for beginners while offering advanced capabilities for experienced users.


The original American Fuzzy Lop was developed by Michal Zalewski (of poF fame and several No Starch Press books) and first released in November 2013. It was immediately recognized as probably the best fuzzer available and became the de-facto standard for security research. AFL++ is a fork of the AFL source code. It offers better performance and more advanced features and that is why we will use it here.


What Does AFL++ Do?


AFL++ is a coverage-guided fuzzer that methodically explores program execution paths to identify vulnerabilities. It retains the core principles of AFL while introducing several key improvements:


  • Code Instrumentation: AFL++ enhances the original AFL's instrumentation, supporting various modes (e.g., classic, persistent, and QEMU modes) for broader compatibility and better performance.

  • Coverage Tracking: It monitors execution paths for each input and prioritizes inputs that lead to new or unique coverage.

  • Enhanced Mutation Strategies: AFL++ incorporates advanced mutation strategies, including deterministic and stochastic techniques, along with cutting-edge mutators for better test case generation.

  • Genetic Algorithm: Like AFL, AFL++ employs a genetic algorithm to mutate inputs that trigger new paths, evolving them to maximize coverage.

  • Input Filtering: Redundant or low-value inputs are efficiently filtered out, optimizing the fuzzing process.

  • Power Schedules: AFL++ offers advanced power schedules to balance input prioritization and mutation efforts effectively.

  • Dictionaries: It supports custom dictionaries for testing "magic values" (e.g., specific strings or patterns), boosting its ability to find specific types of vulnerabilities.

  • Extensibility: AFL++ is designed with extensibility in mind, enabling users to easily integrate custom components or experiment with novel fuzzing techniques.


Download And Build Your Target


For this article, we’ll focus on fuzzing the Xpdf PDF viewer to identify a crash or proof of concept (PoC) for CVE-2019-13288. The target version is Xpdf 3.02, which contains the relevant vulnerability. Thanks to Antonio Morales for publishing the original materials on GitHub.


To begin fuzzing, we need to organize our workspace and prepare the target program:


kali> mkdir fuzzing_xpdf && cd fuzzing_xpdf/


Download Xpdf 3.02:



kali> tar -xvzf xpdf-3.02.tar.gz


Next, build Xpdf:


kali> cd xpdf-3.02

kali> sudo apt update && sudo apt install build-essential gcc -y

kali> ./configure –prefix="$HOME/fuzzing_xpdf/install/"

kali> make

kali> make install


Download sample PDFs: Obtain a collection of PDF files to use as input for testing our build. You can find freely available examples online or use the following command to download:


kali> mkdir pdf_examples && cd pdf_examples


Now, we can test the pdfinfo binary with:


kali> /home/kali/fuzzing_xpdf/install/bin/pdfinfo -box -meta /home/kali/fuzzing_xpdf/pdf_examples/helloworld.pdf


  • pdfinfo: A binary file to extract information from PDF files

  • -box: Displays information about page dimensions and media boxes

  • -meta: Reveals embedded metadata about the document


You should see something like this:



This indicates that we have successfully completed all the steps, and our installation is functioning correctly. We can now proceed with the installation of AFL++.


Install AFL++


kali> cd AFLplusplus

kali> make distrib

kali> sudo make install


If everything went well, you should now be able to run AFL++.


Simply type:


kali> afl-fuzz


Getting Started With Fuzzing


First of all, we're going to clean all previously compiled object files and executables:


kali> rm -r fuzzing_xpdf/install


kali> cd fuzzing_xpdf/xpdf-3.02


kali> make clean


And now we're going to build xpdf using the afl-clang-fast compiler:


kali> export LLVM_CONFIG="llvm-config-16"


kali> CC=/home/kali/AFLplusplus/afl-clang-fast CXX=/home/kali/AFLplusplus/afl-clang-fast++ ./configure –prefix="/home/kali/fuzzing_xpdf/install/"


kali> make


kali> make install


Now, you can run the fuzzer with the following command:


kali> afl-fuzz -i /home/kali/fuzzing_xpdf/pdf_examples/ -o /home/kali/fuzzing_xpdf/out/ -s 123 -- /home/kali/fuzzing_xpdf/install/bin/pdftotext @@ /home/kali/fuzzing_xpdf/output


Explanation of each option:


  • -i: Specifies the directory for input cases (example files).

  • -o: Specifies the directory where AFL++ will store mutated files.

  • -s: Sets a static random seed for reproducibility.

  • @@: A placeholder in the target’s command line that AFL replaces with each input file name.


Essentially, the fuzzer will execute:/home/kali/fuzzing_xpdf/install/bin/pdftotext <input-file-name> /home/kali/fuzzing_xpdf/outputfor each input file.


After a few seconds, you should see an output similar to this:


Depending on the power of your virtual machine, you will see the first hangs and crashes over time. You'll see the 'uniq. crashes' value in red, indicating the number of unique crashes found. These crash files are stored in the /home/kali/fuzzing_xpdf/out/ directory. You can stop the fuzzer after finding the first crash, which we'll focus on. This may take one to two hours, depending on your machine's performance.


How to Reproduce the Crash


To reproduce the crash, locate the file corresponding to the crash in the /home/kali/fuzzing_xpdf/out/default/crashes directory.


In my case, the filename is:id:000000,sig:11,src:000972,time:4417804,execs:2703585,op:havoc,rep:2.


Use GDB to determine why the program crashes with this input.


First, rebuild Xpdf with debugging information enabled to obtain a symbolic stack trace.


kali> rm -r /home/kali/fuzzing_xpdf/install


kali> cd /home/kali/fuzzing_xpdf/xpdf-3.02/


kali> make clean


kali> CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure –prefix="$HOME/fuzzing_xpdf/install/"


  • -g: Includes debug information (symbols and source-level mappings) in the binaries.

  • -O0: Disables compiler optimizations, making the generated code more predictable and easier to debug.


kali> make


kali> make install


Now, you can run GDB:


kali> gdb --args /home/kali/fuzzing_xpdf/install/bin/pdftotext /home/kali/fuzzing_xpdf/out/default/crashes/id:000000,sig:11,src:000972,time:4417804,execs:2703585,op:havoc,rep:2 /home/kali/fuzzing_xpdf/output


And then, type inside GDB:


(gdb) run


It looks like we are overwriting the return address with a getchar command, causing a segmentation fault. This results in a crash. At a minimum, this behavior can lead to a denial-of-service condition if the circumstances are repeated in a loop.


Summary


Fuzzing is often a technique for identifying unknown vulnerabilities (zero-day) in software. It is often the first step to develop your zero-day exploit.


AFL++ is an incredibly powerful tool that remains remarkably easy to use. It streamlines the fuzzing process into just a few simple steps, managing all the complex tasks in the background.


To learn about Exploit Development, become a Subscriber Pro and join our upcoming Exploit Development training.

bottom of page