Update examples of the custom mutator
Merge `examples/python_mutators` into `examples/custom_mutators`
Remove `examples/python_mutators`
Update existing examples to demonstrate new APIs
RENAME
docs/custom_mutator.md
docs/custom_mutators.md
MODIFY
docs/env_variables.md
docs/env_variables.md
@@ -231,7 +231,7 @@ checks or alter some of the more exotic semantics of the tool:
performed with the custom mutator.
This feature allows to configure custom mutators which can be very helpful,
e.g. fuzzing XML or other highly flexible structured input.
- Please see [custom_mutator.md](custom_mutator.md).
+ Please see [custom_mutators.md](custom_mutators.md).
- AFL_FAST_CAL keeps the calibration stage about 2.5x faster (albeit less
precise), which can help when starting a session against a slow target.

MODIFY
examples/custom_mutators/README.md
examples/custom_mutators/README.md
-# A simple example for AFL_CUSTOM_MUTATOR_LIBRARY
+# Examples for the custom mutator
-This is a simple example for the AFL_CUSTOM_MUTATOR_LIBRARY feature.
-For more information see [docs/custom_mutator.md](../docs/custom_mutator.md)
+These are example and helper files for the custom mutator feature.
+See [docs/python_mutators.md](../docs/custom_mutators.md) for more information
+
+Note that if you compile with python3.7 you must use python3 scripts, and if
+you use pyton2.7 to compile python2 scripts!
+
+example.c - this is a simple example written in C and should be compiled to a
+ shared library
+
+example.py - this is the template you can use, the functions are there but they
+ are empty
+
+simple-chunk-replace.py - this is a simple example where chunks are replaced
+
+common.py - this can be used for common functions and helpers.
+ the examples do not use this though. But you can :)
+
+wrapper_afl_min.py - mutation of XML documents, loads XmlMutatorMin.py
+
+XmlMutatorMin.py - module for XML mutation

RENAME
examples/python_mutators/XmlMutatorMin.py
examples/custom_mutators/XmlMutatorMin.py
@@ -7,6 +7,7 @@ from copy import deepcopy
from lxml import etree as ET
import random, re, io
+
###########################
# The XmlMutatorMin class #
###########################
@@ -149,7 +150,7 @@ class XmlMutatorMin:
# We have the attribute to modify
# Get its value
- attrib_value = rand_elem.get(rand_attrib);
+ attrib_value = rand_elem.get(rand_attrib)
# print("- Value: " + attrib_value)
# Should we work on the whole value?

RENAME
examples/python_mutators/common.py
examples/custom_mutators/common.py
@@ -19,16 +19,19 @@ import random
import os
import re
+
def randel(l):
if not l:
return None
return l[random.randint(0, len(l)-1)]
+
def randel_pop(l):
if not l:
return None
return l.pop(random.randint(0, len(l)-1))
+
def write_exc_example(data, exc):
exc_name = re.sub(r'[^a-zA-Z0-9]', '_', repr(exc))

