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:
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:
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
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.