This blog post explores my first experience with AFL (american fuzzy lop). I targeted Xpdf, a PDF viewer for Linux distributions. This post is mostly designed to document my trial and error approach to optimizing the fuzzing process of Xpdf by using various AFL tools.

setup

operating system

All of this was performed on a standard Ubuntu 14.04 build. No updates were installed. This was because I wanted a more vulnerable setup, so that I could have a higher likelihood of success with AFL.

afl

Prepare AFL.

cd ~/bin
wget "http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz"
tar -xf afl-latest.tgz
rm afl-latest.tgz
mv afl* afl

Install dependencies:

sudo apt-get install g++

xpdf

Prepare the Xpdf source code.

cd ~/Downloads
wget "ftp://ftp.foolabs.com/pub/xpdf/xpdf-3.04.tar.gz"
tar -xf xpdf-3.04.tar.gz
rm xpdf-3.04.tar.gz
cd xpdf-3.04

Install the dependencies.

sudo apt-get install libpng12-dev libfreetype6-dev libmotif-dev libxt-dev

Configure Xpdf using AFL.

CXX=~/bin/afl/afl-g++ ./configure --with-freetype2-includes=/usr/include/freetype2
make clean all AFL_HARDEN=1

Note the absence of --disable-shared in the command above. If I included it, I received the following error:

configure: WARNING: unrecognized options: --disable-shared

According to Michal Zalewski, in these cases, --disabled-shared can simply be left out of the configure command.

The compilation process took under a minute.

pdf samples

I used a set of PDF samples that I obtained from a guy at a place. I put these samples in ~/Downloads/pdfsamples. Next ran afl-cmin against these samples to find a minimized set of useful samples:

cd ~/Downloads
mkdir pdfsamples-minimized
~/bin/afl/afl-cmin -t 500 -i pdfsamples -o pdfsamples-minimized xpdf-3.04/xpdf/xpdf

I tried running the following command, but it would just hang after opening the first file:

~/bin/afl/afl-cmin -i pdfsamples -o pdfsamples-minimized xpdf-3.04/xpdf/xpdf

So in another terminal, I started a hackish way to kill any files that do not crash Xpdf. I know there are far more elegant solutions, but I was looking to fuzz, not create beautiful bash:

for i in `seq 1 1000`; do killall xpdf; sleep 1; done

This reduced the fileset by a significant amount. But I wanted the smallest set possible, so I reduced the fileset a bit more, by repeating this process a few times until the number of input files was the same as the number of output files. For example:

~/bin/afl/afl-cmin -i pdfsamples-minimized-1/ -o pdfsamples-minimized-2/ \
    xpdf-3.04/xpdf/xpdf @@
~/bin/afl/afl-cmin -i pdfsamples-minimized-2/ -o pdfsamples-minimized-3/ \
    xpdf-3.04/xpdf/xpdf @@
~/bin/afl/afl-cmin -i pdfsamples-minimized-3/ -o pdfsamples-final/ \
    xpdf-3.04/xpdf/xpdf @@

actual fuzzing

I tried running the following command:

~/bin/afl/afl-fuzz -i pdfsamples-final/ -o pdf-out xpdf-3.04/xpdf/xpdf @@

However, I recieved the following error:

[-] Hmm, your system is configured to send core dump notifications to an
    external utility. This will cause issues due to an extended delay
    between the fuzzed binary malfunctioning and this information being
    eventually relayed to the fuzzer via the standard waitpid() API.
    
    To avoid having crashes misinterpreted as hangs, please log in as root
    and temporarily modify /proc/sys/kernel/core_pattern, like so:

        echo core >/proc/sys/kernel/core_pattern

After running that command as root, and re-attempting afl-fuzz, I got a new error:

[-] Hmm, looks like the target binary terminated before we could complete a
    handshake with the injected code. There are two probable explanations:
    
    - The current memory limit (50.0 MB) is too restrictive, causing an OOM
      fault in the dynamic linker. This can be fixed with the -m option. A
      simple way to confirm the diagnosis may be:
    
      ( ulimit -Sv $[49 << 10]; /path/to/fuzzed_app )
    
      Tip: you can use http://jwilk.net/software/recidivm to quickly
      estimate the required amount of virtual memory for the binary.