ADD
examples/custom_mutators/example.c
+/*
+ New Custom Mutator for AFL++
+ Written by Khaled Yakdan <yakdan@code-intelligence.de>
+ Andrea Fioraldi <andreafioraldi@gmail.com>
+ Shengtuo Hu <h1994st@gmail.com>
+*/
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const char *commands[] = {
+
+ "GET",
+ "PUT",
+ "DEL",
+
+};
+
+static size_t data_size = 100;
+
+void afl_custom_init(unsigned int seed) {
+
+ srand(seed);
+
+}
+
+/**
+ * Perform custom mutations on a given input
+ *
+ * (Optional for now. Required in the future)
+ *
+ * @param[in] buf Input data to be mutated
+ * @param[in] buf_size Size of input data
+ * @param[in] add_buf Buffer containing the additional test case
+ * @param[in] add_buf_size Size of the additional test case
+ * @param[out] mutated_out Buffer to store the mutated input
+ * @param[in] max_size Maximum size of the mutated output. The mutation must not
+ * produce data larger than max_size.
+ * @return Size of the mutated output.
+ */
+size_t afl_custom_fuzz(uint8_t *buf, size_t buf_size,
+ uint8_t *add_buf,size_t add_buf_size, // add_buf can be NULL
+ uint8_t *mutated_out, size_t max_size) {
+
+ // Make sure that the packet size does not exceed the maximum size expected by
+ // the fuzzer
+ size_t mutated_size = data_size <= max_size ? data_size : max_size;
+
+ // Randomly select a command string to add as a header to the packet
+ memcpy(mutated_out, commands[rand() % 3], 3);
+
+ // Mutate the payload of the packet
+ for (int i = 3; i < mutated_size; i++) {
+
+ mutated_out[i] = (data[i] + rand() % 10) & 0xff;
+
+ }
+
+ return mutated_size;
+
+}
+
+/**
+ * A post-processing function to use right before AFL writes the test case to
+ * disk in order to execute the target.
+ *
+ * (Optional) If this functionality is not needed, simply don't define this
+ * function.
+ *
+ * @param[in] buf Buffer containing the test case to be executed
+ * @param[in] buf_size Size of the test case
+ * @param[out] out_buf Pointer to the buffer containing the test case after
+ * processing. External library should allocate memory for out_buf. AFL++
+ * will release the memory after saving the test case.
+ * @return Size of the output buffer after processing
+ */
+size_t afl_custom_pre_save(uint8_t *buf, size_t buf_size, uint8_t **out_buf) {
+
+ size_t out_buf_size;
+
+ out_buf_size = buf_size;
+
+ // External mutator should allocate memory for `out_buf`
+ *out_buf = malloc(out_buf_size);
+ memcpy(*out_buf, buf, out_buf_size);
+
+ return out_buf_size;
+
+}
+
+uint8_t *trim_buf;
+size_t trim_buf_size
+int trimmming_steps;
+int cur_step;
+
+/**
+ * This method is called at the start of each trimming operation and receives
+ * the initial buffer. It should return the amount of iteration steps possible
+ * on this input (e.g. if your input has n elements and you want to remove
+ * them one by one, return n, if you do a binary search, return log(n),
+ * and so on...).
+ *
+ * If your trimming algorithm doesn't allow you to determine the amount of
+ * (remaining) steps easily (esp. while running), then you can alternatively
+ * return 1 here and always return 0 in post_trim until you are finished and
+ * no steps remain. In that case, returning 1 in post_trim will end the
+ * trimming routine. The whole current index/max iterations stuff is only used
+ * to show progress.
+ *
+ * (Optional)
+ *
+ * @param buf Buffer containing the test case
+ * @param buf_size Size of the test case
+ * @return The amount of possible iteration steps to trim the input
+ */
+int afl_custom_init_trim(uint8_t *buf, size_t buf_size) {
+
+ // We simply trim once
+ trimmming_steps = 1;
+
+ cur_step = 0;
+ trim_buf = buf;
+ trim_buf_size = buf_size;
+
+ return trimmming_steps;
+
+}
+
+/**
+ * This method is called for each trimming operation. It doesn't have any
+ * arguments because we already have the initial buffer from init_trim and we
+ * can memorize the current state in global variables. This can also save
+ * reparsing steps for each iteration. It should return the trimmed input
+ * buffer, where the returned data must not exceed the initial input data in
+ * length. Returning anything that is larger than the original data (passed
+ * to init_trim) will result in a fatal abort of AFLFuzz.
+ *
+ * (Optional)
+ *
+ * @param[out] out_buf Pointer to the buffer containing the trimmed test case.
+ * External library should allocate memory for out_buf. AFL++ will release
+ * the memory after saving the test case.
+ * @param[out] out_buf_size Pointer to the size of the trimmed test case
+ */
+void afl_custom_trim(uint8_t **out_buf, size_t* out_buf_size) {
+
+ *out_buf_size = trim_buf_size - 1;
+
+ // External mutator should allocate memory for `out_buf`
+ *out_buf = malloc(*out_buf_size);
+ // Remove the last byte of the trimming input
+ memcpy(*out_buf, trim_buf, *out_buf_size);
+
+}
+
+/**
+ * This method is called after each trim operation to inform you if your
+ * trimming step was successful or not (in terms of coverage). If you receive
+ * a failure here, you should reset your input to the last known good state.
+ *
+ * (Optional)
+ *
+ * @param success Indicates if the last trim operation was successful.
+ * @return The next trim iteration index (from 0 to the maximum amount of
+ * steps returned in init_trim)
+ */
+int afl_custom_post_trim(int success) {
+
+ if (success) {
+ ++cur_step;
+ return cur_step;
+ }
+
+ return trimmming_steps;
+
+}

