This is a simple walkthrough to install, setup and run AFL against an open source dumb C program.

What is AFL

American fuzzy lop is a security-oriented fuzzer that employs a novel type of compile-time instrumentation and genetic algorithms to automatically discover clean, interesting test cases that trigger new internal states in the targeted binary. This substantially improves the functional coverage for the fuzzed code.
AFL website

Install AFL

Download

First, download the latest AFL source code:

1
2
3
4
5
6
7
ghozt@maze:~/research/fuzz$ wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
ghozt@maze:~/research/fuzz$ tar xvf afl-latest.tgz
afl-2.52b/
afl-2.52b/afl-as.h
afl-2.52b/libtokencap/
afl-2.52b/libtokencap/libtokencap.so.c
...

Build

Simply make and make install:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ghozt@maze:~/research/fuzz/afl-2.52b$ make
[*] Checking for the ability to compile x86 code...
[+] Everything seems to be working, ready to compile.
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-gcc.c -o afl-gcc -ldl
...
[+] All right, the instrumentation seems to be working!
[+] LLVM users: see llvm_mode/README.llvm for a faster alternative to afl-gcc.
[+] All done! Be sure to review README - it's pretty short and useful.
NOTE: If you can read this, your terminal probably uses white background.
This will make the UI hard to read. See docs/status_screen.txt for advice.

ghozt@maze:~/research/fuzz/afl-2.52b$ sudo make install
...

ghozt@maze:~/research/fuzz/afl-2.52b$ afl-gcc --version
afl-cc 2.52b by <lcamtuf@google.com>
gcc (Ubuntu 12.3.0-1ubuntu1~23.04) 12.3.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

All good :)

Fuzzing

Binary compilation

The following vulnerable program will be used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{

char service[12];
char passphrase[20];
printf("Service: \n");
gets(service);
printf("Passphrase: \n");
gets(passphrase);

if(strcmp(service, "fuzzing") == 0)
{
if(strcmp(passphrase, "p@s5w0rD") == 0)
{
printf("Authorized -- Fuzzing in progress\n");
return EXIT_SUCCESS;
}
}

printf("Access denied...\n");

return EXIT_FAILURE;
}

In order to fuzz the binary, it must be compiled thanks to our fresh installed afl-gcc:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ghozt@maze:~/research/fuzz/example$  afl-gcc -fno-stack-protector -z execstack vuln.c -o vuln
afl-cc 2.52b by <lcamtuf@google.com>
vuln.c: In function ‘main’:
vuln.c:10:9: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
10 | gets(service);
| ^~~~
| fgets
vuln.c:14:12: warning: implicit declaration of function ‘strcmp’ [-Wimplicit-function-declaration]
14 | if(strcmp(service, "fuzzing") == 0)
| ^~~~~~
vuln.c:3:1: note: include ‘<string.h>’ or provide a declaration of ‘strcmp’
2 | #include <stdlib.h>
+++ |+#include <string.h>
3 |
afl-as 2.52b by <lcamtuf@google.com>
[+] Instrumented 6 locations (64-bit, non-hardened mode, ratio 100%).
/usr/bin/ld: /tmp/ccAAiBud.o : dans la fonction « main » :
/home/ghozt/research/fuzz/example/vuln.c:10: avertissement : the `gets' function is dangerous and should not be used.

The binary works as expected:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ghozt@maze:~/research/fuzz/example$ ./vuln 
Service:
fuzzing
Passphrase:
p@s5w0rD
Authorized -- Fuzzing in progress
ghozt@maze:~/research/fuzz/example$ ./vuln
Service:
fuzzing
Passphrase:
incorrect
Access denied...
ghozt@maze:~/research/fuzz/example$

Test cases

AFL works with tests cases. That mean you have to tell AFL what a legit input looks like.
First, we will create two directories, one for the tests cases, and another one to store the fuzzing results:

1
ghozt@maze:~/research/fuzz/example$ mkdir {testcases,results}

Now, remember the code, in a code coverage point of view, we have three different paths:

1
2
3
4
5
6
7
8
9
10
11
// First path --> service is invalid
if(strcmp(service, "fuzzing") == 0)
{
// Second path --> service is valid, but passphrase is not
if(strcmp(passphrase, "p@s5w0rD") == 0)
{
// Third path --> All is fine :)
printf("Authorized -- Fuzzing in progress\n");
return 0;
}
}

Let’s create 3 test cases:

1
2
3
ghozt@maze:~/research/fuzz/example$ echo "fuzzing\np@s5w0rD" > testcases/tc1.txt
ghozt@maze:~/research/fuzz/example$ echo "fuzzing\nbadpass" > testcases/tc2.txt
ghozt@maze:~/research/fuzz/example$ echo "badserv\nbadpass" > testcases/tc3.txt

The tests cases here are simply texts files representing the data as we could provide it when we execute the binary.

Run

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ghozt@maze:~/research/fuzz/example$ afl-fuzz -i testcases -o results vuln
afl-fuzz 2.52b by <lcamtuf@google.com>
[+] You have 16 CPU cores and 2 runnable tasks (utilization: 12%).
[+] Try parallel jobs - see /usr/local/share/doc/afl/parallel_fuzzing.txt.
[*] Checking CPU core loadout...
[+] Found a free CPU core, binding to #0.
[*] Checking core_pattern...

[-] Hmm, your system is configured to send core dump notifications to an
external utility. This will cause issues: there will be an extended delay
between stumbling upon a crash and having this information relayed to the
fuzzer via the standard waitpid() API.

To avoid having crashes misinterpreted as timeouts, please log in as root
and temporarily modify /proc/sys/kernel/core_pattern, like so:

echo core >/proc/sys/kernel/core_pattern

[-] PROGRAM ABORT : Pipe at the beginning of 'core_pattern'
Location : check_crash_handling(), afl-fuzz.c:7275

Some errors need to be fixed. Just follow the output steps:

1
2
3
4
echo core >/proc/sys/kernel/core_pattern
cd /sys/devices/system/cpu
echo performance | tee cpu*/cpufreq/scaling_governor

It all is ok, the following screen will be displayed:

If you want to fully understand this screen, you should read this documentation

Here we can see that AFL found a uniq crashe. That means one of our three paths can be crashed.

The results are stored in the result/crashes directory and contains input that can crash our binary. Let’s verify it:

1
2
3
4
5
ghozt@maze:~/research/fuzz/example$ ./vuln < results/crashes/id\:000000\,sig\:11\,src\:000002\,op\:havoc\,rep\:128 
Service:
Passphrase:
Access denied...
Erreur de segmentation

The program crash with segfault :)

2024-09-12

⬆︎TOP