At the suggestion of a friend, I added the -m2G parameter to the afl-fuzz command:

~/bin/afl/afl-fuzz -m2G -i pdfsamples-final/ -o pdf-out xpdf-3.04/xpdf/xpdf @@

This did the trick. However, once it was fuzzing away, I noticed a very slow execution rate:

slow-performance-1

So I tried following instructions from this blog.

cat << EOF > ~/bin/set-up-ramdisk.sh
#!/bin/bash
if grep -qs '/tmp/afl-ramdisk' /proc/mounts; then
    sudo umount /tmp/afl-ramdisk
    rm -rf /tmp/afl-ramdisk
fi
sudo mkdir /tmp/afl-ramdisk && sudo chmod 777 /tmp/afl-ramdisk
sudo mount -t tmpfs -o size=1G tmpfs /tmp/afl-ramdisk
cp -R ~/Downloads/xpdf-3.04 /tmp/afl-ramdisk
cp -R ~/bin/afl /tmp/afl-ramdisk
cp -R ~/Downloads/pdfsamples-minimized-4 /tmp/afl-ramdisk/samples
EOF
chmod +x ~/bin/set-up-ramdisk.sh
sudo -E env "PATH=$PATH" set-up-ramdisk.sh

Next, the following commands kicked off the new setup on the RAM disk:

cd /tmp/afl-ramdisk
./afl/afl-fuzz -m2G -i samples/ -o pdf-out xpdf-3.04/xpdf/xpdf @@

The results actually dropped a bit to 22-24 execs/second. After setting the VM to use 6 cores, it only slightly increased up to about 45-48 execs/second.

So to utilize all 6 cores, I tried using bnagy’s afl-launch:

go get -u github.com/bnagy/afl-launch
cat << EOF >> ~/.bashrc
export GOPATH=\$HOME/.go
export PATH="\$PATH:\$GOPATH/bin"
EOF

And to set up the ramdisk afresh, I ran the script once more:

sudo -E env "PATH=$PATH" set-up-ramdisk.sh

And finally kicked it off:

cd /tmp/afl-ramdisk
export PATH="$PATH:/tmp/afl-ramdisk/afl"
sudo chown $(whoami):$(whoami) -R afl/ samples/ xpdf-3.04/
afl-launch -m="2G" -i samples -o pdf-out -n 4 xpdf-3.04/xpdf/xpdf @@

To watch the progress, I ran:

watch afl-whatsup -s pdf-out

Still slow:

slow-performance-2

Time for a more dramatic approach: run the entire VM in RAM.

Since I’m running a Windows host, I used imdisk to set up a RAM disk. This has served me well in the past, so let’s give it another go. I set up a disk to run the Ubuntu VM (7.4 gigs):

# Run the following from an Administrator prompt.
imdisk -a -m Z: -o rw,fix,hd -s 15G -p "/fs:ntfs /q /y"
# Copy virtual machine to ramdisk.
cp -R E:\vms\fuzz-machine Z:\fuzz-machine

Next I started the machine and then just ran the fuzzing commands from the ~/Downloads directory:

cd ~/Downloads
export PATH="$PATH:/home/$(whoami)/bin/afl"
afl-launch -m="2G" -i pdfsamples-final -o pdf-out -n 4 xpdf-3.04/xpdf/xpdf @@

Checking on the performance, there is no improvement:

watch afl-whatsup -s pdf-out

slow-performance-3

I don’t even.

Given that this is my first endeavor with AFL, I’m sure there are things that I’m overlooking and/or that I could have done in a much more efficient manner. I hope to follow this post up at some point with lessons learned, and perhaps a better approach.

misc notes

For those who are looking for a large set of PDF files, but who don’t know a guy at a place, then I suggest checking out the huge PDF repository of both clean and malicious PDFs, which are made available here. Huge thanks to Mila for making this available.