RENAME
examples/python_mutators/example.py
examples/custom_mutators/example.py
@@ -16,6 +16,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
import random
+
def init(seed):
'''
Called once when AFLFuzz starts up. Used to seed our RNG.
@@ -24,9 +25,9 @@ def init(seed):
@param seed: A 32-bit random value
'''
random.seed(seed)
- return 0
-def fuzz(buf, add_buf):
+
+def fuzz(buf, add_buf, max_size):
'''
Called per fuzzing iteration.
@@ -36,6 +37,10 @@ def fuzz(buf, add_buf):
@type add_buf: bytearray
@param add_buf: A second buffer that can be used as mutation source.
+ @type max_size: int
+ @param max_size: Maximum size of the mutated output. The mutation must not
+ produce data larger than max_size.
+
@rtype: bytearray
@return: A new bytearray containing the mutated data
'''
@@ -101,3 +106,17 @@ def fuzz(buf, add_buf):
# # removed in the last step
#
# return next_index
+#
+# def pre_save(buf):
+# '''
+# Called just before the execution to write the test case in the format
+# expected by the target
+#
+# @type buf: bytearray
+# @param buf: The buffer containing the test case to be executed
+#
+# @rtype: bytearray
+# @return: The buffer containing the test case after
+# '''
+# return buf
+#

RENAME
examples/python_mutators/simple-chunk-replace.py
examples/custom_mutators/simple-chunk-replace.py
@@ -16,6 +16,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
import random
+
def init(seed):
'''
Called once when AFLFuzz starts up. Used to seed our RNG.
@@ -25,9 +26,9 @@ def init(seed):
'''
# Seed our RNG
random.seed(seed)
- return 0
-def fuzz(buf, add_buf):
+
+def fuzz(buf, add_buf, max_size):
'''
Called per fuzzing iteration.
@@ -37,6 +38,10 @@ def fuzz(buf, add_buf):
@type add_buf: bytearray
@param add_buf: A second buffer that can be used as mutation source.
+ @type max_size: int
+ @param max_size: Maximum size of the mutated output. The mutation must not
+ produce data larger than max_size.
+
@rtype: bytearray
@return: A new bytearray containing the mutated data
'''

DELETE
examples/custom_mutators/simple_mutator.c
-/*
- Simple Custom Mutator for AFL
-
- Written by Khaled Yakdan <yakdan@code-intelligence.de>
-
- This a simple mutator that assumes that the generates messages starting with
- one of the three strings GET, PUT, or DEL followed by a payload. The mutator
- randomly selects a commend and mutates the payload of the seed provided as
- input.
-*/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-static const char *commands[] = {
-
- "GET",
- "PUT",
- "DEL",
-
-};
-
-static size_t data_size = 100;
-
-size_t afl_custom_mutator(uint8_t *data, size_t size, uint8_t *mutated_out,
- size_t max_size, unsigned int seed) {
-
- // Seed the PRNG
- srand(seed);
-
- // Make sure that the packet size does not exceed the maximum size expected by
- // the fuzzer
- size_t mutated_size = data_size <= max_size ? data_size : max_size;
-
- // Randomly select a command string to add as a header to the packet
- memcpy(mutated_out, commands[rand() % 3], 3);
-
- // Mutate the payload of the packet
- for (int i = 3; i < mutated_size; i++) {
-
- mutated_out[i] = (data[i] + rand() % 10) & 0xff;
-
- }
-
- return mutated_size;
-
-}
-

