• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Custom Mutators in AFL++
2
3This file describes how you can implement custom mutations to be used in AFL.
4For now, we support C/C++ library and Python module, collectively named as the
5custom mutator.
6
7There is also experimental support for Rust in `custom_mutators/rust`. For
8documentation, refer to that directory. Run `cargo doc -p custom_mutator --open`
9in that directory to view the documentation in your web browser.
10
11Implemented by
12- C/C++ library (`*.so`): Khaled Yakdan from Code Intelligence
13  (<yakdan@code-intelligence.de>)
14- Python module: Christian Holler from Mozilla (<choller@mozilla.com>)
15
16## 1) Introduction
17
18Custom mutators can be passed to `afl-fuzz` to perform custom mutations on test
19cases beyond those available in AFL. For example, to enable structure-aware
20fuzzing by using libraries that perform mutations according to a given grammar.
21
22The custom mutator is passed to `afl-fuzz` via the `AFL_CUSTOM_MUTATOR_LIBRARY`
23or `AFL_PYTHON_MODULE` environment variable, and must export a fuzz function.
24Now AFL++ also supports multiple custom mutators which can be specified in the
25same `AFL_CUSTOM_MUTATOR_LIBRARY` environment variable like this.
26
27```bash
28export AFL_CUSTOM_MUTATOR_LIBRARY="full/path/to/mutator_first.so;full/path/to/mutator_second.so"
29```
30
31For details, see [APIs](#2-apis) and [Usage](#3-usage).
32
33The custom mutation stage is set to be the first non-deterministic stage (right
34before the havoc stage).
35
36Note: If `AFL_CUSTOM_MUTATOR_ONLY` is set, all mutations will solely be
37performed with the custom mutator.
38
39## 2) APIs
40
41C/C++:
42
43```c
44void *afl_custom_init(afl_state_t *afl, unsigned int seed);
45unsigned int afl_custom_fuzz_count(void *data, const unsigned char *buf, size_t buf_size);
46size_t afl_custom_fuzz(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, unsigned char *add_buf, size_t add_buf_size, size_t max_size);
47const char *afl_custom_describe(void *data, size_t max_description_len);
48size_t afl_custom_post_process(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf);
49int afl_custom_init_trim(void *data, unsigned char *buf, size_t buf_size);
50size_t afl_custom_trim(void *data, unsigned char **out_buf);
51int afl_custom_post_trim(void *data, unsigned char success);
52size_t afl_custom_havoc_mutation(void *data, unsigned char *buf, size_t buf_size, unsigned char **out_buf, size_t max_size);
53unsigned char afl_custom_havoc_mutation_probability(void *data);
54unsigned char afl_custom_queue_get(void *data, const unsigned char *filename);
55u8 afl_custom_queue_new_entry(void *data, const unsigned char *filename_new_queue, const unsigned int *filename_orig_queue);
56const char* afl_custom_introspection(my_mutator_t *data);
57void afl_custom_deinit(void *data);
58```
59
60Python:
61
62```python
63def init(seed):
64    pass
65
66def fuzz_count(buf, add_buf, max_size):
67    return cnt
68
69def fuzz(buf, add_buf, max_size):
70    return mutated_out
71
72def describe(max_description_length):
73    return "description_of_current_mutation"
74
75def post_process(buf):
76    return out_buf
77
78def init_trim(buf):
79    return cnt
80
81def trim():
82    return out_buf
83
84def post_trim(success):
85    return next_index
86
87def havoc_mutation(buf, max_size):
88    return mutated_out
89
90def havoc_mutation_probability():
91    return probability # int in [0, 100]
92
93def queue_get(filename):
94    return True
95
96def queue_new_entry(filename_new_queue, filename_orig_queue):
97    return False
98
99def introspection():
100    return string
101
102def deinit():  # optional for Python
103    pass
104```
105
106### Custom Mutation
107
108- `init`:
109
110    This method is called when AFL++ starts up and is used to seed RNG and set
111    up buffers and state.
112
113- `queue_get` (optional):
114
115    This method determines whether the custom fuzzer should fuzz the current
116    queue entry or not
117
118- `fuzz_count` (optional):
119
120    When a queue entry is selected to be fuzzed, afl-fuzz selects the number of
121    fuzzing attempts with this input based on a few factors. If, however, the
122    custom mutator wants to set this number instead on how often it is called
123    for a specific queue entry, use this function. This function is most useful
124    if `AFL_CUSTOM_MUTATOR_ONLY` is **not** used.
125
126- `fuzz` (optional):
127
128    This method performs custom mutations on a given input. It also accepts an
129    additional test case. Note that this function is optional - but it makes
130    sense to use it. You would only skip this if `post_process` is used to fix
131    checksums etc. so if you are using it, e.g., as a post processing library.
132    Note that a length > 0 *must* be returned!
133
134- `describe` (optional):
135
136    When this function is called, it shall describe the current test case,
137    generated by the last mutation. This will be called, for example, to name
138    the written test case file after a crash occurred. Using it can help to
139    reproduce crashing mutations.
140
141- `havoc_mutation` and `havoc_mutation_probability` (optional):
142
143    `havoc_mutation` performs a single custom mutation on a given input. This
144    mutation is stacked with other mutations in havoc. The other method,
145    `havoc_mutation_probability`, returns the probability that `havoc_mutation`
146    is called in havoc. By default, it is 6%.
147
148- `post_process` (optional):
149
150    For some cases, the format of the mutated data returned from the custom
151    mutator is not suitable to directly execute the target with this input. For
152    example, when using libprotobuf-mutator, the data returned is in a protobuf
153    format which corresponds to a given grammar. In order to execute the target,
154    the protobuf data must be converted to the plain-text format expected by the
155    target. In such scenarios, the user can define the `post_process` function.
156    This function is then transforming the data into the format expected by the
157    API before executing the target.
158
159    This can return any python object that implements the buffer protocol and
160    supports PyBUF_SIMPLE. These include bytes, bytearray, etc.
161
162- `queue_new_entry` (optional):
163
164    This methods is called after adding a new test case to the queue. If the
165    contents of the file was changed, return True, False otherwise.
166
167- `introspection` (optional):
168
169    This method is called after a new queue entry, crash or timeout is
170    discovered if compiled with INTROSPECTION. The custom mutator can then
171    return a string (const char *) that reports the exact mutations used.
172
173- `deinit`:
174
175    The last method to be called, deinitializing the state.
176
177Note that there are also three functions for trimming as described in the next
178section.
179
180### Trimming Support
181
182The generic trimming routines implemented in AFL++ can easily destroy the
183structure of complex formats, possibly leading to a point where you have a lot
184of test cases in the queue that your Python module cannot process anymore but
185your target application still accepts. This is especially the case when your
186target can process a part of the input (causing coverage) and then errors out on
187the remaining input.
188
189In such cases, it makes sense to implement a custom trimming routine. The API
190consists of multiple methods because after each trimming step, we have to go
191back into the C code to check if the coverage bitmap is still the same for the
192trimmed input. Here's a quick API description:
193
194- `init_trim` (optional):
195
196    This method is called at the start of each trimming operation and receives
197    the initial buffer. It should return the amount of iteration steps possible
198    on this input (e.g., if your input has n elements and you want to remove
199    them one by one, return n, if you do a binary search, return log(n), and so
200    on).
201
202    If your trimming algorithm doesn't allow to determine the amount of
203    (remaining) steps easily (esp. while running), then you can alternatively
204    return 1 here and always return 0 in `post_trim` until you are finished and
205    no steps remain. In that case, returning 1 in `post_trim` will end the
206    trimming routine. The whole current index/max iterations stuff is only used
207    to show progress.
208
209- `trim` (optional)
210
211    This method is called for each trimming operation. It doesn't have any
212    arguments because there is already the initial buffer from `init_trim` and
213    we can memorize the current state in the data variables. This can also save
214    reparsing steps for each iteration. It should return the trimmed input
215    buffer.
216
217- `post_trim` (optional)
218
219    This method is called after each trim operation to inform you if your
220    trimming step was successful or not (in terms of coverage). If you receive a
221    failure here, you should reset your input to the last known good state. In
222    any case, this method must return the next trim iteration index (from 0 to
223    the maximum amount of steps you returned in `init_trim`).
224
225Omitting any of three trimming methods will cause the trimming to be disabled
226and trigger a fallback to the built-in default trimming routine.
227
228### Environment Variables
229
230Optionally, the following environment variables are supported:
231
232- `AFL_CUSTOM_MUTATOR_ONLY`
233
234    Disable all other mutation stages. This can prevent broken test cases (those
235    that your Python module can't work with anymore) to fill up your queue. Best
236    combined with a custom trimming routine (see below) because trimming can
237    cause the same test breakage like havoc and splice.
238
239- `AFL_PYTHON_ONLY`
240
241    Deprecated and removed, use `AFL_CUSTOM_MUTATOR_ONLY` instead.
242
243- `AFL_DEBUG`
244
245    When combined with `AFL_NO_UI`, this causes the C trimming code to emit
246    additional messages about the performance and actions of your custom
247    trimmer. Use this to see if it works :)
248
249## 3) Usage
250
251### Prerequisite
252
253For Python mutators, the python 3 or 2 development package is required. On
254Debian/Ubuntu/Kali it can be installed like this:
255
256```bash
257sudo apt install python3-dev
258# or
259sudo apt install python-dev
260```
261
262Then, AFL++ can be compiled with Python support. The AFL++ Makefile detects
263Python 2 and 3 through `python-config` if it is in the PATH and compiles
264`afl-fuzz` with the feature if available.
265
266Note: for some distributions, you might also need the package `python[23]-apt`.
267In case your setup is different, set the necessary variables like this:
268`PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make`.
269
270### Custom Mutator Preparation
271
272For C/C++ mutators, the source code must be compiled as a shared object:
273
274```bash
275gcc -shared -Wall -O3 example.c -o example.so
276```
277
278Note that if you specify multiple custom mutators, the corresponding functions
279will be called in the order in which they are specified. E.g., the first
280`post_process` function of `example_first.so` will be called and then that of
281`example_second.so`.
282
283### Run
284
285C/C++
286
287```bash
288export AFL_CUSTOM_MUTATOR_LIBRARY="/full/path/to/example_first.so;/full/path/to/example_second.so"
289afl-fuzz /path/to/program
290```
291
292Python
293
294```bash
295export PYTHONPATH=`dirname /full/path/to/example.py`
296export AFL_PYTHON_MODULE=example
297afl-fuzz /path/to/program
298```
299
300## 4) Example
301
302See [example.c](../custom_mutators/examples/example.c) and
303[example.py](../custom_mutators/examples/example.py).
304
305## 5) Other Resources
306
307- AFL libprotobuf mutator
308    - [bruce30262/libprotobuf-mutator_fuzzing_learning](https://github.com/bruce30262/libprotobuf-mutator_fuzzing_learning/tree/master/4_libprotobuf_aflpp_custom_mutator)
309    - [thebabush/afl-libprotobuf-mutator](https://github.com/thebabush/afl-libprotobuf-mutator)
310- [XML Fuzzing@NullCon 2017](https://www.agarri.fr/docs/XML_Fuzzing-NullCon2017-PUBLIC.pdf)
311    - [A bug detected by AFL + XML-aware mutators](https://bugs.chromium.org/p/chromium/issues/detail?id=930663)