diff options
-rw-r--r-- | docs/README | 3 | ||||
-rw-r--r-- | docs/python_mutators.txt | 5 | ||||
-rw-r--r-- | python_mutators/README | 11 | ||||
-rw-r--r-- | python_mutators/common.py | 37 | ||||
-rw-r--r-- | python_mutators/example.py | 103 | ||||
-rw-r--r-- | python_mutators/simple-chunk-replace.py | 59 |
6 files changed, 218 insertions, 0 deletions
diff --git a/docs/README b/docs/README index 9c81a788..cc42d767 100644 --- a/docs/README +++ b/docs/README @@ -21,6 +21,9 @@ american fuzzy lop plus plus https://github.com/andreafioraldi/afl and got the community patches applied to it. + C. Hoellers Python mutator module support was added too + (https://github.com/choeller/afl) + So all in all this is the best-of AFL that is currently out there :-) diff --git a/docs/python_mutators.txt b/docs/python_mutators.txt index 04fd7208..0d4eef1e 100644 --- a/docs/python_mutators.txt +++ b/docs/python_mutators.txt @@ -12,6 +12,8 @@ Adding custom mutators to AFL using Python modules NOTE: This is for Python 2.7 ! Anyone who wants to add Python 3.7 support is happily welcome :) + For an example and a template see ../python_mutators/ + 1) Description and purpose -------------------------- @@ -50,6 +52,7 @@ is requested. There is also optional support for a trimming API, see the section below for further information about this feature. + 3) How to compile AFLFuzz with Python support --------------------------------------------- @@ -91,6 +94,7 @@ AFL_DEBUG - When combined with AFL_NO_UI, this causes the C trimming code to emit additional messages about the performance and actions of your custom Python trimmer. Use this to see if it works :) + 5) Order and statistics ----------------------- @@ -99,6 +103,7 @@ the havoc stage). In the statistics however, it shows up as the third number under "havoc". That's because I'm lazy and I didn't want to mess with the UI too much ;) + 6) Trimming support ------------------- diff --git a/python_mutators/README b/python_mutators/README new file mode 100644 index 00000000..174c8a64 --- /dev/null +++ b/python_mutators/README @@ -0,0 +1,11 @@ +These are example and helper files for the AFL_PYTHON_MODULE feature. +See docs/python_mutators.txt for more information + + +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 :) diff --git a/python_mutators/common.py b/python_mutators/common.py new file mode 100644 index 00000000..28b8ee80 --- /dev/null +++ b/python_mutators/common.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Module containing functions shared between multiple AFL modules + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +from __future__ import print_function +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)) + + if not os.path.exists(exc_name): + with open(exc_name, 'w') as f: + f.write(data) diff --git a/python_mutators/example.py b/python_mutators/example.py new file mode 100644 index 00000000..d32a7eb2 --- /dev/null +++ b/python_mutators/example.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Example Python Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + random.seed(seed) + return 0 + +def fuzz(buf, add_buf): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + ret = bytearray(buf) + # Do something interesting with ret + + return ret + +# Uncomment and implement the following methods if you want to use a custom +# trimming algorithm. See also the documentation for a better API description. + +# def init_trim(buf): +# ''' +# Called per trimming iteration. +# +# @type buf: bytearray +# @param buf: The buffer that should be trimmed. +# +# @rtype: int +# @return: The maximum number of trimming steps. +# ''' +# global ... +# +# # Initialize global variables +# +# # Figure out how many trimming steps are possible. +# # If this is not possible for your trimming, you can +# # return 1 instead and always return 0 in post_trim +# # until you are done (then you return 1). +# +# return steps +# +# def trim(): +# ''' +# Called per trimming iteration. +# +# @rtype: bytearray +# @return: A new bytearray containing the trimmed data. +# ''' +# global ... +# +# # Implement the actual trimming here +# +# return bytearray(...) +# +# def post_trim(success): +# ''' +# Called after each trimming operation. +# +# @type success: bool +# @param success: Indicates if the last trim operation was successful. +# +# @rtype: int +# @return: The next trim index (0 to max number of steps) where max +# number of steps indicates the trimming is done. +# ''' +# global ... +# +# if not success: +# # Restore last known successful input, determine next index +# else: +# # Just determine the next index, based on what was successfully +# # removed in the last step +# +# return next_index diff --git a/python_mutators/simple-chunk-replace.py b/python_mutators/simple-chunk-replace.py new file mode 100644 index 00000000..218dd4f8 --- /dev/null +++ b/python_mutators/simple-chunk-replace.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# encoding: utf-8 +''' +Simple Chunk Cross-Over Replacement Module for AFLFuzz + +@author: Christian Holler (:decoder) + +@license: + +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +@contact: choller@mozilla.com +''' + +import random + +def init(seed): + ''' + Called once when AFLFuzz starts up. Used to seed our RNG. + + @type seed: int + @param seed: A 32-bit random value + ''' + # Seed our RNG + random.seed(seed) + return 0 + +def fuzz(buf, add_buf): + ''' + Called per fuzzing iteration. + + @type buf: bytearray + @param buf: The buffer that should be mutated. + + @type add_buf: bytearray + @param add_buf: A second buffer that can be used as mutation source. + + @rtype: bytearray + @return: A new bytearray containing the mutated data + ''' + # Make a copy of our input buffer for returning + ret = bytearray(buf) + + # Take a random fragment length between 2 and 32 (or less if add_buf is shorter) + fragment_len = random.randint(1, min(len(add_buf), 32)) + + # Determine a random source index where to take the data chunk from + rand_src_idx = random.randint(0, len(add_buf) - fragment_len) + + # Determine a random destination index where to put the data chunk + rand_dst_idx = random.randint(0, len(buf)) + + # Make the chunk replacement + ret[rand_dst_idx:rand_dst_idx + fragment_len] = add_buf[rand_src_idx:rand_src_idx + fragment_len] + + # Return data + return ret |