-
Notifications
You must be signed in to change notification settings - Fork 87
/
blind_code_coverage_fuzzer.txt
192 lines (152 loc) · 7.12 KB
/
blind_code_coverage_fuzzer.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
Blind Code Coverage Fuzzer (BCCF)
---------------------------------
This is a new addition to the Nightmare fuzzing suite. This tool tries
to do one of the following 3 tasks:
* Maximize code coverage of one sample.
* Create new generations that can be used as templates for fuzzing
covering more features that then original template from one file
or a directory with multiple files.
* Discover bugs by executing code not typically covered.
It can be considered an evolutionary fuzzer.
How it works
------------
This fuzzer takes as input some template file and tries to make changes
here and there, randomly, in order to increase code coverage. The target
application will run under instrumentation
in order to be able to determine the total number of different basic
blocks that were executed in each run.
Initially, before making mutations, it will meassure after a number of
executions (10 by default) that maximum, minimum and average number of
different basic blocks executed per each execution (as it may vary
depending on the target application) and then start making many random
modifications any where in the sample in order to maximize execution. By
default, a maximum number of 10 generations (i.e., modifications that
caused the application to execute more new basic blocks) will be saved
in memory.
A generic is dropped after a number of "failures". I mean, if a single
modification makes it execute more basic blocks but after a number of
added modifications the number of basic blocks is always the same or it
drops to a bad number, the generation will be eventually dropped. If, on
the other hand, a new change discover more new basic blocks, the current
generation and statistics will be stored (in memory) and the last new
generation will be considered the current generation.
While this approach is not bullet proof, it certainly can:
* Discover new bugs.
* Maximize code coverage of a sample or set of samples in order to use
them as new templates for another mutator (like radamsa).
Configuration
-------------
This is an experimental tool and, as such, there are so many little
configuration directives and options. However, using the supplied .cfg
file (bcf.cfg) starting with this fuzzer is easy.
For example, let's say that you want to fuzz the tool "readelf". We need
to configure in bcf.cfg:
* The BCF tool section.
* The DynamoRIO section.
* The specific target section.
For BCF and DynamoRIO the following options lines are all we need:
#-----------------------------------------------------------------------
# Configuration for the BCF fuzzer
#-----------------------------------------------------------------------
[BCF]
templates-path=/home/joxean/Documentos/research/nightmare/samples/av
# Current options are: DynamoRIO, Pin
bininst-tool=DynamoRIO
# Use *ONLY* iterative algorithm instead of all algorithms?
#iterative=1
# Use *ONLY* radamsa instead of all the implemented algorithms?
#radamsa=1
#-----------------------------------------------------------------------
# Configuration for DynamoRIO (requied for code coverage)
#-----------------------------------------------------------------------
[DynamoRIO]
path=/home/joxean/devel/dynamorio/DynamoRIO-Linux-4.2.0-3/
The options "iterative" and "radamsa" are completely uninteresting yet,
so let's forget them for now. After those lines we will need to add the
directive for the target application we want to fuzz, in our example
case, "readelf". The following lines are what we need:
#-----------------------------------------------------------------------
# Configuration for "readelf" tool
#-----------------------------------------------------------------------
[readelf]
# Command line to launch it
command=/usr/bin/readelf -a
# Base tube name
basetube=readelf
# The tube the fuzzer will use to pull of samples
tube=%(basetube)s-samples
# The tube the fuzzer will use to record crashes
crash-tube=%(basetube)s-crash
# Extension for the files to be fuzzed
extension=.fil
# Timeout for this fuzzer
timeout=15
# Environment
environment=common-environment
current-state-file=current-state-readelf
generation-bottom-level=-500
hide-output=1
skip-bytes=4
non-uniques=True
#iterative=1
#radamsa=1
[common-environment]
MALLOC_CHECK_=2
The options are either self-explanatory or already commented so I think
there is no need to go through all of them with the only exception of
the following ones:
* iterative: Use the iterative algorithm? The iterative algorithm will
make modifications byte-per-byte starting from the value "skip-bytes"
value.
* radamsa: Use only radamsa to make new generations?
Both configuration directives can be set at either BCF level (global) or
specific fuzzer level (the recommended setting).
Once we have the BCF tool, DynamoRIO section and our specific target
configuration section(s) already configured in our bcf.cfg file we need
to simply run the following command:
$ cd $NIGHTMARE_DIR/fuzzers
$ bcf.py 32 bcf.cfg readelf inputtemplate output_dir
The first argument given to bcf.py is the architecture: 32 for 32bits
architectures (Intel x86) and 64 for 64bits (x86_64).
As for the input template(s): this tool is can be considered "smart". We
can feed to it an (almost) empty file and it will discover the relevant
modifications required to execute more code and, hopefully, discover new
bugs. For example, given a 1024 bytes file filled with 0x00 characters
and only with the typical "\x1FELF" header, the tool will manage to
create files that will make to crash "readelf" or, at the very least,
that will execute more code (new different basic blocks) in the target
application.
Other binary instrumentation backends
-------------------------------------
The tool was originally developed with DynamoRIO but Pin was also added
as an experimental instrumentation backend. The example of Pin shows
how other backends can be added.
First of all, you should download Pin:
https://software.intel.com/en-us/articles/pin-a-dynamic-binary-instrumentation-tool
To use Pin for BCF you should grab the RunTracer pintool that is
included in the dependencies/ directory as a Git submodule. You can issue
the following command to grab it:
$ git submodule init
$ git submodule update
After downloading you should copy the RunTracer directory under your
%PINROOT%/source/tools/ and compile with the included Makefile. Pin
requires the following kernel option to be set:
# echo 0 > /proc/sys/kernel/yama/ptrace_scope
After everything works as expected you should configure your fuzzer to
work with Pin in bcf.cnf:
[BCF]
templates-path=/home/joxean/Documentos/research/nightmare/samples/av
# Current options are: DynamoRIO, Pin
bininst-tool=Pin
[Pin]
path=/home/b/tools/pin/
As you can see you should set the bininst-tool parameter in the BCF
section to 'Pin' (case sensitive, no quotes) and also have to define a new
section called 'Pin' and a 'path' parameter pointing to your Pin
installation.
Pin backend is handled by the CPinCoverage class declared in the
runtime/nfp_coverage.py file. It is registered with the name 'Pin' in
the BININST_AVAILABLE_TOOLS dictionary - this is not the best solution
but probably good enough for now.
---
Copyright (c) 2013, 2014 Joxean Koret