RENAME
examples/python_mutators/wrapper_afl_min.py
examples/custom_mutators/wrapper_afl_min.py
@@ -9,8 +9,8 @@ __seed__ = "RANDOM"
__log__ = False
__log_file__ = "wrapper.log"
-# AFL functions
+# AFL functions
def log(text):
"""
Logger
@@ -24,6 +24,7 @@ def log(text):
with open(__log_file__, "a") as logf:
logf.write("[%s] %s\n" % (__seed__, text))
+
def init(seed):
"""
Called once when AFL starts up. Seed is used to identify the AFL instance in log files
@@ -42,7 +43,8 @@ def init(seed):
except RuntimeError as e:
log("init(): Can't create mutator: %s" % e.message)
-def fuzz(buf, add_buf):
+
+def fuzz(buf, add_buf, max_size):
"""
Called for each fuzzing iteration.
"""
@@ -62,7 +64,7 @@ def fuzz(buf, add_buf):
try:
buf_str = str(buf)
log("fuzz(): AFL buffer converted to a string")
- except:
+ except Exception:
via_buffer = False
log("fuzz(): Can't convert AFL buffer to a string")
@@ -71,7 +73,7 @@ def fuzz(buf, add_buf):
try:
__mutator__.init_from_string(buf_str)
log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str))
- except:
+ except Exception:
via_buffer = False
log("fuzz(): Can't initialize mutator with AFL buffer")
@@ -84,7 +86,7 @@ def fuzz(buf, add_buf):
try:
__mutator__.mutate(max=5)
log("fuzz(): Input mutated")
- except:
+ except Exception:
log("fuzz(): Can't mutate input => returning buf")
return buf
@@ -92,7 +94,7 @@ def fuzz(buf, add_buf):
try:
data = bytearray(__mutator__.save_to_string())
log("fuzz(): Mutated data converted as bytes")
- except:
+ except Exception:
log("fuzz(): Can't convert mutated data to bytes => returning buf")
return buf
@@ -100,8 +102,8 @@ def fuzz(buf, add_buf):
log("fuzz(): Returning %d bytes" % len(data))
return data
-# Main (for debug)
+# Main (for debug)
if __name__ == '__main__':
__log__ = True
@@ -114,4 +116,3 @@ if __name__ == '__main__':
in_2 = bytearray("<abc abc123='456' abcCBA='ppppppppppppppppppppppppppppp'/>")
out = fuzz(in_1, in_2)
print(out)
-

DELETE
examples/python_mutators/README
-These are example and helper files for the AFL_PYTHON_MODULE feature.
-See [docs/python_mutators.md](../docs/python_mutators.md) for more information
-
-Note that if you compile with python3.7 you must use python3 scripts, and if
-you use pyton2.7 to compile python2 scripts!
-
-
-example.py - this is the template you can use, the functions are there
- but they are empty
-
-simple-chunk-replace.py - this is a simple example where chunks are replaced
-
-common.py - this can be used for common functions and helpers.
- the examples do not use this though. But you can :)
-
-wrapper_afl_min.py - mutation of XML documents, loads XmlMutatorMin.py
-
-XmlMutatorMin.py - module for XML mutation

Update examples of the custom mutator

- Merge `examples/python_mutators` into `examples/custom_mutators`
- Remove `examples/python_mutators`
- Update existing examples to demonstrate new APIs