A Simple Tutorial of AFL-Fuzzer « SpencerWuWuWu
AFL(American Fuzzy Lop) is a very powerful ffindingsuzzer. Unlike other examples in other websites, this tutorial is aimed to provide a vivid demostration of how and what afl is doing.
I will recommend to install afl in a virtual environment in virtualbox since we might change some of the settings in your computer which might affect your other programs.
I’m using arch-linux to do the demonstration.
Nội Dung Chính
Download and install afl
First we need to download the afl binaries. Arch-linux provides an official package. However, this package doesn’t support afl qemu-mode, which enable us to fuzz a binary file without compiling without the binary’s source code.
If you don’t need to use qemu-mode, run the following command directly to install afl. After the installation, you can skip to the next section and start using afl.
sudo pacman -S --noconfirm afl
If you want to use qemu-mode, or if you are not using arch linux, download the following source of the official website and compile.
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz tar -xf afl-latest.tgz cd afl-<version> make
Then edit your ~/.bashrc to point the command to the directory you extract. Add these lines to your bashrc. Remember to change the path and the shell you are using.
alias afl-fuzz="$HOME/path-to-afl/afl-fuzz alias afl-tmin="$HOME/path-to-afl/afl-tmin alias afl-showmap="$HOME/path-to-afl/afl-showmap
Then restart your shell.
exec $SHELL
Here we have finish the basic setup of afl.
A Simple Example
Following is a simple program. It will abort either we pipe “deadbeef” or take the string as an angrument. I found a similiar code online written in objectiveC and edit it to a C program that can be fuzz with afl.
test.c
#include <stdio.h> #include <stdlib.h>
int
main
(
int
argc
,
char
**
argv
)
{
char
ptr
[
20
];
if
(
argc
>
1
){
FILE
*
fp
=
fopen
(
argv
[
1
],
"r"
);
fgets
(
ptr
,
sizeof
(
ptr
),
fp
);
}
else
{
fgets
(
ptr
,
sizeof
(
ptr
),
stdin
);
}
printf
(
"%s"
,
ptr
);
if
(
ptr
[
0
]
==
'd'
)
{
if
(
ptr
[
1
]
==
'e'
)
{
if
(
ptr
[
2
]
==
'a'
)
{
if
(
ptr
[
3
]
==
'd'
)
{
if
(
ptr
[
4
]
==
'b'
)
{
if
(
ptr
[
5
]
==
'e'
)
{
if
(
ptr
[
6
]
==
'e'
)
{
if
(
ptr
[
7
]
==
'f'
)
{
abort
();
}
else
printf
(
"%c"
,
ptr
[
7
]);
}
else
printf
(
"%c"
,
ptr
[
6
]);
}
else
printf
(
"%c"
,
ptr
[
5
]);
}
else
printf
(
"%c"
,
ptr
[
4
]);
}
else
printf
(
"%c"
,
ptr
[
3
]);
}
else
printf
(
"%c"
,
ptr
[
2
]);
}
else
printf
(
"%c"
,
ptr
[
1
]);
}
else
printf
(
"%c"
,
ptr
[
0
]);
return
0
;
}
compile and try the program, if you pipe “deadbeef” into the binary file you complie, it will abort. Otherwise it will print out the string you pipe in again.
Fuzzing with AFL while source code is avaible
AFL provides several compilers to make the fuzzing process more efficiency. The example above is a C program so we can use afl-gcc or afl-clang to recompile our code.
CC=/path-to-afl/afl-gcc make clean all
or
CC=/path-to-afl/afl-clang ./configure make clean all
Now we will get a new binary file called a.out, we can start fuzzing this program.
Before start fuzzing, I’ll first introduce a useful function of afl, afl-showmap.
afl-showmap
The afl-showmap tool will run a given instrumented binary (passing any input received via stdin to the instrumented binary via stdin) and print a report of the feedback it sees during program execution. For instance:
afl-showmap -o /dev/null -- ./a.out < <(echo hello)
you get
[2017-01-31 Tue 10:57 pm] spencerwu @ arch64 : ~/project/simple $ afl-showmap -o /dev/null -- ./a.out < <(echo hello) afl-showmap 2.35b by <[email protected]> [*] Executing './a.out'... -- Program output begins -- hello h-- Program output ends -- [+] Captured 6 tuples in '/dev/null'.
Tuples are the transition between blocks of programs. As the following digram shows, although 1 and 2 has the same result E, afl will regard them as different tuples.
#1: A -> B -> C -> D -> E #2: A -> B -> C -> A -> E
If you run afl-showmap with a input “hello”, “dead”, “deadbeef”, you’ll see different result of the tuple number. The different tuple number is how afl find the program’s crashes cases.
There are ohter useful command such as afl-tmin, afl-cmin, but I won’t dicuss them here yet.
Fuzzing the program
Now we can start to use the afl-fuzz command to fuzz our program. But first we have to create the test cases. By the official user guide we should start the test case by the string “hello”. I will write some article about optimizing the test input, but “hello” shall do well in this tutorial.
We start by creating the testcase file and create a file containing the string “hello” in it.
mkdir testcases echo "hello" > testcases/foo
After creating our testcase, we can start the fuzzing process. Afl will start to fuzz our program with the string in the testcase directory, then adapt it gradually to fint the crash case.
Let’s create a directory to contain the afl output. First we have to switch to root to arrange your core_pattern. Login as root and type the following command.
echo core >/proc/sys/kernel/core_pattern
Then we can start fuzzing.
mkdir findings afl-fuzz -i testcases -o findings -- ./a.out
You will see the afl fuzzing window.
If you’ve done the above steps correctly, your screen will be like this. After a couple of minutes, you can see that afl found some uniq crashes. You can type ctrl+C to terminate the fuzzing process.
Now in the findings directory, you’ll see some files and three directories.
The three directories contain the folowing information.
queue – contains test cases for every distinctive execution path, plus all the starting files given by the user. In this tutorial, if you print out the files in queue/, you’ll see as the id increases, afl gradually approach from the init input “hello” to “deadbeef”, which will crash our program.
crashes – contains test cases that cause the tested program to receive a fatal signal
hangs – test cases that cause the tested program to time out
In our case, you’ll find a file that contains a string begins with “deadbeef”, which will make our program abort.
Fuzzing without source code
AFL also provide ways to fuzz without recompiling source code with qemu. First cd into the afl directory in your system. You’ll see a directory call “qemu_mode”.
cd qemu_mode ./build_qemu_support.sh
Before running the build script, you have to change your python version to python2 from the system level if you are using arch linux as the default python of arch is python3.
You can refer to this method. Remember to undo the change after installing.
You may also confront some error stating that missing some package. All packages can be found and installed by pacman.
After finish the install process, you can start fuzzing with qemu. First compile test.c again with gcc, then run with the same command as above but add the -Q argument. Then you can start fuzzing as we do in the previous section.
afl-fuzz -i testcases -o findings -Q -- ./a.out
This is the end of this simple tutorial. Hope youll get a clearer sight of how to opearte afl fuzzer ^^.