Fuzzing with Fandango#
Creating a Name Database#
Let us now come up with an input that is slightly more complex. We want to create a set of inputs with names of persons and their respective age. These two values would be comma-separated, such that typical inputs would look like this:
Alice Doe,27
John Smith,45
...
This makes the overall format of our input look like this:
<start> ::= <person_name> "," <age>
with <age>
again being a sequence of digits, and a <person>
’s name being defined as
<person_name> ::= <first_name> " " <last_name>
where both first and last name would be a sequence of letters - first, an uppercase letter, and then a sequence of lowercase letters. The full definition looks like this:
<start> ::= <person_name> "," <age>
<person_name> ::= <first_name> " " <last_name>
<first_name> ::= <name>
<last_name> ::= <name>
<name> ::= <ascii_uppercase_letter><ascii_lowercase_letter>+
<age> ::= <digit>+
The symbols <ascii_uppercase_letter>
, <ascii_lowercase_letter>
, and <digits>
are predefined in the Fandango Standard Library; they are defined exactly as would be expected.
Create or download a file persons.fan
and run Fandango on it:
$ fandango fuzz -f persons.fan -n 10
Your output will look like this:
Otlgfy Mctwpj,14
Cd Ywdp,969
Mbi Rj,3003
Wt Vpbmdo,06
Tcnth Ke,328
Nm Xqtpd,15
Zyqgcz Paq,6
Ore Cbxdkz,750
Rwfbro Gw,984
Rqrdi Uempn,615
Such random names are a typical result of fuzzing – that is, testing with randomly generated values. How do we get these into a program under test?
Feeding Inputs Into Programs#
So far, we have had Fandango simply output the strings it generates. This is nice for understanding and debugging, but not necessarily useful for processing these inputs further. Fandango provides a number of means to help you process its output.
Feed Inputs into a Single file#
Fandango has a -o
option that directs its output into a single file.
To store the generated strings in a file named persons.txt
, use -o persons.txt
:
$ fandango fuzz -f persons.fan -n 10 -o persons.txt
By default, the generated strings are separated by newlines.
With the -s
option, you can define an alternate separator.
To have the outputs generated separated by :
, for instance, use
$ fandango fuzz -f persons.fan -n 10 -o persons.txt -s ':'
Feed Inputs into Individual Files#
With the -d
option, Fandango can store its strings into individual files in the directory given after -d
.
They can then be picked up for post-processing.
To store all outputs in a directory named persons
, for instance, use -d persons
:
$ fandango fuzz -f persons.fan -n 10 -d persons
Fandango will create the directory and store the inputs as Fandango-0001.txt
, Fandango-0002.txt
, etc.
Note
If the directory is already present, Fandango will use that directory.
Warning
Fandango will overwrite files Fandango-0001.txt
, Fandango-0002.txt
, etc., but leave other files in the directory unchanged.
If you want a different file extension (for instance, because .txt
is not suitable), Fandango provides a --filename-extension
option to set a different one.
Invoking Programs Directly#
You can have Fandango invoke programs directly, and have Fandango feed them the generated inputs. The programs are specified as arguments on the command line.
There are three ways to pass the input into programs, on the command line, via standard input, and in-process to a shared object with a libFuzzer style harness.
Refer to this example for further details on how a harness can be compiled to interface with each mode.
Passing Inputs on the Command Line#
When Fandango invokes programs, you can pass input as a file name as last argument on the program’s command line.
This is the default, and can also be obtained with the --input-method=filename
option.
For instance, to test the wc
program with its own -c
option and the inputs Fandango generates, use
$ fandango fuzz -f persons.fan -n 10 wc -c
The wc
program is then invoked as wc -c FILE_1
, wc -c FILE_2
, etc., where each FILE
contains an individual input from Fandango.
Passing Inputs via Standard Input#
As an alternative, Fandango can pass inputs via standard input.
For this, use the --input-method=stdin
option.
For instance, to test the cat
program with its own -n
option and the inputs Fandango generates, use
$ fandango fuzz -f persons.fan -n 10 --input-method=stdin cat -n
The cat
program is then invoked repeatedly, each time passing a new Fandango-generated input as its standard input.
Calling a libFuzzer style harness directly#
Fandango further supports calling a libFuzzer style harness directly. If you are able to compile your binary to a shared object (.so
on Linux or .dylib
on macOS), Fandango can load the binary and directly call the function. This requires some extra work initially but removes the need to start a new process for each input evaluation, thus improving performance significantly. Fandango will exit on the first crashing input. --input-method=libfuzzer
is only supported in combination with --file-mode=binary
, since libFuzzer style harnesses expect binary data.
Fandango is invoked as follows:
$ fandango fuzz -f persons.fan -n 10 --input-method=libfuzzer --file-mode=binary ./harness.{so,dylib}
Executable .fan
files#
On a Unix system, you can turn a .fan
file into an executable file by placing a line
#!/usr/bin/env -S fandango fuzz -f
at the top.
If you set its “executable” flag with chmod +x FILE
, you can then directly execute the .fan
file as a command as if it were prefixed by fandango fuzz -f
.
As an example, let us create a file fuzz-persons.fan
:
$ (echo '#!/usr/bin/env -S fandango fuzz -f'; cat persons.fan) > fuzz-persons.fan
$ chmod +x fuzz-persons.fan
You can now invoke the file, even with extra arguments:
$ ./fuzz-persons.fan -n 1
Eqpa Dlb,84093
Note
The env
command varies greatly across Unix flavors and versions.
If the above does not work on your system, try skipping the -S
option, such that the first line reads:
#!/usr/bin/env fandango fuzz -f