This blog post is about Docker images for a very specific fuzzer and its sister projects, the American Fuzzy Lop (AFL). If you need a short introduction to the idea of fuzzing or you want to find general information for AFL, check out our last blog post "Fuzzing Java with JQF" (that will then go on and explain how to fuzz Java code) which has pointers to other ressources on the web.
The various sub-topics of fuzzing with AFL and AFL-derived projects can be arbitrarily complex in practice. The automation of being able to detect behavior and especially highlight insecure behavior is called instrumentation. Fuzzing for memory corruptions is so successful because it has become more easy to do the instrumentation and highlight the insecure behavior (such as SEGFAULTs or ASAN aborts). One of the steps that improved the state of the art is AFL. At the beginning it was designed to be very simple and to have only a couple of command line options. And those command line options worked and still work for many use cases. However, as said before the sub-topics of fuzzing can be arbitrarily complex. A lot of fundamental question came up over time on the afl-users mailing list and there was often more than one answer and usually the answers relied on more assumptions. Here are some very common examples:
Q: "AFL only supports target programs that read the fuzzed input from stdin or file. I have a target that listens on a network port!". A: "Use preeny, a LD_PRELOAD library that tries to redirect socket IO to stdin/stdout". A2: "I've created the AFL-fork named <INSERT PROJECT NAME HERE> that is able to fuzz network IO"
Q: "I don't have the source code of the target at all, so I can't compile with afl-clang!". A: "AFL now supports emulating instrumentation with QEMU, just use the -Q command line switch". A2: "Use the afl-dyninst tool."
Q: "I don't find bugs and my fuzzer is slow". A: "You also don't find new paths in your binary, so your setup is incorrect". A2: "Choose a different target configuration". A3: "Use libdislocator/ASAN/persistent mode"
Q: "My target is not written in C or C++/is not compiled with gcc/clang". A: "Use AFL's sister project <INSERT PROJECT NAME HERE>"
Overall, starting with AFL with a specific target is not the easiest task if you don't know the most important gotchas. Not only do you need to know how software is built, how Makefiles and configure scripts work, but more often you will be the one person who does a more complicated compilation of the target than the average user. To name an example, if you want to compile software with ASAN and add a CFLAG="-fsanitize=address" this might work for some software, other software (especially based on libtool) will require you to specify LDFLAGS='-fsanitize=address' as well. And you are lucky if the compilation simply accepts these flags. Afterwards you need to know at least the red flags of fuzzing with AFL, such as the total paths not increasing (your instrumentation is not working), slow speeds (did you run strace on afl-showmap?) and how to debug them. So you need to be able to do performance debugging as well. You will also need to know why no crashes are found, for example because you simply don't use more elaborate instrumentation with libdislocator or ASAN.
Even for people using AFL on a regular basis, this can make it time consuming to setup a fuzzing run for a new target. That's why we thought it would be nice to have the most important AFL setups and AFL sister project setups as Docker containers. Here we go:
All containers based on Ubuntu 19.10, simply because compared to the usual usage of Docker, you will want to run manual commands in Docker later and compile more software. That's why we opted for a full OS.
afl-jqf (based on afl-base): Installs JQF including JQF-AFL and JQF-Zest. Used to fuzz projects written in Java.
afl-binary-only (based on afl-base): Installs QEMU mode, unicorn mode and other things in the binary-only compile of AFL.
afl-blackbox (based on afl-binary-only): Additionally installed afl-dyninst.
afl-demo (based on afl-blackbox): Installs an old version of readelf from an old binutils, afterwards a bash script will do a (very simple but interactive) demo so you can see AFL and the sister projects in action.
You can find the Dockerfile and everything you need to build the images on github. But even better, you can just pull the images from the Pentagrid Dockerhub, see the README on github. If you are not that much into Docker and don't have the time, you can see what the interactive demo does in a video on Vimeo.