Skip to content

Basic Blackbox Fuzzing

Marshall Whittaker edited this page Jun 6, 2017 · 2 revisions

What is blackbox fuzzing

Origins

Blackbox fuzzing (or fuzz testing) was originally developed by Barton Miller at the University of Wisconsin in 1989. A fuzzer will input massive amounts of random or semi-random data into another program to see how it responds, then reports back with details on how the program responded to the "fuzz test". Most often the result of a fuzz test is only a crash, where it be the system, or a process spawned on the system. You can use fuzz testing software such as ansvif to find bugs that often lead to security vulnerabilities, as well as verifying code for basic quality assurance purposes (making sure that something doesn't crash because of two incompatible flags for example).


Fuzzing is often referred to as brute force vulnerability discovery. It's akin to hitting a login prompt with millions of passwords to see what works; except a fuzzer just does this semi-randomly on another application, or operating system to see what flags, files, or other types of input will cause a program to malfunction.

ansvif

Ansvif originally went under two different names in it's very early stages of development, peachfuzz (discarded because it is already used) and segfault_4me, before I decided on the acronym ansvif in March, 2015. Ansvif stands for A Not So Very Intelligent Fuzzer, because it was intended to be a small offhand C++ project, then morphed into something actually useful. It is a highly adaptable, scalable, advanced fuzz tester with many options and ways to implement either command line fuzzing, file fuzzing, and system call fuzzing.

A list of ansvif options

-t This file should hold line by line command arguments as shown in the example file.
-e This file should hold line by line environment variables as shown in the example
file. You can usually get these by doing something like:
$ strings /bin/mount | perl -ne 'print if /[A-Z]=$/' > mount_envs
-c Specifies the command path.
-p Specifies the manpage location (as an integer, usually 1 or 8)
-m Specifies the commands manpage.
-D Dumps whats found in the manpage.
-f Number of threads to use. Default is 2.
-b Specifies the buffer size to fuzz with. 256-2048 Is usually sufficient.
-r Uses only random garbage data.
-o Writes output to log file.
-z Randomize the buffer size from 1 to what is specified by -b.
-x Other junk to put in. Usernames and such can go here.
-S Seperator between options.
-s Omitted character specification. Defaults are <>\\n |&\[]\()\{}:;\ and newline is mandatory.
-T Timeout for threads.
-W Timeout for threads.
-L Unpriviledged user to run as if root.
-A Always put whats after this after command to run.
-B Always put whats after this before the command to run.
-F File to feed into the program that -x along with normal fuzzing data will be put in.
-n Never use random data in the fuzz.
-R Run this command after each fuzz.
-C A Non standard error code to detect.
-V Use Valgrind if installed.
-1 Try to make it fault once, if it doesn't happen, throw error code 64. Useful for scripting.
-P Use % to represent binary in fuzz.
-M Max arguments to use in the fuzz.
-y Short for -b 0 and usually only useful with -A or -B
-K Keep fuzzing after a crash in the target.
-v Verbose.
-d Debug data.
-h Shows the help page.

A basic fuzz with ansvif to get you started

Let's start with the most basic fuzz.

We'll only be using the required command line options, and we can use the ./faulty program that is in ansvif's source tree for an example. You probably want core dumps on for doing debugging after the crash, so let's do: ulimit -c unlimited

Then we can do:

./ansvif -t examples/space.txt -c ./faulty -b 32
This means blackbox fuzz the program ./faulty (specified with the -c option), with the template being a file with a space on one line (specified with the -t option and the file path), and a static buffer size of 32 (specified with -b).
You should get some output that may look like variations of:

running strcpy...
*** buffer overflow detected ***: ./faulty terminated
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7eff0f5127e5]
/lib/x86_64-linux-gnu/libc.so.6(__fortify_fail+0x5c)[0x7eff0f5b356c]
/lib/x86_64-linux-gnu/libc.so.6(+0x116570)[0x7eff0f5b1570]
/lib/x86_64-linux-gnu/libc.so.6(+0x1158c2)[0x7eff0f5b08c2]
./faulty[0x40069a]
./faulty[0x40055c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7eff0f4bb830]
./faulty[0x400599]
======= Memory map: ========
00400000-00401000 r-xp 00000000 fc:00 7865508 /home/marshall/Code/ansvif/faulty
00600000-00601000 r--p 00000000 fc:00 7865508 /home/marshall/Code/ansvif/faulty
00601000-00602000 rw-p 00001000 fc:00 7865508 /home/marshall/Code/ansvif/faulty
01016000-01038000 rw-p 00000000 00:00 0 [heap]
7f45dd241000-7f45dd257000 r-xp 00000000 08:06 660485 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f45dd257000-7f45dd456000 ---p 00016000 08:06 660485 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f45dd456000-7f45dd457000 rw-p 00015000 08:06 660485 /lib/x86_64-linux-gnu/libgcc_s.so.1
7f45dd457000-7f45dd616000 r-xp 00000000 08:06 662334 /lib/x86_64-linux-gnu/libc-2.23.so
7f45dd616000-7f45dd816000 ---p 001bf000 08:06 662334 /lib/x86_64-linux-gnu/libc-2.23.so
7f45dd816000-7f45dd81a000 r--p 001bf000 08:06 662334 /lib/x86_64-linux-gnu/libc-2.23.so
7f45dd81a000-7f45dd81c000 rw-p 001c3000 08:06 662334 /lib/x86_64-linux-gnu/libc-2.23.so
7f45dd81c000-7f45dd820000 rw-p 00000000 00:00 0
7f45dd820000-7f45dd846000 r-xp 00000000 08:06 660064 /lib/x86_64-linux-gnu/ld-2.23.so
7f45dda17000-7f45dda1a000 rw-p 00000000 00:00 0
7f45dda42000-7f45dda45000 rw-p 00000000 00:00 0
7f45dda45000-7f45dda46000 r--p 00025000 08:06 660064 /lib/x86_64-linux-gnu/ld-2.23.so
7f45dda46000-7f45dda47000 rw-p 00026000 08:06 660064 /lib/x86_64-linux-gnu/ld-2.23.so
7f45dda47000-7f45dda48000 rw-p 00000000 00:00 0
7ffe36bd0000-7ffe36bf1000 rw-p 00000000 00:00 0 [stack]
7ffe36bf8000-7ffe36bfa000 r--p 00000000 00:00 0 [vvar]
7ffe36bfa000-7ffe36bfc000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
PID: 9937
Exit Code: 134
Crashed with command:
./faulty $(printf "\\x20\\x20\\x20\\x2D\\x31\\x20\\x20") $(printf "\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x20")
The output is pretty self explanatory, meaning that the PID (process ID) of the faulty as it crashed was 9937, the exit code it threw was 134, and it should output the command that 'faulty' crashed with (in this case with a buffer overflow).
The printf's are necessary because if we don't have them then ansvif would output a bunch of garbage and potentially mess with the terminal.
You can try this yourself by rerunning the code that made 'faulty' crashed with by doing:
./faulty $(printf "\\x20\\x20\\x20\\x2D\\x31\\x20\\x20") $(printf "\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x25\\x73\\x20")
(or whatever ansvif output for you) in your terminal.