aboutsummaryrefslogtreecommitdiff
path: root/python_mutators
diff options
context:
space:
mode:
authorAndrea Fioraldi <andreafioraldi@gmail.com>2020-02-03 13:11:10 +0100
committerAndrea Fioraldi <andreafioraldi@gmail.com>2020-02-03 13:11:10 +0100
commit2fe7889912c9bb340f302a037585b7b1836ac94f (patch)
tree5c3e4e5829f45dce46794ebc2681732738d689fe /python_mutators
parente2eedefc65bec1a04605f117a11ca8bdf9d80323 (diff)
downloadafl++-2fe7889912c9bb340f302a037585b7b1836ac94f.tar.gz
move custom and pythoon mutators examples into examples/
Diffstat (limited to 'python_mutators')
-rw-r--r--python_mutators/README18
-rw-r--r--python_mutators/XmlMutatorMin.py331
-rw-r--r--python_mutators/common.py37
-rw-r--r--python_mutators/example.py103
-rw-r--r--python_mutators/simple-chunk-replace.py59
-rw-r--r--python_mutators/wrapper_afl_min.py117
6 files changed, 0 insertions, 665 deletions
diff --git a/python_mutators/README b/python_mutators/README
deleted file mode 100644
index 4e7d62bc..00000000
--- a/python_mutators/README
+++ /dev/null
@@ -1,18 +0,0 @@
-These are example and helper files for the AFL_PYTHON_MODULE feature.
-See docs/python_mutators.txt 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
diff --git a/python_mutators/XmlMutatorMin.py b/python_mutators/XmlMutatorMin.py
deleted file mode 100644
index 058b7e61..00000000
--- a/python_mutators/XmlMutatorMin.py
+++ /dev/null
@@ -1,331 +0,0 @@
-#!/usr/bin/python
-
-""" Mutation of XML documents, should be called from one of its wrappers (CLI, AFL, ...) """
-
-from __future__ import print_function
-from copy import deepcopy
-from lxml import etree as ET
-import random, re, io
-
-###########################
-# The XmlMutatorMin class #
-###########################
-
-class XmlMutatorMin:
-
- """
- Optionals parameters:
- seed Seed used by the PRNG (default: "RANDOM")
- verbose Verbosity (default: False)
- """
-
- def __init__(self, seed="RANDOM", verbose=False):
-
- """ Initialize seed, database and mutators """
-
- # Verbosity
- self.verbose = verbose
-
- # Initialize PRNG
- self.seed = str(seed)
- if self.seed == "RANDOM":
- random.seed()
- else:
- if self.verbose:
- print("Static seed '%s'" % self.seed)
- random.seed(self.seed)
-
- # Initialize input and output documents
- self.input_tree = None
- self.tree = None
-
- # High-level mutators (no database needed)
- hl_mutators_delete = [ "del_node_and_children", "del_node_but_children", "del_attribute", "del_content" ] # Delete items
- hl_mutators_fuzz = ["fuzz_attribute"] # Randomly change attribute values
-
- # Exposed mutators
- self.hl_mutators_all = hl_mutators_fuzz + hl_mutators_delete
-
- def __parse_xml (self, xml):
-
- """ Parse an XML string. Basic wrapper around lxml.parse() """
-
- try:
- # Function parse() takes care of comments / DTD / processing instructions / ...
- tree = ET.parse(io.BytesIO(xml))
- except ET.ParseError:
- raise RuntimeError("XML isn't well-formed!")
- except LookupError as e:
- raise RuntimeError(e)
-
- # Return a document wrapper
- return tree
-
- def __exec_among (self, module, functions, min_times, max_times):
-
- """ Randomly execute $functions between $min and $max times """
-
- for i in xrange (random.randint (min_times, max_times)):
- # Function names are mangled because they are "private"
- getattr (module, "_XmlMutatorMin__" + random.choice(functions)) ()
-
- def __serialize_xml (self, tree):
-
- """ Serialize a XML document. Basic wrapper around lxml.tostring() """
-
- return ET.tostring(tree, with_tail=False, xml_declaration=True, encoding=tree.docinfo.encoding)
-
- def __ver (self, version):
-
- """ Helper for displaying lxml version numbers """
-
- return ".".join(map(str, version))
-
- def reset (self):
-
- """ Reset the mutator """
-
- self.tree = deepcopy(self.input_tree)
-
- def init_from_string (self, input_string):
-
- """ Initialize the mutator from a XML string """
-
- # Get a pointer to the top-element
- self.input_tree = self.__parse_xml(input_string)
-
- # Get a working copy
- self.tree = deepcopy(self.input_tree)
-
- def save_to_string (self):
-
- """ Return the current XML document as UTF-8 string """
-
- # Return a text version of the tree
- return self.__serialize_xml(self.tree)
-
- def __pick_element (self, exclude_root_node = False):
-
- """ Pick a random element from the current document """
-
- # Get a list of all elements, but nodes like PI and comments
- elems = list(self.tree.getroot().iter(tag=ET.Element))
-
- # Is the root node excluded?
- if exclude_root_node:
- start = 1
- else:
- start = 0
-
- # Pick a random element
- try:
- elem_id = random.randint (start, len(elems) - 1)
- elem = elems[elem_id]
- except ValueError:
- # Should only occurs if "exclude_root_node = True"
- return (None, None)
-
- return (elem_id, elem)
-
- def __fuzz_attribute (self):
-
- """ Fuzz (part of) an attribute value """
-
- # Select a node to modify
- (rand_elem_id, rand_elem) = self.__pick_element()
-
- # Get all the attributes
- attribs = rand_elem.keys()
-
- # Is there attributes?
- if len(attribs) < 1:
- if self.verbose:
- print("No attribute: can't replace!")
- return
-
- # Pick a random attribute
- rand_attrib_id = random.randint (0, len(attribs) - 1)
- rand_attrib = attribs[rand_attrib_id]
-
- # We have the attribute to modify
- # Get its value
- attrib_value = rand_elem.get(rand_attrib);
- # print("- Value: " + attrib_value)
-
- # Should we work on the whole value?
- func_call = "(?P<func>[a-zA-Z:\-]+)\((?P<args>.*?)\)"
- p = re.compile(func_call)
- l = p.findall(attrib_value)
- if random.choice((True,False)) and l:
- # Randomly pick one the function calls
- (func, args) = random.choice(l)
- # Split by "," and randomly pick one of the arguments
- value = random.choice(args.split(','))
- # Remove superfluous characters
- unclean_value = value
- value = value.strip(" ").strip("'")
- # print("Selected argument: [%s]" % value)
- else:
- value = attrib_value
-
- # For each type, define some possible replacement values
- choices_number = ( \
- "0", \
- "11111", \
- "-128", \
- "2", \
- "-1", \
- "1/3", \
- "42/0", \
- "1094861636 idiv 1.0", \
- "-1123329771506872 idiv 3.8", \
- "17=$numericRTF", \
- str(3 + random.randrange(0, 100)), \
- )
-
- choices_letter = ( \
- "P" * (25 * random.randrange(1, 100)), \
- "%s%s%s%s%s%s", \
- "foobar", \
- )
-
- choices_alnum = ( \
- "Abc123", \
- "020F0302020204030204", \
- "020F0302020204030204" * (random.randrange(5, 20)), \
- )
-
- # Fuzz the value
- if random.choice((True,False)) and value == "":
-
- # Empty
- new_value = value
-
- elif random.choice((True,False)) and value.isdigit():
-
- # Numbers
- new_value = random.choice(choices_number)
-
- elif random.choice((True,False)) and value.isalpha():
-
- # Letters
- new_value = random.choice(choices_letter)
-
- elif random.choice((True,False)) and value.isalnum():
-
- # Alphanumeric
- new_value = random.choice(choices_alnum)
-
- else:
-
- # Default type
- new_value = random.choice(choices_alnum + choices_letter + choices_number)
-
- # If we worked on a substring, apply changes to the whole string
- if value != attrib_value:
- # No ' around empty values
- if new_value != "" and value != "":
- new_value = "'" + new_value + "'"
- # Apply changes
- new_value = attrib_value.replace(unclean_value, new_value)
-
- # Log something
- if self.verbose:
- print("Fuzzing attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
-
- # Modify the attribute
- rand_elem.set(rand_attrib, new_value.decode("utf-8"))
-
- def __del_node_and_children (self):
-
- """ High-level minimizing mutator
- Delete a random node and its children (i.e. delete a random tree) """
-
- self.__del_node(True)
-
- def __del_node_but_children (self):
-
- """ High-level minimizing mutator
- Delete a random node but its children (i.e. link them to the parent of the deleted node) """
-
- self.__del_node(False)
-
- def __del_node (self, delete_children):
-
- """ Called by the __del_node_* mutators """
-
- # Select a node to modify (but the root one)
- (rand_elem_id, rand_elem) = self.__pick_element (exclude_root_node = True)
-
- # If the document includes only a top-level element
- # Then we can't pick a element (given that "exclude_root_node = True")
-
- # Is the document deep enough?
- if rand_elem is None:
- if self.verbose:
- print("Can't delete a node: document not deep enough!")
- return
-
- # Log something
- if self.verbose:
- but_or_and = "and" if delete_children else "but"
- print("Deleting tag #%i '%s' %s its children" % (rand_elem_id, rand_elem.tag, but_or_and))
-
- if delete_children is False:
- # Link children of the random (soon to be deleted) node to its parent
- for child in rand_elem:
- rand_elem.getparent().append(child)
-
- # Remove the node
- rand_elem.getparent().remove(rand_elem)
-
- def __del_content (self):
-
- """ High-level minimizing mutator
- Delete the attributes and children of a random node """
-
- # Select a node to modify
- (rand_elem_id, rand_elem) = self.__pick_element()
-
- # Log something
- if self.verbose:
- print("Reseting tag #%i '%s'" % (rand_elem_id, rand_elem.tag))
-
- # Reset the node
- rand_elem.clear()
-
- def __del_attribute (self):
-
- """ High-level minimizing mutator
- Delete a random attribute from a random node """
-
- # Select a node to modify
- (rand_elem_id, rand_elem) = self.__pick_element()
-
- # Get all the attributes
- attribs = rand_elem.keys()
-
- # Is there attributes?
- if len(attribs) < 1:
- if self.verbose:
- print("No attribute: can't delete!")
- return
-
- # Pick a random attribute
- rand_attrib_id = random.randint (0, len(attribs) - 1)
- rand_attrib = attribs[rand_attrib_id]
-
- # Log something
- if self.verbose:
- print("Deleting attribute #%i '%s' of tag #%i '%s'" % (rand_attrib_id, rand_attrib, rand_elem_id, rand_elem.tag))
-
- # Delete the attribute
- rand_elem.attrib.pop(rand_attrib)
-
- def mutate (self, min=1, max=5):
-
- """ Execute some high-level mutators between $min and $max times, then some medium-level ones """
-
- # High-level mutation
- self.__exec_among(self, self.hl_mutators_all, min, max)
-
diff --git a/python_mutators/common.py b/python_mutators/common.py
deleted file mode 100644
index 28b8ee80..00000000
--- a/python_mutators/common.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/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
deleted file mode 100644
index d32a7eb2..00000000
--- a/python_mutators/example.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/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
deleted file mode 100644
index 218dd4f8..00000000
--- a/python_mutators/simple-chunk-replace.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/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
diff --git a/python_mutators/wrapper_afl_min.py b/python_mutators/wrapper_afl_min.py
deleted file mode 100644
index df09b40a..00000000
--- a/python_mutators/wrapper_afl_min.py
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/usr/bin/env python
-
-from XmlMutatorMin import XmlMutatorMin
-
-# Default settings (production mode)
-
-__mutator__ = None
-__seed__ = "RANDOM"
-__log__ = False
-__log_file__ = "wrapper.log"
-
-# AFL functions
-
-def log(text):
- """
- Logger
- """
-
- global __seed__
- global __log__
- global __log_file__
-
- if __log__:
- 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
- """
-
- global __mutator__
- global __seed__
-
- # Get the seed
- __seed__ = seed
-
- # Create a global mutation class
- try:
- __mutator__ = XmlMutatorMin(__seed__, verbose=__log__)
- log("init(): Mutator created")
- except RuntimeError as e:
- log("init(): Can't create mutator: %s" % e.message)
-
-def fuzz(buf, add_buf):
- """
- Called for each fuzzing iteration.
- """
-
- global __mutator__
-
- # Do we have a working mutator object?
- if __mutator__ is None:
- log("fuzz(): Can't fuzz, no mutator available")
- return buf
-
- # Try to use the AFL buffer
- via_buffer = True
-
- # Interpret the AFL buffer (an array of bytes) as a string
- if via_buffer:
- try:
- buf_str = str(buf)
- log("fuzz(): AFL buffer converted to a string")
- except:
- via_buffer = False
- log("fuzz(): Can't convert AFL buffer to a string")
-
- # Load XML from the AFL string
- if via_buffer:
- try:
- __mutator__.init_from_string(buf_str)
- log("fuzz(): Mutator successfully initialized with AFL buffer (%d bytes)" % len(buf_str))
- except:
- via_buffer = False
- log("fuzz(): Can't initialize mutator with AFL buffer")
-
- # If init from AFL buffer wasn't succesful
- if not via_buffer:
- log("fuzz(): Returning unmodified AFL buffer")
- return buf
-
- # Sucessful initialization -> mutate
- try:
- __mutator__.mutate(max=5)
- log("fuzz(): Input mutated")
- except:
- log("fuzz(): Can't mutate input => returning buf")
- return buf
-
- # Convert mutated data to a array of bytes
- try:
- data = bytearray(__mutator__.save_to_string())
- log("fuzz(): Mutated data converted as bytes")
- except:
- log("fuzz(): Can't convert mutated data to bytes => returning buf")
- return buf
-
- # Everything went fine, returning mutated content
- log("fuzz(): Returning %d bytes" % len(data))
- return data
-
-# Main (for debug)
-
-if __name__ == '__main__':
-
- __log__ = True
- __log_file__ = "/dev/stdout"
- __seed__ = "RANDOM"
-
- init(__seed__)
-
- in_1 = bytearray("<foo ddd='eeee'>ffff<a b='c' d='456' eee='ffffff'>zzzzzzzzzzzz</a><b yyy='YYY' zzz='ZZZ'></b></foo>")
- in_2 = bytearray("<abc abc123='456' abcCBA='ppppppppppppppppppppppppppppp'/>")
- out = fuzz(in_1, in_2)
- print(out)
-