Minerva_lib is still in its early development stage, therefore it is full of bugs. It also lacks many features. Since dumb fuzzing is just dumb fuzzing please don't expect any sort of intelligence from it. You're using it at your own risk. You've been warned.
If you would like to contribute to the project, please feel free to submit pull requests, bug reports, letters of complain (or love) etc. Any support is appreciated.
PFL is a fuzzer designed mainly for torturing stuff (mainly libraries or APIs, sometimes co-workers). It uses minerva algorithm (please refer to an article listed as [1] in reading section for any further information). Minerva_lib is able to fuzz any piece of code, as long as it can be linked against its core.
PFL can also be treated as an animal. Feed it with C function prototypes, supply a simple Makefile and it will treat you with a new shiny fuzzer. Since, as mentioned earlier on, PFL is in its early development stage, most cases you will have to provide generators though.
Let's make dumb fuzzing great again!
We tried to make this process as painless as possible. It is on you to provide config file (target.mi) and magic makefile (refer to a few target examples we provided in case of any problems). Than, PFL generates target.c and target.h, which supported by minerva_core results in target_bin (your shiny fuzzer, that is).
+-----------+ +----------------+
| target.mi |<----------------| magic Makefile |
| (config) | +----------------+
+-----------+ |
| | things you provide
-- 8< - 8< - 8< - 8< - 8< - 8< - 8< - 8< - 8< ------------------
| | stuff that is done
| | automatically
| (mi2c compiler) |
| |
v v
+------------+ +--------------+
| target.[ch]|-------------->| minerva core |
+------------+ +--------------+
| |
\--------------+---------------/
|
v
+------------+
| target_bin | (your shiny fuzzer)
+------------+
The format used in configuration file is very similar to C format. To help you understand it we provide the following scheme.
include files (e.g.. #include <header_file.h>
C prototypes => check function; (e.g. int foo(int x) => generic_success;)
In order to help you get the basics we created a toy target(You can find it in /target/toy directory). Taking toy api's declarations from toy.h:
int zero(void);
int add_one(int x);
int crashme(int x);
A configuration file (toy.mi) would look like that:
#include <minerva_generic.h>
#include <toy.h>
--this a comment
int zero() => generic_success;
int add_one(int x) => generic_success;
int crashme(int x) => generic_success;
For the sake of PFL working, minerva includes generic functions in order to check the result of particular call. Take a look at include/minerva_generic.h for the complete list of these.
###Variables can also have annotations
void BN_CTX_free(BN_CTX *c {DESTROY}) => generic_void;
DESTROY indicates that call destroys the variable, so fuzzer wont use it any further.
int mutate_int_and(int a {MUTATE}, int b {MUTATE}) => generic_success;
MUTATE indicates that particular variable is mutated by the function. Fuzzer uses it for mutation phase (see var_mut shell command).
Create your own subdirectory in target/ directory, it should contain configuration file (as explained above) and Makefile. That's it, a minimal set of files needed to generate fuzzer. Example Makefile looks as follows(/target/toy/Makefile):
TARGET=toy
LOCAL_SRC= \
toy.c
include ../../mk/minerva.mk
TARGET - is a target name (could be any name). LOCAL_SRC - is a set of files that is additionally linked with the fuzzer (helper functions etc).
Makefiles may also include LDFLAGS in order to link against other libraries (see target/openssl/Makefile):
TARGET=openssl
LOCAL_SRC= \
minerva_vars_init.c
LDFLAGS=-lssl -lcrypto
include ../../mk/minerva.mk
Including of minerva.mk is mandatory (it does magic to generate fuzzer for You). Building system is mostly inspired by BSD ports.
Makefile also supports a few parameters, as follows:
- DEBUG=1 - build without optimizations, additional debug features are turned on.
- ASAN=1 - build with address sanitizer
- WITHOUT_READLINE=1 - build without readline library
- WITHOUT_PROGRESSBAR=1 - build without progressbar library
Fuzzing should be as easy as running compiled binary.
target/toy/ $ ./bin/minerva-toy-toy
seed: 2946546160
\o/ found crash *yay* \o/
target/toy/ $ lldb ./bin/minerva-toy-toy
(lldb) target create "./bin/minerva-toy-toy"
Current executable set to './bin/minerva-toy-toy' (x86_64).
(lldb) r
Process 58757 launched: './bin/minerva-toy-toy' (x86_64)
seed: 799555025
Process 58757 stopped
* thread #1: (...) toy.c:22, queue = 'com.apple.main-thread',
stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x000000010000809d minerva-toy-toy`crashme(x=5) + 29 at toy.c:22
19 {
20 int *y = NULL;
21 if (x > 4)
-> 22 return *y;
23 else
24 return x+2;
25 }
If you want to run the fuzzing in shell mode, execute the binary with -r option:
target/toy/ $ ./bin/minerva-toy-toy -r
seed: 100837884
pfl> help
fuzz([iter, [ctx]]) - fuzz: does iter iterations, uses ctx
var_mut(iter, ctx) - does iter mutations of variables stored in ctx
var_stat(ctx) - shows statistics about variables stored in ctx
func_stat(ctx) - shows statistics about functions calls stored in ctx
play(trace) - plays trace or context
restore("filename") - restores trace from file
save(trace, "filename") - saves trace to file
min(trace) - minimizes trace
show(trace) - shows trace
quit, exit - quits
In order to perform fuzzing, being in shell mode, use fuzz command:
target/toy/ $ ./bin/minerva-toy-toy -r
seed: 3366955315
pfl> x = fuzz(100)
Fuzzing |====================================================| ETA: 0h00m00s
pfl>
Fuzzing process saves traces, which can be replayed later on in order to reproduce particular execution. It uses simple format to save the function calls:
target/toy/ $ ./bin/minerva-toy-toy -T /dev/stdout
seed: 2631490055
\o/ found crash *yay* \o/
0 = zero()
1 = zero()
2 = crashme(1)
3 = add_one(2)
4 = crashme(3)
5 = crashme(0)
6 = crashme(2)
7 = zero()
8 = zero()
9 = zero()
10 = crashme(3)
11 = add_one(5)
12 = crashme(4)
target/toy/ $ ./bin/minerva-toy-toy -T tmp
seed: 3370842429
\o/ found crash *yay* \o/
target/toy/ $ ./bin/minerva-toy-toy -p tmp
seed: 1519824755
Replay |=================================================== | ETA: 0h00m00s
target/toy/ $ lldb ./bin/minerva-toy-toy
r -p tmp
(lldb) target create "./bin/minerva-toy-toy"
Current executable set to './bin/minerva-toy-toy' (x86_64).
(lldb) r -p tmp
Process 12467 launched: './bin/minerva-toy-toy' (x86_64)
seed: 904045424
Process 12467 stopped======================================= | ETA: 0h00m00s
* thread #1: tid = 0x2bca2, 0x000000010000823d minerva-toy-toy`crashme + 29,
queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1,
address=0x0)
A: Write a simple wrapper to make sure that those conditions are met.
A: Write wrapper or introduce new types that indicate internal state. Here's an quick example:
STH_new *STH_new(void) => generic_not_null;
STH *STH_init(STH_new *) => generic_not_null;
A: Use -h switch or help command in the shell mode.
A: You have to wrap function yourself, sorry.
A: Take a look at TODO list, fill bug reports, send PRs. Your help is more than welcome.
A: Python, GNU Make, C compiler, Bison, Flex, py-yacc, libprogressbar.
A: No, it's a coincidence.
A: Yes, including various minor bugs in OpenSSL, LibreSSL and OpenSSH.
A: We successfully ran this software on Linux, FreeBSD, NetBSD, OpenBSD and Mac OS X.
A: We don't know. However, You're more than welcome to develop it on your own and share it with us.
- http://akat1.pl/talks/2016-alligatorcon.pdf - slides about minerva_lib itself
- https://web.archive.org/web/20170717042933/http://php-security.org/2010/05/11/mops-submission-05-the-minerva-php-fuzzer/ - text about minerva algorithm
- http://www.slideshare.net/logicaltrust/torturing-the-php-interpreter - slides that contains something about minerva algorithm
- https://www.youtube.com/watch?v=bXpZoHlmsUU - talk about minerva_lib from BSidesWarsaw 2018 (encrypted in Polish)
- @akat1_pl - http://akat1.pl/
- n1x0n
- s1m0n
- PSi
- Zeruś