PFL is a fuzzer designed mainly for torturing stuff (mainly libraries or APIs, sometimes co-workers). It uses minerva algorithm. Minerva_lib is able to fuzz any piece of code, as long as it can be linked against its core.
2. Using SMT solvers, concolic execution,
second-order logic and knot theory to identify bugs
in software a.k.a polish fuzzy lop
shm a.k.a.
@akat1 pl
SecurityBSides
October 2018
5. Short story behind this talk
July 2015 - we (me, s1m0n, n1x0n) gave a talk at AlligatorCon
→ we promised to release fuzzer for libraries soon (tm)
...AlligatorCon 2016 was a great opportunity to do so!
6. Minerva Fuzzers Family
no cutting-edge content here, mostly dumb fuzzing stuff
idea implemented in minerva php fuzzer released at Month of
PHP bugs (2010) - http://php-security.org/2010/05/11/
mops-submission-05-the-minerva-php-fuzzer/
previous talks on this subject:
Torturing the PHP interpreter - Confidence 2016 - http:
//www.slideshare.net/logicaltrust/torturing-the-php-interpreter
Fuzzing Challenges - AlligatorCon 2015 - http:
//www.slideshare.net/slajdszer/fuzzing-challenges-alligatorcon
Fuzzing interpretera PHP (pl) - local OWASP meeting 2011 -
http://www.slideshare.net/logicaltrust/
201105-owasp-fuzzing-interpretera-php
simple but effective, we applied it to many targets & protocols
...one day we realized that universal implementation could be
written...
9. So here we are with shiny (((((((
minerva lib polish fuzzy lop
implementation of dead simple algorithm
written in C & python (py-yacc) & makefile (GNU) & flex &
bison (few KLOCs)
fuzzing
libraries APIs (((((
functions libraries
fuzz anything that can be linked with minerva piece of code
mostly to catch low hanging fruits (for now)
work in progress since 2012 - released (tm) under beerware
license
https://github.com/LogicalTrust/minerva lib
support for Linux, FreeBSD, NetBSD, OpenBSD, OS X...
11. Minerva algorithm - the idea
1. script ← ””
2. X ← Initial set of variables with their types
3. G ← Fresh variable generator
4. F ← Function database
5. for i in 1..n:
5.1 f ← GET RANDOM(F, X)
5.2 v ← G()
5.3 script ← script . v . ” = ” . f call with random arguments from X
(but with proper types)
5.4 X ← X ∪ (v, f result type)
6. return script
*ekhm*, what?
12. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
13. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
14. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
15. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
16. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
17. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
18. Minerva algorithm - the idea - example
F = {int = A(), int = B(int, int), char∗ = C(int), char∗ =
D(int, char∗), ...}
X = ∅
x, y - simple types
1. v1 = A(); X = {int : {v1}}
2. v2 = B(v1, v1); X = {int : {v1, v2}}
3. v3 = C(v2); X = {int : {v1, v2}, char∗ : {v3}}
4. v4 = D(v1, v3); X = {int : {v1, v2}, char∗ : {v3, v4}}
5. v5 = A(); X = {int : {v1, v2, v5}, char∗ : {v3, v4}}
6. ...repeat it until crash
19. Minerva - the algorithm
pros:
rather simple algorithm
easy to implement
in some way similar to CFG fuzzing
quite universal
make dumb fuzzing great again (tm)
cons:
dumb fuzzer is dumb
by default it produces pretty flat call trees
20. (((((((((((
Fuzzing problems marketing
in perfect case you have to only provide API definition (C
function prototypes)
found minor bugs in:
LibreSSL
OpenSSL
OpenSSH
...
easy configuration
easy to add new target
shell mode
framework to manage test cases (traces)
21. Minerva - how does it work?
+-----------+ +----------------+
| target.mi |----------------| magic Makefile |
| (config) | +----------------+
+-----------+ |
| |
| (mi2c compiler) |
v v
+------------+ +--------------+
| target.[ch]|--------------| minerva core |
+------------+ +--------------+
| |
--------------+---------------/
|
v
+------------+
| target_bin | (your shiny fuzzer)
+------------+
27. Minerva - configuration - what we need?
set of functions’ prototypes
set of check functions (some are provided by default)
set of #includes
28. Minerva - building system
inspired by (Free/Net/Open)BSD ports
written in Makefile (GNU)
adding new target should be as easy as writing few lines of
Makefile
support for ASAN/DEBUG builds
example file:
TARGET=openssl
LOCAL_SRC=
minerva_vars_init.c
LDFLAGS=-lssl -lcrypto
include ../../mk/minerva.mk
29. Minerva - the result
target/openssl$ make MICONFIG=bn.mi
####################################
# Generate Build targets #
####################################
../../compiler/mi2c.py -m bn.mi -o bin//target//target
cc -I../../include/ -Wall -O0 -Ibin//target/ -I. -c -o bin//target//target.o bin//t
####################################
# Build local #
####################################
cc -I../../include/ -Wall -O0 -Ibin//target/ -I. -c -o bin//local//minerva_vars_ini
####################################
# Building core #
####################################
bison -d ../../core//minerva_parser.y -o bin//core/minerva_parser.tab.c
flex -o bin//core/minerva_lexer.c --header-file=bin//core/minerva_lexer.h ../../cor
cc -I../../include/ -Wall -O0 -Ibin//target/ -c -o bin//core/minerva_lexer.o bin/
cc -I../../include/ -Wall -O0 -Ibin//target/ -c -o bin//core/minerva_parser.tab.o
30. Minerva - the result
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
cc -I../../include/ -Wall -O0 -Ibin//target/ -I../../include/ -I../../lib/progressb
####################################
# Linking #
####################################
cc -o bin//minerva-openssl-bn bin//core/main.o bin//core/minerva_dict.o
bin//core/minerva_call.o bin//core/minerva_func.o bin//core/minerva_generic.o
bin//core/minerva_var.o bin//core/xmalloc.o bin//core/minerva_repl.o
bin//core/minerva_loop.o bin//core/minerva_trace.o
bin//core/dict.o bin//core/minerva_lexer.o
bin//core/minerva_parser.tab.o bin//target//target.o
bin//local//minerva_vars_init.o -lssl -lcrypto
../../lib/progressbar/libprogressbar.a -lreadline -lcurses
31. Minerva - the result
target/openssl $ ./bin/minerva-openssl-bn
seed: 2813646763
o/ found crash *yay* o/
32. Minerva - (Libre—Open)SSL - off-by-one #1
Let’s take BIGNUM −0 and apply it to the BN bn2hex
crypto/bn/bn print.c:
char *BN_bn2hex(const BIGNUM *a)
{
char *buf;
char *p;
buf=(char *)OPENSSL_malloc(a-top*BN_BYTES*2+2);
[...]
p=buf;
if (a-neg) *(p++)=’-’;
if (BN_is_zero(a)) *(p++)=’0’;
for (i=a-top-1; i =0; i--)
[...]
*p=’0’;
How to get −0 in LibreSSL? It is a task for the listener
33. Minerva - (Libre—Open)SSL - off-by-one #2
Let’s call BN rand(BN, 1, 1, 0)- bnrand crypto/bn/bn rand.c:
static int
bnrand(int pseudorand, BIGNUM *rnd, int bits, int top, int bottom)
{
unsigned char *buf = NULL;
[...]
bytes = (bits + 7) / 8;
bit = (bits - 1) % 8;
buf = OPENSSL_malloc(bytes);
if (top != -1) {
if (top) {
if (bit == 0) {
buf[0] = 1;
buf[1] |= 0x80;
[...]
37. minerva - shell mode
you can now be the fuzz operator
guide process where to go!
stats - func stat, var stat
fuzzing - fuzz (iterations, context)
var = func(arg1, ..., argn)
context = trace, vars, funcs
test case (trace) management - restore, save
mutations, coverage, ...
38. minerva - minimize test case
$ ./bin/minerva-toy-toy -r
seed: 607440374
pfl x = fuzz()
o/ found crash *yay* o/
pfl m = min(x)
pfl show(m)
0 = zero()
16 = add_one(0)
23 = crashme(16)
26 = crashme(23)
88 = crashme(26)
pfl play(m)
Segmentation fault: 11======= | ETA: 0h00m00s
40. minerva - tipstricks
automate things as much as you can
jenkins Kibana ... are your friends
cluster crashes (or be lost)
*sanitizers are more than useful
...for more check our presentation from AlligatorCon 2015
41. minerva - problems
I showed you perfect cases
... BN new() allocates and initializes a BIGNUM structure.
BN init() initializes an existing uninitialized BIGNUM. [...] →
BN new() + BN init() = bn new
you have to deal with many things on your own (e.g. I/O)
sometimes you have to take care of ”objects state” → you can do
it by introducing new wrappers and/or types
some APIs requires initialization
shell fuzzing stuff in one process is not the best idea
42. Future work
work in progress:
(((((((((((
framework for mutations
framework for coverage (a.k.a. smart fuzzing)
framework for differential fuzzing
see the BN sqrt case in OpenSSL
diff(OpenSSL bn, libgcrypt) -
https://github.com/hannob/bignum-fuzz
diff between versions
framework for snapshoting
it’s PoC → slow, messy, ineffective
we’re open to any suggestions
better documentation
blend or steal ideas from libfuzzer or other fuzzers
NetBSD rump kernel fuzzing
when to expect the 1.0 release?
43. Credits
Large parts of this presentation or project were done in cooperation
with:
n1x0n
s1m0n
PSi
Zeru´s
THANKS!