From 2fe7889912c9bb340f302a037585b7b1836ac94f Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Mon, 3 Feb 2020 13:11:10 +0100 Subject: move custom and pythoon mutators examples into examples/ --- custom_mutators/README | 2 - custom_mutators/simple_mutator.c | 49 --- docs/custom_mutator.md | 2 +- docs/python_mutators.md | 148 +++++++++ docs/python_mutators.txt | 156 ---------- examples/README.md | 47 +++ examples/argv_fuzzing/Makefile | 41 +++ examples/argv_fuzzing/README.md | 16 + examples/argv_fuzzing/argv-fuzz-inl.h | 90 ++++++ examples/argv_fuzzing/argvfuzz.c | 49 +++ examples/asan_cgroups/limit_memory.sh | 157 ++++++++++ examples/bash_shellshock/shellshock-fuzz.diff | 59 ++++ examples/canvas_harness/canvas_harness.html | 170 +++++++++++ examples/clang_asm_normalize/as | 75 +++++ examples/crash_triage/triage_crashes.sh | 115 +++++++ examples/custom_mutators/README | 2 + examples/custom_mutators/simple_mutator.c | 49 +++ examples/distributed_fuzzing/sync_script.sh | 94 ++++++ examples/libpng_no_checksum/libpng-nocrc.patch | 15 + examples/persistent_demo/persistent_demo.c | 101 +++++++ examples/post_library/post_library.so.c | 120 ++++++++ examples/post_library/post_library_png.so.c | 114 +++++++ examples/python_mutators/README | 18 ++ examples/python_mutators/XmlMutatorMin.py | 331 +++++++++++++++++++++ examples/python_mutators/common.py | 37 +++ examples/python_mutators/example.py | 103 +++++++ examples/python_mutators/simple-chunk-replace.py | 59 ++++ examples/python_mutators/wrapper_afl_min.py | 117 ++++++++ examples/socket_fuzzing/Makefile | 39 +++ examples/socket_fuzzing/README.md | 11 + examples/socket_fuzzing/socketfuzz.c | 110 +++++++ experimental/README.experiments | 41 --- experimental/argv_fuzzing/Makefile | 41 --- experimental/argv_fuzzing/README.md | 16 - experimental/argv_fuzzing/argv-fuzz-inl.h | 90 ------ experimental/argv_fuzzing/argvfuzz.c | 49 --- experimental/asan_cgroups/limit_memory.sh | 157 ---------- experimental/bash_shellshock/shellshock-fuzz.diff | 59 ---- experimental/canvas_harness/canvas_harness.html | 170 ----------- experimental/clang_asm_normalize/as | 75 ----- experimental/crash_triage/triage_crashes.sh | 115 ------- experimental/distributed_fuzzing/sync_script.sh | 94 ------ experimental/libpng_no_checksum/libpng-nocrc.patch | 15 - experimental/persistent_demo/persistent_demo.c | 101 ------- experimental/post_library/post_library.so.c | 120 -------- experimental/post_library/post_library_png.so.c | 114 ------- experimental/socket_fuzzing/Makefile | 39 --- experimental/socket_fuzzing/README.md | 11 - experimental/socket_fuzzing/socketfuzz.c | 110 ------- python_mutators/README | 18 -- python_mutators/XmlMutatorMin.py | 331 --------------------- python_mutators/common.py | 37 --- python_mutators/example.py | 103 ------- python_mutators/simple-chunk-replace.py | 59 ---- python_mutators/wrapper_afl_min.py | 117 -------- 55 files changed, 2288 insertions(+), 2290 deletions(-) delete mode 100644 custom_mutators/README delete mode 100644 custom_mutators/simple_mutator.c create mode 100644 docs/python_mutators.md delete mode 100644 docs/python_mutators.txt create mode 100644 examples/README.md create mode 100644 examples/argv_fuzzing/Makefile create mode 100644 examples/argv_fuzzing/README.md create mode 100644 examples/argv_fuzzing/argv-fuzz-inl.h create mode 100644 examples/argv_fuzzing/argvfuzz.c create mode 100755 examples/asan_cgroups/limit_memory.sh create mode 100644 examples/bash_shellshock/shellshock-fuzz.diff create mode 100644 examples/canvas_harness/canvas_harness.html create mode 100755 examples/clang_asm_normalize/as create mode 100755 examples/crash_triage/triage_crashes.sh create mode 100644 examples/custom_mutators/README create mode 100644 examples/custom_mutators/simple_mutator.c create mode 100755 examples/distributed_fuzzing/sync_script.sh create mode 100644 examples/libpng_no_checksum/libpng-nocrc.patch create mode 100644 examples/persistent_demo/persistent_demo.c create mode 100644 examples/post_library/post_library.so.c create mode 100644 examples/post_library/post_library_png.so.c create mode 100644 examples/python_mutators/README create mode 100644 examples/python_mutators/XmlMutatorMin.py create mode 100644 examples/python_mutators/common.py create mode 100644 examples/python_mutators/example.py create mode 100644 examples/python_mutators/simple-chunk-replace.py create mode 100644 examples/python_mutators/wrapper_afl_min.py create mode 100644 examples/socket_fuzzing/Makefile create mode 100644 examples/socket_fuzzing/README.md create mode 100644 examples/socket_fuzzing/socketfuzz.c delete mode 100644 experimental/README.experiments delete mode 100644 experimental/argv_fuzzing/Makefile delete mode 100644 experimental/argv_fuzzing/README.md delete mode 100644 experimental/argv_fuzzing/argv-fuzz-inl.h delete mode 100644 experimental/argv_fuzzing/argvfuzz.c delete mode 100755 experimental/asan_cgroups/limit_memory.sh delete mode 100644 experimental/bash_shellshock/shellshock-fuzz.diff delete mode 100644 experimental/canvas_harness/canvas_harness.html delete mode 100755 experimental/clang_asm_normalize/as delete mode 100755 experimental/crash_triage/triage_crashes.sh delete mode 100755 experimental/distributed_fuzzing/sync_script.sh delete mode 100644 experimental/libpng_no_checksum/libpng-nocrc.patch delete mode 100644 experimental/persistent_demo/persistent_demo.c delete mode 100644 experimental/post_library/post_library.so.c delete mode 100644 experimental/post_library/post_library_png.so.c delete mode 100644 experimental/socket_fuzzing/Makefile delete mode 100644 experimental/socket_fuzzing/README.md delete mode 100644 experimental/socket_fuzzing/socketfuzz.c delete mode 100644 python_mutators/README delete mode 100644 python_mutators/XmlMutatorMin.py delete mode 100644 python_mutators/common.py delete mode 100644 python_mutators/example.py delete mode 100644 python_mutators/simple-chunk-replace.py delete mode 100644 python_mutators/wrapper_afl_min.py diff --git a/custom_mutators/README b/custom_mutators/README deleted file mode 100644 index e83baa67..00000000 --- a/custom_mutators/README +++ /dev/null @@ -1,2 +0,0 @@ -This is a simple example for the AFL_CUSTOM_MUTATOR_LIBRARY feature. -For more information see docs/custom_mutator.txt diff --git a/custom_mutators/simple_mutator.c b/custom_mutators/simple_mutator.c deleted file mode 100644 index bf655679..00000000 --- a/custom_mutators/simple_mutator.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - Simple Custom Mutator for AFL - - Written by Khaled Yakdan - - 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 -#include -#include - -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; - -} - diff --git a/docs/custom_mutator.md b/docs/custom_mutator.md index 142396dd..19009f92 100644 --- a/docs/custom_mutator.md +++ b/docs/custom_mutator.md @@ -33,4 +33,4 @@ is not needed. ## 2) Example -A simple example is provided in ../custom_mutators/ +A simple example is provided in ../examples/custom_mutators/ diff --git a/docs/python_mutators.md b/docs/python_mutators.md new file mode 100644 index 00000000..a7e2c7de --- /dev/null +++ b/docs/python_mutators.md @@ -0,0 +1,148 @@ +# Adding custom mutators to AFL using Python modules + + This file describes how you can utilize the external Python API to write + your own custom mutation routines. + + Note: This feature is highly experimental. Use at your own risk. + + Implemented by Christian Holler (:decoder) . + + NOTE: Only cPython 2.7, 3.7 and above are supported, although others may work. + Depending on with which version afl-fuzz was compiled against, you must use + python2 or python3 syntax in your scripts! + After a major version upgrade (e.g. 3.7 -> 3.8), a recompilation of afl-fuzz may be needed. + + For an example and a template see ../examples/python_mutators/ + + +## 1) Description and purpose + +While AFLFuzz comes with a good selection of generic deterministic and +non-deterministic mutation operations, it sometimes might make sense to extend +these to implement strategies more specific to the target you are fuzzing. + +For simplicity and in order to allow people without C knowledge to extend +AFLFuzz, I implemented a "Python" stage that can make use of an external +module (written in Python) that implements a custom mutation stage. + +The main motivation behind this is to lower the barrier for people +experimenting with this tool. Hopefully, someone will be able to do useful +things with this extension. + +If you find it useful, have questions or need additional features added to the +interface, feel free to send a mail to . + +See the following information to get a better pictures: + https://www.agarri.fr/docs/XML_Fuzzing-NullCon2017-PUBLIC.pdf + https://bugs.chromium.org/p/chromium/issues/detail?id=930663 + + +## 2) How the Python module looks like + +You can find a simple example in pymodules/example.py including documentation +explaining each function. In the same directory, you can find another simple +module that performs simple mutations. + +Right now, "init" is called at program startup and can be used to perform any +kinds of one-time initializations while "fuzz" is called each time a mutation +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 + +You must install the python 3 or 2 development package of your Linux +distribution before this will work. On Debian/Ubuntu/Kali this can be done +with either: + apt install python3-dev +or + apt install python-dev +Note that for some distributions you might also need the package python[23]-apt + +A prerequisite for using this mode is to compile AFLFuzz with Python support. + +The AFL++ Makefile detects Python 3 and 2 through `python-config` if is is in the PATH +and compiles afl-fuzz with the feature if available. + +In case your setup is different set the necessary variables like this: +PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make + + +## 4) How to run AFLFuzz with your custom module + +You must pass the module name inside the env variable AFL_PYTHON_MODULE. + +In addition, if you are trying to load the module from the local directory, +you must adjust your PYTHONPATH to reflect this circumstance. The following +command should work if you are inside the aflfuzz directory: + +$ AFL_PYTHON_MODULE="pymodules.test" PYTHONPATH=. ./afl-fuzz + +Optionally, the following environment variables are supported: + +AFL_PYTHON_ONLY - Disable all other mutation stages. This can prevent broken + testcases (those that your Python module can't work with + anymore) to fill up your queue. Best combined with a custom + trimming routine (see below) because trimming can cause the + same test breakage like havoc and splice. + +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 + +The Python stage is set to be the first non-deterministic stage (right before +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 + +The generic trimming routines implemented in AFLFuzz can easily destroy the +structure of complex formats, possibly leading to a point where you have a lot +of testcases in the queue that your Python module cannot process anymore but +your target application still accepts. This is especially the case when your +target can process a part of the input (causing coverage) and then errors out +on the remaining input. + +In such cases, it makes sense to implement a custom trimming routine in Python. +The API consists of multiple methods because after each trimming step, we have +to go back into the C code to check if the coverage bitmap is still the same +for the trimmed input. Here's a quick API description: + +init_trim: 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. + +trim: 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. + +post_trim: 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. + In any case, this method must return the next trim iteration index + (from 0 to the maximum amount of steps you returned in init_trim). + +Omitting any of the methods will cause Python trimming to be disabled and +trigger a fallback to the builtin default trimming routine. diff --git a/docs/python_mutators.txt b/docs/python_mutators.txt deleted file mode 100644 index 7fd54547..00000000 --- a/docs/python_mutators.txt +++ /dev/null @@ -1,156 +0,0 @@ -================================================== -Adding custom mutators to AFL using Python modules -================================================== - - This file describes how you can utilize the external Python API to write - your own custom mutation routines. - - Note: This feature is highly experimental. Use at your own risk. - - Implemented by Christian Holler (:decoder) . - - NOTE: Only cPython 2.7, 3.7 and above are supported, although others may work. - Depending on with which version afl-fuzz was compiled against, you must use - python2 or python3 syntax in your scripts! - After a major version upgrade (e.g. 3.7 -> 3.8), a recompilation of afl-fuzz may be needed. - - For an example and a template see ../python_mutators/ - - -1) Description and purpose --------------------------- - -While AFLFuzz comes with a good selection of generic deterministic and -non-deterministic mutation operations, it sometimes might make sense to extend -these to implement strategies more specific to the target you are fuzzing. - -For simplicity and in order to allow people without C knowledge to extend -AFLFuzz, I implemented a "Python" stage that can make use of an external -module (written in Python) that implements a custom mutation stage. - -The main motivation behind this is to lower the barrier for people -experimenting with this tool. Hopefully, someone will be able to do useful -things with this extension. - -If you find it useful, have questions or need additional features added to the -interface, feel free to send a mail to . - -See the following information to get a better pictures: - https://www.agarri.fr/docs/XML_Fuzzing-NullCon2017-PUBLIC.pdf - https://bugs.chromium.org/p/chromium/issues/detail?id=930663 - - -2) How the Python module looks like ------------------------------------ - -You can find a simple example in pymodules/example.py including documentation -explaining each function. In the same directory, you can find another simple -module that performs simple mutations. - -Right now, "init" is called at program startup and can be used to perform any -kinds of one-time initializations while "fuzz" is called each time a mutation -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 ---------------------------------------------- - -You must install the python 3 or 2 development package of your Linux -distribution before this will work. On Debian/Ubuntu/Kali this can be done -with either: - apt install python3-dev -or - apt install python-dev -Note that for some distributions you might also need the package python[23]-apt - -A prerequisite for using this mode is to compile AFLFuzz with Python support. - -The AFL++ Makefile detects Python 3 and 2 through `python-config` if is is in the PATH -and compiles afl-fuzz with the feature if available. - -In case your setup is different set the necessary variables like this: -PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make - - -4) How to run AFLFuzz with your custom module ---------------------------------------------- - -You must pass the module name inside the env variable AFL_PYTHON_MODULE. - -In addition, if you are trying to load the module from the local directory, -you must adjust your PYTHONPATH to reflect this circumstance. The following -command should work if you are inside the aflfuzz directory: - -$ AFL_PYTHON_MODULE="pymodules.test" PYTHONPATH=. ./afl-fuzz - -Optionally, the following environment variables are supported: - -AFL_PYTHON_ONLY - Disable all other mutation stages. This can prevent broken - testcases (those that your Python module can't work with - anymore) to fill up your queue. Best combined with a custom - trimming routine (see below) because trimming can cause the - same test breakage like havoc and splice. - -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 ------------------------ - -The Python stage is set to be the first non-deterministic stage (right before -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 -------------------- - -The generic trimming routines implemented in AFLFuzz can easily destroy the -structure of complex formats, possibly leading to a point where you have a lot -of testcases in the queue that your Python module cannot process anymore but -your target application still accepts. This is especially the case when your -target can process a part of the input (causing coverage) and then errors out -on the remaining input. - -In such cases, it makes sense to implement a custom trimming routine in Python. -The API consists of multiple methods because after each trimming step, we have -to go back into the C code to check if the coverage bitmap is still the same -for the trimmed input. Here's a quick API description: - -init_trim: 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. - -trim: 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. - -post_trim: 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. - In any case, this method must return the next trim iteration index - (from 0 to the maximum amount of steps you returned in init_trim). - -Omitting any of the methods will cause Python trimming to be disabled and -trigger a fallback to the builtin default trimming routine. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..512b03f7 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,47 @@ +# AFL++ Examples + +Here's a quick overview of the stuff you can find in this directory: + + - custom_mutstors - An example custom mutator + + - python_mutators - Python mutators examples + + - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed + (e.g., to test setuid programs). + + - asan_cgroups - a contributed script to simplify fuzzing ASAN + binaries with robust memory limits on Linux. + + - bash_shellshock - a simple hack used to find a bunch of + post-Shellshock bugs in bash. + + - canvas_harness - a test harness used to find browser bugs with a + corpus generated using simple image parsing + binaries & afl-fuzz. + + - clang_asm_normalize - a script that makes it easy to instrument + hand-written assembly, provided that you have clang. + + - crash_triage - a very rudimentary example of how to annotate crashes + with additional gdb metadata. + + - distributed_fuzzing - a sample script for synchronizing fuzzer instances + across multiple machines (see parallel_fuzzing.md). + + - libpng_no_checksum - a sample patch for removing CRC checks in libpng. + + - persistent_demo - an example of how to use the LLVM persistent process + mode to speed up certain fuzzing jobs. + + - post_library - an example of how to build postprocessors for AFL. + + - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin + for fuzzing access with afl++ + +Note that the minimize_corpus.sh tool has graduated from the experimental/ +directory and is now available as ../afl-cmin. The LLVM mode has likewise +graduated to ../llvm_mode/*. + +Most of the tools in this directory are meant chiefly as examples that need to +be tweaked for your specific needs. They come with some basic documentation, +but are not necessarily production-grade. diff --git a/examples/argv_fuzzing/Makefile b/examples/argv_fuzzing/Makefile new file mode 100644 index 00000000..ab16be87 --- /dev/null +++ b/examples/argv_fuzzing/Makefile @@ -0,0 +1,41 @@ +# +# american fuzzy lop++ - argvfuzz +# -------------------------------- +# +# Copyright 2019-2020 Kjell Braden +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +ifneq "$(filter Linux GNU%,$(shell uname))" "" + LDFLAGS += -ldl +endif + +all: argvfuzz32.so argvfuzz64.so + +argvfuzz32.so: argvfuzz.c + -$(CC) -m32 $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "argvfuzz32 build failure (that's fine)" + +argvfuzz64.so: argvfuzz.c + -$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +install: argvfuzz32.so argvfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/ + +clean: + rm -f argvfuzz32.so argvfuzz64.so diff --git a/examples/argv_fuzzing/README.md b/examples/argv_fuzzing/README.md new file mode 100644 index 00000000..fa8cad80 --- /dev/null +++ b/examples/argv_fuzzing/README.md @@ -0,0 +1,16 @@ +# argvfuzz + +afl supports fuzzing file inputs or stdin. When source is available, +`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. + +`argvfuzz` tries to provide the same functionality for binaries. When loaded +using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace +argv using the same logic of `argv-fuzz-inl.h`. + +A few conditions need to be fulfilled for this mechanism to work correctly: + +1. As it relies on hooking the loader, it cannot work on static binaries. +2. If the target binary does not use the default libc's `_start` implementation + (crt1.o), the hook may not run. +3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the + target binary expects argv to be living on the stack, things may go wrong. diff --git a/examples/argv_fuzzing/argv-fuzz-inl.h b/examples/argv_fuzzing/argv-fuzz-inl.h new file mode 100644 index 00000000..4d880020 --- /dev/null +++ b/examples/argv_fuzzing/argv-fuzz-inl.h @@ -0,0 +1,90 @@ +/* + american fuzzy lop++ - sample argv fuzzing wrapper + ------------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file shows a simple way to fuzz command-line parameters with stock + afl-fuzz. To use, add: + + #include "/path/to/argv-fuzz-inl.h" + + ...to the file containing main(), ideally placing it after all the + standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of + main(). + + This will cause the program to read NUL-delimited input from stdin and + put it in argv[]. Two subsequent NULs terminate the array. Empty + params are encoded as a lone 0x02. Lone 0x02 can't be generated, but + that shouldn't matter in real life. + + If you would like to always preserve argv[0], use this instead: + AFL_INIT_SET0("prog_name"); + +*/ + +#ifndef _HAVE_ARGV_FUZZ_INL +#define _HAVE_ARGV_FUZZ_INL + +#include + +#define AFL_INIT_ARGV() \ + do { \ + \ + argv = afl_init_argv(&argc); \ + \ + } while (0) + +#define AFL_INIT_SET0(_p) \ + do { \ + \ + argv = afl_init_argv(&argc); \ + argv[0] = (_p); \ + if (!argc) argc = 1; \ + \ + } while (0) + +#define MAX_CMDLINE_LEN 100000 +#define MAX_CMDLINE_PAR 1000 + +static char** afl_init_argv(int* argc) { + + static char in_buf[MAX_CMDLINE_LEN]; + static char* ret[MAX_CMDLINE_PAR]; + + char* ptr = in_buf; + int rc = 0; + + if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} + + while (*ptr) { + + ret[rc] = ptr; + if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; + rc++; + + while (*ptr) + ptr++; + ptr++; + + } + + *argc = rc; + + return ret; + +} + +#undef MAX_CMDLINE_LEN +#undef MAX_CMDLINE_PAR + +#endif /* !_HAVE_ARGV_FUZZ_INL */ + diff --git a/examples/argv_fuzzing/argvfuzz.c b/examples/argv_fuzzing/argvfuzz.c new file mode 100644 index 00000000..4251ca4c --- /dev/null +++ b/examples/argv_fuzzing/argvfuzz.c @@ -0,0 +1,49 @@ +/* + american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries + ------------------------------------------------------------ + + Copyright 2019-2020 Kjell Braden + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + */ + +#define _GNU_SOURCE /* for RTLD_NEXT */ +#include +#include +#include +#include +#include "argv-fuzz-inl.h" + +int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), + void (*rtld_fini)(void), void *stack_end) { + + int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, + void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), + void *stack_end); + int sub_argc; + char **sub_argv; + + (void)argc; + (void)argv; + + orig = dlsym(RTLD_NEXT, __func__); + + if (!orig) { + + fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); + exit(EXIT_FAILURE); + + } + + sub_argv = afl_init_argv(&sub_argc); + + return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); + +} + diff --git a/examples/asan_cgroups/limit_memory.sh b/examples/asan_cgroups/limit_memory.sh new file mode 100755 index 00000000..ac3a90fe --- /dev/null +++ b/examples/asan_cgroups/limit_memory.sh @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# +# american fuzzy lop++ - limit memory using cgroups +# ----------------------------------------------- +# +# Written by Samir Khakimov and +# David A. Wheeler +# +# Edits to bring the script in line with afl-cmin and other companion scripts +# by Michal Zalewski. All bugs are my fault. +# +# Copyright 2015 Institute for Defense Analyses. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This tool allows the amount of actual memory allocated to a program +# to be limited on Linux systems using cgroups, instead of the traditional +# setrlimit() API. This helps avoid the address space problems discussed in +# docs/notes_for_asan.txt. +# +# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some +# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed +# task. +# + +echo "cgroup tool for afl-fuzz by and " +echo + +unset NEW_USER +MEM_LIMIT="50" + +while getopts "+u:m:" opt; do + + case "$opt" in + + "u") + NEW_USER="$OPTARG" + ;; + + "m") + MEM_LIMIT="$[OPTARG]" + ;; + + "?") + exit 1 + ;; + + esac + +done + +if [ "$MEM_LIMIT" -lt "5" ]; then + echo "[-] Error: malformed or dangerously low value of -m." 1>&2 + exit 1 +fi + +shift $((OPTIND-1)) + +TARGET_BIN="$1" + +if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then + + cat 1>&2 <<_EOF_ +Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] + +Required parameters: + + -u user - run the fuzzer as a specific user after setting up limits + +Optional parameters: + + -m megs - set memory limit to a specified value ($MEM_LIMIT MB) + +This tool configures cgroups-based memory limits for a fuzzing job to simplify +the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in +conjunction with '-m none' passed to the afl-fuzz binary itself, say: + + $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target + +_EOF_ + + exit 1 + +fi + +# Basic sanity checks + +if [ ! "`uname -s`" = "Linux" ]; then + echo "[-] Error: this tool does not support non-Linux systems." 1>&2 + exit 1 +fi + +if [ ! "`id -u`" = "0" ]; then + echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 + exit 1 +fi + +if ! type cgcreate 2>/dev/null 1>&2; then + + echo "[-] Error: you need to install cgroup tools first." 1>&2 + + if type apt-get 2>/dev/null 1>&2; then + echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 + elif type yum 2>/dev/null 1>&2; then + echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 + fi + + exit 1 + +fi + +if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then + echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 + exit 1 +fi + +# Create a new cgroup path if necessary... We used PID-keyed groups to keep +# parallel afl-fuzz tasks separate from each other. + +CID="afl-$NEW_USER-$$" + +CPATH="/sys/fs/cgroup/memory/$CID" + +if [ ! -d "$CPATH" ]; then + + cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 + +fi + +# Set the appropriate limit... + +if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null + echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +elif grep -qE 'partition|file' /proc/swaps; then + + echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 + exit 1 + +else + + echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 + +fi + +# All right. At this point, we can just run the command. + +cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" + +cgdelete -g "memory:$CID" diff --git a/examples/bash_shellshock/shellshock-fuzz.diff b/examples/bash_shellshock/shellshock-fuzz.diff new file mode 100644 index 00000000..3fa05bf8 --- /dev/null +++ b/examples/bash_shellshock/shellshock-fuzz.diff @@ -0,0 +1,59 @@ +This patch shows a very simple way to find post-Shellshock bugs in bash, as +discussed here: + + http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html + +In essence, it shows a way to fuzz environmental variables. Instructions: + +1) Download bash 4.3, apply this patch, compile with: + + CC=/path/to/afl-gcc ./configure + make clean all + + Note that the harness puts the fuzzed output in $TEST_VARIABLE. With + Florian's Shellshock patch (bash43-028), this is no longer passed down + to the parser. + +2) Create and cd to an empty directory, put the compiled bash binary in + there, and run these commands: + + mkdir in_dir + echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt + +3) Run the fuzzer with: + + /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : + + The -d parameter is advisable only if the tested shell is fairly slow + or if you are in a hurry; will cover more ground faster, but + less systematically. + +4) Watch for crashes in out_dir/crashes/. Also watch for any new files + created in cwd if you're interested in non-crash RCEs (files will be + created whenever the shell executes "foo>bar" or something like + that). You can correlate their creation date with new entries in + out_dir/queue/. + + You can also modify the bash binary to directly check for more subtle + fault conditions, or use the synthesized entries in out_dir/queue/ + as a seed for other, possibly slower or more involved testing regimes. + + Expect several hours to get decent coverage. + +--- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 ++++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 +@@ -371,6 +371,14 @@ + env = environ; + #endif /* __OPENNT */ + ++ { ++ ++ static char val[1024 * 16]; ++ read(0, val, sizeof(val) - 1); ++ setenv("TEST_VARIABLE", val, 1); ++ ++ } ++ + USE_VAR(argc); + USE_VAR(argv); + USE_VAR(env); diff --git a/examples/canvas_harness/canvas_harness.html b/examples/canvas_harness/canvas_harness.html new file mode 100644 index 00000000..a37b6937 --- /dev/null +++ b/examples/canvas_harness/canvas_harness.html @@ -0,0 +1,170 @@ + + + + + +
+ +
+ + + +

Results

+ +
    + + + + diff --git a/examples/clang_asm_normalize/as b/examples/clang_asm_normalize/as new file mode 100755 index 00000000..45537cae --- /dev/null +++ b/examples/clang_asm_normalize/as @@ -0,0 +1,75 @@ +#!/bin/sh +# +# american fuzzy lop++ - clang assembly normalizer +# ---------------------------------------------- +# +# Originally written by Michal Zalewski +# The idea for this wrapper comes from Ryan Govostes. +# +# Copyright 2013, 2014 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# This 'as' wrapper should allow you to instrument unruly, hand-written +# assembly with afl-as. +# +# Usage: +# +# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ +# AFL_PATH=/path/to/this/directory/ make clean all + +if [ "$#" -lt "2" ]; then + echo "[-] Error: this utility can't be called directly." 1>&2 + exit 1 +fi + +if [ "$AFL_REAL_PATH" = "" ]; then + echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 + exit 1 +fi + +if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then + echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 + exit 1 +fi + +unset __AFL_AS_CMDLINE __AFL_FNAME + +while [ ! "$#" = "0" ]; do + + if [ "$#" = "1" ]; then + __AFL_FNAME="$1" + else + __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" + fi + + shift + +done + +test "$TMPDIR" = "" && TMPDIR=/tmp + +TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` + +test "$TMPFILE" = "" && exit 1 + +clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" + +ERR="$?" + +if [ ! "$ERR" = "0" ]; then + rm -f "$TMPFILE" + exit $ERR +fi + +"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" + +ERR="$?" + +rm -f "$TMPFILE" + +exit "$ERR" diff --git a/examples/crash_triage/triage_crashes.sh b/examples/crash_triage/triage_crashes.sh new file mode 100755 index 00000000..6d026d61 --- /dev/null +++ b/examples/crash_triage/triage_crashes.sh @@ -0,0 +1,115 @@ +#!/bin/sh +# +# american fuzzy lop++ - crash triage utility +# ----------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Note that this assumes that the targeted application reads from stdin +# and requires no other cmdline parameters. Modify as needed if this is +# not the case. +# +# Note that on OpenBSD, you may need to install a newer version of gdb +# (e.g., from ports). You can set GDB=/some/path to point to it if +# necessary. +# + +echo "crash triage utility for afl-fuzz by Michal Zalewski" +echo + +ulimit -v 100000 2>/dev/null +ulimit -d 100000 2>/dev/null + +if [ "$#" -lt "2" ]; then + echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 + echo 1>&2 + exit 1 +fi + +DIR="$1" +BIN="$2" +shift +shift + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + echo "$DIR" | grep -qE '^(/var)?/tmp/' + T1="$?" + + echo "$BIN" | grep -qE '^(/var)?/tmp/' + T2="$?" + + if [ "$T1" = "0" -o "$T2" = "0" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +if + [ "$GDB" = "" ]; then + GDB=gdb +fi + +if [ ! -f "$BIN" -o ! -x "$BIN" ]; then + echo "[-] Error: binary '$2' not found or is not executable." 1>&2 + exit 1 +fi + +if [ ! -d "$DIR/queue" ]; then + echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 + exit 1 +fi + +CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) + +if [ "$CCOUNT" = "0" ]; then + echo "No crashes recorded in the target directory - nothing to be done." + exit 0 +fi + +echo + +for crash in $DIR/crashes/id:*; do + + id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` + sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` + + # Grab the args, converting @@ to $crash + + use_args="" + use_stdio=1 + + for a in $@; do + + if [ "$a" = "@@" ] ; then + args="$use_args $crash" + unset use_stdio + else + args="$use_args $a" + fi + + done + + # Strip the trailing space + use_args="${use_args# }" + + echo "+++ ID $id, SIGNAL $sig +++" + echo + + if [ "$use_stdio" = "1" ]; then + $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0 + + 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 +#include +#include + +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; + +} + diff --git a/examples/distributed_fuzzing/sync_script.sh b/examples/distributed_fuzzing/sync_script.sh new file mode 100755 index 00000000..c45ae69b --- /dev/null +++ b/examples/distributed_fuzzing/sync_script.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# +# american fuzzy lop++ - fuzzer synchronization tool +# -------------------------------------------------- +# +# Originally written by Michal Zalewski +# +# Copyright 2014 Google Inc. All rights reserved. +# Copyright 2019-2020 AFLplusplus Project. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# To make this script work: +# +# - Edit FUZZ_HOSTS, FUZZ_DOMAIN, FUZZ_USER, and SYNC_DIR to reflect your +# environment. +# +# - Make sure that the system you are running this on can log into FUZZ_HOSTS +# without a password (authorized_keys or otherwise). +# +# - Make sure that every fuzzer is running with -o pointing to SYNC_DIR and -S +# that consists of its local host name, followed by an underscore, and then +# by some host-local fuzzer ID. +# + +# Hosts to synchronize the data across. +FUZZ_HOSTS='host1 host2 host3 host4' + +# Domain for all hosts +FUZZ_DOMAIN='example.com' + +# Remote user for SSH +FUZZ_USER=bob + +# Directory to synchronize +SYNC_DIR='/home/bob/sync_dir' + +# Interval (seconds) between sync attempts +SYNC_INTERVAL=$((30 * 60)) + +if [ "$AFL_ALLOW_TMP" = "" ]; then + + if [ "$PWD" = "/tmp" -o "$PWD" = "/var/tmp" ]; then + echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 + exit 1 + fi + +fi + +rm -rf .sync_tmp 2>/dev/null +mkdir .sync_tmp || exit 1 + +while :; do + + # Pull data in... + + for host in $FUZZ_HOSTS; do + + echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ + "cd '$SYNC_DIR' && tar -czf - ${host}_*/[qf]*" >".sync_tmp/${host}.tgz" + + done + + # Distribute data. For large fleets, see tips in the docs/ directory. + + for dst_host in $FUZZ_HOSTS; do + + echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." + + for src_host in $FUZZ_HOSTS; do + + test "$src_host" = "$dst_host" && continue + + echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." + + ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ + "cd '$SYNC_DIR' && tar -xkzf -" <".sync_tmp/${src_host}.tgz" + + done + + done + + echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." + + sleep $SYNC_INTERVAL + +done + diff --git a/examples/libpng_no_checksum/libpng-nocrc.patch b/examples/libpng_no_checksum/libpng-nocrc.patch new file mode 100644 index 00000000..0a3793a0 --- /dev/null +++ b/examples/libpng_no_checksum/libpng-nocrc.patch @@ -0,0 +1,15 @@ +--- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 ++++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 +@@ -268,7 +268,11 @@ + if (need_crc != 0) + { + crc = png_get_uint_32(crc_bytes); +- return ((int)(crc != png_ptr->crc)); ++ ++ if (crc != png_ptr->crc) ++ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); ++ ++ return ((int)(1 != 1)); + } + + else diff --git a/examples/persistent_demo/persistent_demo.c b/examples/persistent_demo/persistent_demo.c new file mode 100644 index 00000000..7d8638fb --- /dev/null +++ b/examples/persistent_demo/persistent_demo.c @@ -0,0 +1,101 @@ +/* + american fuzzy lop++ - persistent mode example + -------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + This file demonstrates the high-performance "persistent mode" that may be + suitable for fuzzing certain fast and well-behaved libraries, provided that + they are stateless or that their internal state can be easily reset + across runs. + + To make this work, the library and this shim need to be compiled in LLVM + mode using afl-clang-fast (other compiler wrappers will *not* work). + + */ + +#include +#include +#include +#include +#include + +/* Main entry point. */ + +int main(int argc, char** argv) { + + ssize_t len; /* how much input did we read? */ + char buf[100]; /* Example-only buffer, you'd replace it with other global or + local variables appropriate for your use case. */ + + /* The number passed to __AFL_LOOP() controls the maximum number of + iterations before the loop exits and the program is allowed to + terminate normally. This limits the impact of accidental memory leaks + and similar hiccups. */ + + while (__AFL_LOOP(1000)) { + + /*** PLACEHOLDER CODE ***/ + + /* STEP 1: Fully re-initialize all critical variables. In our example, this + involves zeroing buf[], our input buffer. */ + + memset(buf, 0, 100); + + /* STEP 2: Read input data. When reading from stdin, no special preparation + is required. When reading from a named file, you need to close + the old descriptor and reopen the file first! + + Beware of reading from buffered FILE* objects such as stdin. Use + raw file descriptors or call fopen() / fdopen() in every pass. */ + + len = read(0, buf, 100); + + /* STEP 3: This is where we'd call the tested library on the read data. + We just have some trivial inline code that faults on 'foo!'. */ + + /* do we have enough data? */ + if (len < 4) return 0; + + if (buf[0] == 'f') { + + printf("one\n"); + if (buf[1] == 'o') { + + printf("two\n"); + if (buf[2] == 'o') { + + printf("three\n"); + if (buf[3] == '!') { + + printf("four\n"); + abort(); + + } + + } + + } + + } + + /*** END PLACEHOLDER CODE ***/ + + } + + /* Once the loop is exited, terminate normally - AFL will restart the process + when this happens, with a clean slate when it comes to allocated memory, + leftover file descriptors, etc. */ + + return 0; + +} + diff --git a/examples/post_library/post_library.so.c b/examples/post_library/post_library.so.c new file mode 100644 index 00000000..487b9a6d --- /dev/null +++ b/examples/post_library/post_library.so.c @@ -0,0 +1,120 @@ +/* + american fuzzy lop++ - postprocessor library example + -------------------------------------------------- + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Postprocessor libraries can be passed to afl-fuzz to perform final cleanup + of any mutated test cases - for example, to fix up checksums in PNG files. + + Please heed the following warnings: + + 1) In almost all cases, it is more productive to comment out checksum logic + in the targeted binary (as shown in ../libpng_no_checksum/). One possible + exception is the process of fuzzing binary-only software in QEMU mode. + + 2) The use of postprocessors for anything other than checksums is + questionable and may cause more harm than good. AFL is normally pretty good + about dealing with length fields, magic values, etc. + + 3) Postprocessors that do anything non-trivial must be extremely robust to + gracefully handle malformed data and other error conditions - otherwise, + they will crash and take afl-fuzz down with them. Be wary of reading past + *len and of integer overflows when calculating file offsets. + + In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, + honestly know what you're doing =) + + With that out of the way: the postprocessor library is passed to afl-fuzz + via AFL_POST_LIBRARY. The library must be compiled with: + + gcc -shared -Wall -O3 post_library.so.c -o post_library.so + + AFL will call the afl_postprocess() function for every mutated output buffer. + From there, you have three choices: + + 1) If you don't want to modify the test case, simply return the original + buffer pointer ('in_buf'). + + 2) If you want to skip this test case altogether and have AFL generate a + new one, return NULL. Use this sparingly - it's faster than running + the target program with patently useless inputs, but still wastes CPU + time. + + 3) If you want to modify the test case, allocate an appropriately-sized + buffer, move the data into that buffer, make the necessary changes, and + then return the new pointer. You can update *len if necessary, too. + + Note that the buffer will *not* be freed for you. To avoid memory leaks, + you need to free it or reuse it on subsequent calls (as shown below). + + *** DO NOT MODIFY THE ORIGINAL 'in_buf' BUFFER. *** + + Aight. The example below shows a simple postprocessor that tries to make + sure that all input files start with "GIF89a". + + PS. If you don't like C, you can try out the unix-based wrapper from + Ben Nagy instead: https://github.com/bnagy/aflfix + + */ + +#include +#include +#include + +/* Header that must be present at the beginning of every test case: */ + +#define HEADER "GIF89a" + +/* The actual postprocessor routine called by afl-fuzz: */ + +const unsigned char* afl_postprocess(const unsigned char* in_buf, + unsigned int* len) { + + static unsigned char* saved_buf; + unsigned char* new_buf; + + /* Skip execution altogether for buffers shorter than 6 bytes (just to + show how it's done). We can trust *len to be sane. */ + + if (*len < strlen(HEADER)) return NULL; + + /* Do nothing for buffers that already start with the expected header. */ + + if (!memcmp(in_buf, HEADER, strlen(HEADER))) return in_buf; + + /* Allocate memory for new buffer, reusing previous allocation if + possible. */ + + new_buf = realloc(saved_buf, *len); + + /* If we're out of memory, the most graceful thing to do is to return the + original buffer and give up on modifying it. Let AFL handle OOM on its + own later on. */ + + if (!new_buf) return in_buf; + saved_buf = new_buf; + + /* Copy the original data to the new location. */ + + memcpy(new_buf, in_buf, *len); + + /* Insert the new header. */ + + memcpy(new_buf, HEADER, strlen(HEADER)); + + /* Return modified buffer. No need to update *len in this particular case, + as we're not changing it. */ + + return new_buf; + +} + diff --git a/examples/post_library/post_library_png.so.c b/examples/post_library/post_library_png.so.c new file mode 100644 index 00000000..43cb1101 --- /dev/null +++ b/examples/post_library/post_library_png.so.c @@ -0,0 +1,114 @@ +/* + american fuzzy lop++ - postprocessor for PNG + ------------------------------------------ + + Originally written by Michal Zalewski + + Copyright 2015 Google Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + See post_library.so.c for a general discussion of how to implement + postprocessors. This specific postprocessor attempts to fix up PNG + checksums, providing a slightly more complicated example than found + in post_library.so.c. + + Compile with: + + gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz + + */ + +#include +#include +#include +#include +#include + +#include + +/* A macro to round an integer up to 4 kB. */ + +#define UP4K(_i) ((((_i) >> 12) + 1) << 12) + +const unsigned char* afl_postprocess(const unsigned char* in_buf, + unsigned int* len) { + + static unsigned char* saved_buf; + static unsigned int saved_len; + + unsigned char* new_buf = (unsigned char*)in_buf; + unsigned int pos = 8; + + /* Don't do anything if there's not enough room for the PNG header + (8 bytes). */ + + if (*len < 8) return in_buf; + + /* Minimum size of a zero-length PNG chunk is 12 bytes; if we + don't have that, we can bail out. */ + + while (pos + 12 <= *len) { + + unsigned int chunk_len, real_cksum, file_cksum; + + /* Chunk length is the first big-endian dword in the chunk. */ + + chunk_len = ntohl(*(uint32_t*)(in_buf + pos)); + + /* Bail out if chunk size is too big or goes past EOF. */ + + if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > *len) break; + + /* Chunk checksum is calculated for chunk ID (dword) and the actual + payload. */ + + real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); + + /* The in-file checksum is the last dword past the chunk data. */ + + file_cksum = *(uint32_t*)(in_buf + pos + 8 + chunk_len); + + /* If the checksums do not match, we need to fix the file. */ + + if (real_cksum != file_cksum) { + + /* First modification? Make a copy of the input buffer. Round size + up to 4 kB to minimize the number of reallocs needed. */ + + if (new_buf == in_buf) { + + if (*len <= saved_len) { + + new_buf = saved_buf; + + } else { + + new_buf = realloc(saved_buf, UP4K(*len)); + if (!new_buf) return in_buf; + saved_buf = new_buf; + saved_len = UP4K(*len); + memcpy(new_buf, in_buf, *len); + + } + + } + + *(uint32_t*)(new_buf + pos + 8 + chunk_len) = real_cksum; + + } + + /* Skip the entire chunk and move to the next one. */ + + pos += 12 + chunk_len; + + } + + return new_buf; + +} + diff --git a/examples/python_mutators/README b/examples/python_mutators/README new file mode 100644 index 00000000..4e7d62bc --- /dev/null +++ b/examples/python_mutators/README @@ -0,0 +1,18 @@ +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/examples/python_mutators/XmlMutatorMin.py b/examples/python_mutators/XmlMutatorMin.py new file mode 100644 index 00000000..058b7e61 --- /dev/null +++ b/examples/python_mutators/XmlMutatorMin.py @@ -0,0 +1,331 @@ +#!/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[a-zA-Z:\-]+)\((?P.*?)\)" + 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/examples/python_mutators/common.py b/examples/python_mutators/common.py new file mode 100644 index 00000000..28b8ee80 --- /dev/null +++ b/examples/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/examples/python_mutators/example.py b/examples/python_mutators/example.py new file mode 100644 index 00000000..d32a7eb2 --- /dev/null +++ b/examples/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/examples/python_mutators/simple-chunk-replace.py b/examples/python_mutators/simple-chunk-replace.py new file mode 100644 index 00000000..218dd4f8 --- /dev/null +++ b/examples/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 diff --git a/examples/python_mutators/wrapper_afl_min.py b/examples/python_mutators/wrapper_afl_min.py new file mode 100644 index 00000000..df09b40a --- /dev/null +++ b/examples/python_mutators/wrapper_afl_min.py @@ -0,0 +1,117 @@ +#!/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("ffffzzzzzzzzzzzz") + in_2 = bytearray("") + out = fuzz(in_1, in_2) + print(out) + diff --git a/examples/socket_fuzzing/Makefile b/examples/socket_fuzzing/Makefile new file mode 100644 index 00000000..0191ba53 --- /dev/null +++ b/examples/socket_fuzzing/Makefile @@ -0,0 +1,39 @@ +# +# american fuzzy lop++ - socket_fuzz +# ---------------------------------- +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# + +.PHONY: all install clean + +PREFIX ?= /usr/local +BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl + +CFLAGS = -fPIC -Wall -Wextra +LDFLAGS = -shared + +ifneq "$(filter Linux GNU%,$(shell uname))" "" + LDFLAGS += -ldl +endif + +all: socketfuzz32.so socketfuzz64.so + +socketfuzz32.so: socketfuzz.c + -$(CC) -m32 $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "socketfuzz32 build failure (that's fine)" + +socketfuzz64.so: socketfuzz.c + -$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + +install: socketfuzz32.so socketfuzz64.so + install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ + if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi + install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/ + +clean: + rm -f socketfuzz32.so socketfuzz64.so diff --git a/examples/socket_fuzzing/README.md b/examples/socket_fuzzing/README.md new file mode 100644 index 00000000..79f28bea --- /dev/null +++ b/examples/socket_fuzzing/README.md @@ -0,0 +1,11 @@ +# socketfuzz + +when you want to fuzz a network service and you can not/do not want to modify +the source (or just have a binary), then this LD_PRELOAD library will allow +for sending input to stdin which the target binary will think is coming from +a network socket. + +This is desock_dup.c from the amazing preeny project +https://github.com/zardus/preeny + +It is packaged in afl++ to have it at hand if needed diff --git a/examples/socket_fuzzing/socketfuzz.c b/examples/socket_fuzzing/socketfuzz.c new file mode 100644 index 00000000..3ec8383b --- /dev/null +++ b/examples/socket_fuzzing/socketfuzz.c @@ -0,0 +1,110 @@ +/* + * This is desock_dup.c from the amazing preeny project + * https://github.com/zardus/preeny + * + * It is packaged in afl++ to have it at hand if needed + * + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include // +#include // +#include // +#include // +#include +#include +#include +#include +#include +#include +#include +//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " + +// +// originals +// +int (*original_close)(int); +int (*original_dup2)(int, int); +__attribute__((constructor)) void preeny_desock_dup_orig() { + + original_close = dlsym(RTLD_NEXT, "close"); + original_dup2 = dlsym(RTLD_NEXT, "dup2"); + +} + +int close(int sockfd) { + + if (sockfd <= 2) { + + fprintf(stderr, "Info: Disabling close on %d\n", sockfd); + return 0; + + } else { + + return original_close(sockfd); + + } + +} + +int dup2(int old, int new) { + + if (new <= 2) { + + fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); + return 0; + + } else { + + return original_dup2(old, new); + + } + +} + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); + return 0; + +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + + (void)sockfd; + (void)addr; + (void)addrlen; + fprintf(stderr, "Info: Emulating bind on port %d\n", + ntohs(((struct sockaddr_in *)addr)->sin_port)); + return 0; + +} + +int listen(int sockfd, int backlog) { + + (void)sockfd; + (void)backlog; + return 0; + +} + +int setsockopt(int sockfd, int level, int optid, const void *optdata, + socklen_t optdatalen) { + + (void)sockfd; + (void)level; + (void)optid; + (void)optdata; + (void)optdatalen; + return 0; + +} + diff --git a/experimental/README.experiments b/experimental/README.experiments deleted file mode 100644 index 06f22ee1..00000000 --- a/experimental/README.experiments +++ /dev/null @@ -1,41 +0,0 @@ -Here's a quick overview of the stuff you can find in this directory: - - - argv_fuzzing - a simple wrapper to allow cmdline to be fuzzed - (e.g., to test setuid programs). - - - asan_cgroups - a contributed script to simplify fuzzing ASAN - binaries with robust memory limits on Linux. - - - bash_shellshock - a simple hack used to find a bunch of - post-Shellshock bugs in bash. - - - canvas_harness - a test harness used to find browser bugs with a - corpus generated using simple image parsing - binaries & afl-fuzz. - - - clang_asm_normalize - a script that makes it easy to instrument - hand-written assembly, provided that you have clang. - - - crash_triage - a very rudimentary example of how to annotate crashes - with additional gdb metadata. - - - distributed_fuzzing - a sample script for synchronizing fuzzer instances - across multiple machines (see parallel_fuzzing.md). - - - libpng_no_checksum - a sample patch for removing CRC checks in libpng. - - - persistent_demo - an example of how to use the LLVM persistent process - mode to speed up certain fuzzing jobs. - - - post_library - an example of how to build postprocessors for AFL. - - - socket_fuzzing - a LD_PRELOAD library 'redirects' a socket to stdin - for fuzzing access with afl++ - -Note that the minimize_corpus.sh tool has graduated from the experimental/ -directory and is now available as ../afl-cmin. The LLVM mode has likewise -graduated to ../llvm_mode/*. - -Most of the tools in this directory are meant chiefly as examples that need to -be tweaked for your specific needs. They come with some basic documentation, -but are not necessarily production-grade. diff --git a/experimental/argv_fuzzing/Makefile b/experimental/argv_fuzzing/Makefile deleted file mode 100644 index ab16be87..00000000 --- a/experimental/argv_fuzzing/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -# -# american fuzzy lop++ - argvfuzz -# -------------------------------- -# -# Copyright 2019-2020 Kjell Braden -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -ifneq "$(filter Linux GNU%,$(shell uname))" "" - LDFLAGS += -ldl -endif - -all: argvfuzz32.so argvfuzz64.so - -argvfuzz32.so: argvfuzz.c - -$(CC) -m32 $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "argvfuzz32 build failure (that's fine)" - -argvfuzz64.so: argvfuzz.c - -$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -install: argvfuzz32.so argvfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f argvfuzz32.so ]; then set -e; install -m 755 argvfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - install -m 755 argvfuzz64.so $(DESTDIR)$(HELPER_PATH)/ - -clean: - rm -f argvfuzz32.so argvfuzz64.so diff --git a/experimental/argv_fuzzing/README.md b/experimental/argv_fuzzing/README.md deleted file mode 100644 index fa8cad80..00000000 --- a/experimental/argv_fuzzing/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# argvfuzz - -afl supports fuzzing file inputs or stdin. When source is available, -`argv-fuzz-inl.h` can be used to change `main()` to build argv from stdin. - -`argvfuzz` tries to provide the same functionality for binaries. When loaded -using `LD_PRELOAD`, it will hook the call to `__libc_start_main` and replace -argv using the same logic of `argv-fuzz-inl.h`. - -A few conditions need to be fulfilled for this mechanism to work correctly: - -1. As it relies on hooking the loader, it cannot work on static binaries. -2. If the target binary does not use the default libc's `_start` implementation - (crt1.o), the hook may not run. -3. The hook will replace argv with pointers to `.data` of `argvfuzz.so`. If the - target binary expects argv to be living on the stack, things may go wrong. diff --git a/experimental/argv_fuzzing/argv-fuzz-inl.h b/experimental/argv_fuzzing/argv-fuzz-inl.h deleted file mode 100644 index 4d880020..00000000 --- a/experimental/argv_fuzzing/argv-fuzz-inl.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - american fuzzy lop++ - sample argv fuzzing wrapper - ------------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file shows a simple way to fuzz command-line parameters with stock - afl-fuzz. To use, add: - - #include "/path/to/argv-fuzz-inl.h" - - ...to the file containing main(), ideally placing it after all the - standard includes. Next, put AFL_INIT_ARGV(); near the very beginning of - main(). - - This will cause the program to read NUL-delimited input from stdin and - put it in argv[]. Two subsequent NULs terminate the array. Empty - params are encoded as a lone 0x02. Lone 0x02 can't be generated, but - that shouldn't matter in real life. - - If you would like to always preserve argv[0], use this instead: - AFL_INIT_SET0("prog_name"); - -*/ - -#ifndef _HAVE_ARGV_FUZZ_INL -#define _HAVE_ARGV_FUZZ_INL - -#include - -#define AFL_INIT_ARGV() \ - do { \ - \ - argv = afl_init_argv(&argc); \ - \ - } while (0) - -#define AFL_INIT_SET0(_p) \ - do { \ - \ - argv = afl_init_argv(&argc); \ - argv[0] = (_p); \ - if (!argc) argc = 1; \ - \ - } while (0) - -#define MAX_CMDLINE_LEN 100000 -#define MAX_CMDLINE_PAR 1000 - -static char** afl_init_argv(int* argc) { - - static char in_buf[MAX_CMDLINE_LEN]; - static char* ret[MAX_CMDLINE_PAR]; - - char* ptr = in_buf; - int rc = 0; - - if (read(0, in_buf, MAX_CMDLINE_LEN - 2) < 0) {} - - while (*ptr) { - - ret[rc] = ptr; - if (ret[rc][0] == 0x02 && !ret[rc][1]) ret[rc]++; - rc++; - - while (*ptr) - ptr++; - ptr++; - - } - - *argc = rc; - - return ret; - -} - -#undef MAX_CMDLINE_LEN -#undef MAX_CMDLINE_PAR - -#endif /* !_HAVE_ARGV_FUZZ_INL */ - diff --git a/experimental/argv_fuzzing/argvfuzz.c b/experimental/argv_fuzzing/argvfuzz.c deleted file mode 100644 index 4251ca4c..00000000 --- a/experimental/argv_fuzzing/argvfuzz.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - american fuzzy lop++ - LD_PRELOAD for fuzzing argv in binaries - ------------------------------------------------------------ - - Copyright 2019-2020 Kjell Braden - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - */ - -#define _GNU_SOURCE /* for RTLD_NEXT */ -#include -#include -#include -#include -#include "argv-fuzz-inl.h" - -int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), - void (*rtld_fini)(void), void *stack_end) { - - int (*orig)(int (*main)(int, char **, char **), int argc, char **argv, - void (*init)(void), void (*fini)(void), void (*rtld_fini)(void), - void *stack_end); - int sub_argc; - char **sub_argv; - - (void)argc; - (void)argv; - - orig = dlsym(RTLD_NEXT, __func__); - - if (!orig) { - - fprintf(stderr, "hook did not find original %s: %s\n", __func__, dlerror()); - exit(EXIT_FAILURE); - - } - - sub_argv = afl_init_argv(&sub_argc); - - return orig(main, sub_argc, sub_argv, init, fini, rtld_fini, stack_end); - -} - diff --git a/experimental/asan_cgroups/limit_memory.sh b/experimental/asan_cgroups/limit_memory.sh deleted file mode 100755 index ac3a90fe..00000000 --- a/experimental/asan_cgroups/limit_memory.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env bash -# -# american fuzzy lop++ - limit memory using cgroups -# ----------------------------------------------- -# -# Written by Samir Khakimov and -# David A. Wheeler -# -# Edits to bring the script in line with afl-cmin and other companion scripts -# by Michal Zalewski. All bugs are my fault. -# -# Copyright 2015 Institute for Defense Analyses. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This tool allows the amount of actual memory allocated to a program -# to be limited on Linux systems using cgroups, instead of the traditional -# setrlimit() API. This helps avoid the address space problems discussed in -# docs/notes_for_asan.txt. -# -# Important: the limit covers *both* afl-fuzz and the fuzzed binary. In some -# hopefully rare circumstances, afl-fuzz could be killed before the fuzzed -# task. -# - -echo "cgroup tool for afl-fuzz by and " -echo - -unset NEW_USER -MEM_LIMIT="50" - -while getopts "+u:m:" opt; do - - case "$opt" in - - "u") - NEW_USER="$OPTARG" - ;; - - "m") - MEM_LIMIT="$[OPTARG]" - ;; - - "?") - exit 1 - ;; - - esac - -done - -if [ "$MEM_LIMIT" -lt "5" ]; then - echo "[-] Error: malformed or dangerously low value of -m." 1>&2 - exit 1 -fi - -shift $((OPTIND-1)) - -TARGET_BIN="$1" - -if [ "$TARGET_BIN" = "" -o "$NEW_USER" = "" ]; then - - cat 1>&2 <<_EOF_ -Usage: $0 [ options ] -- /path/to/afl-fuzz [ ...afl options... ] - -Required parameters: - - -u user - run the fuzzer as a specific user after setting up limits - -Optional parameters: - - -m megs - set memory limit to a specified value ($MEM_LIMIT MB) - -This tool configures cgroups-based memory limits for a fuzzing job to simplify -the task of fuzzing ASAN or MSAN binaries. You would normally want to use it in -conjunction with '-m none' passed to the afl-fuzz binary itself, say: - - $0 -u joe ./afl-fuzz -i input -o output -m none /path/to/target - -_EOF_ - - exit 1 - -fi - -# Basic sanity checks - -if [ ! "`uname -s`" = "Linux" ]; then - echo "[-] Error: this tool does not support non-Linux systems." 1>&2 - exit 1 -fi - -if [ ! "`id -u`" = "0" ]; then - echo "[-] Error: you need to run this script as root (sorry!)." 1>&2 - exit 1 -fi - -if ! type cgcreate 2>/dev/null 1>&2; then - - echo "[-] Error: you need to install cgroup tools first." 1>&2 - - if type apt-get 2>/dev/null 1>&2; then - echo " (Perhaps 'apt-get install cgroup-bin' will work.)" 1>&2 - elif type yum 2>/dev/null 1>&2; then - echo " (Perhaps 'yum install libcgroup-tools' will work.)" 1>&2 - fi - - exit 1 - -fi - -if ! id -u "$NEW_USER" 2>/dev/null 1>&2; then - echo "[-] Error: user '$NEW_USER' does not seem to exist." 1>&2 - exit 1 -fi - -# Create a new cgroup path if necessary... We used PID-keyed groups to keep -# parallel afl-fuzz tasks separate from each other. - -CID="afl-$NEW_USER-$$" - -CPATH="/sys/fs/cgroup/memory/$CID" - -if [ ! -d "$CPATH" ]; then - - cgcreate -a "$NEW_USER" -g memory:"$CID" || exit 1 - -fi - -# Set the appropriate limit... - -if [ -f "$CPATH/memory.memsw.limit_in_bytes" ]; then - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" 2>/dev/null - echo "${MEM_LIMIT}M" > "$CPATH/memory.memsw.limit_in_bytes" || exit 1 - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -elif grep -qE 'partition|file' /proc/swaps; then - - echo "[-] Error: your system requires swap to be disabled first (swapoff -a)." 1>&2 - exit 1 - -else - - echo "${MEM_LIMIT}M" > "$CPATH/memory.limit_in_bytes" || exit 1 - -fi - -# All right. At this point, we can just run the command. - -cgexec -g "memory:$CID" su -c "$*" "$NEW_USER" - -cgdelete -g "memory:$CID" diff --git a/experimental/bash_shellshock/shellshock-fuzz.diff b/experimental/bash_shellshock/shellshock-fuzz.diff deleted file mode 100644 index 3fa05bf8..00000000 --- a/experimental/bash_shellshock/shellshock-fuzz.diff +++ /dev/null @@ -1,59 +0,0 @@ -This patch shows a very simple way to find post-Shellshock bugs in bash, as -discussed here: - - http://lcamtuf.blogspot.com/2014/10/bash-bug-how-we-finally-cracked.html - -In essence, it shows a way to fuzz environmental variables. Instructions: - -1) Download bash 4.3, apply this patch, compile with: - - CC=/path/to/afl-gcc ./configure - make clean all - - Note that the harness puts the fuzzed output in $TEST_VARIABLE. With - Florian's Shellshock patch (bash43-028), this is no longer passed down - to the parser. - -2) Create and cd to an empty directory, put the compiled bash binary in - there, and run these commands: - - mkdir in_dir - echo -n '() { a() { a; }; : >b; }' >in_dir/script.txt - -3) Run the fuzzer with: - - /path/to/afl-fuzz -d -i in_dir -o out_dir ./bash -c : - - The -d parameter is advisable only if the tested shell is fairly slow - or if you are in a hurry; will cover more ground faster, but - less systematically. - -4) Watch for crashes in out_dir/crashes/. Also watch for any new files - created in cwd if you're interested in non-crash RCEs (files will be - created whenever the shell executes "foo>bar" or something like - that). You can correlate their creation date with new entries in - out_dir/queue/. - - You can also modify the bash binary to directly check for more subtle - fault conditions, or use the synthesized entries in out_dir/queue/ - as a seed for other, possibly slower or more involved testing regimes. - - Expect several hours to get decent coverage. - ---- bash-4.3/shell.c.orig 2014-01-14 14:04:32.000000000 +0100 -+++ bash-4.3/shell.c 2015-04-30 05:56:46.000000000 +0200 -@@ -371,6 +371,14 @@ - env = environ; - #endif /* __OPENNT */ - -+ { -+ -+ static char val[1024 * 16]; -+ read(0, val, sizeof(val) - 1); -+ setenv("TEST_VARIABLE", val, 1); -+ -+ } -+ - USE_VAR(argc); - USE_VAR(argv); - USE_VAR(env); diff --git a/experimental/canvas_harness/canvas_harness.html b/experimental/canvas_harness/canvas_harness.html deleted file mode 100644 index a37b6937..00000000 --- a/experimental/canvas_harness/canvas_harness.html +++ /dev/null @@ -1,170 +0,0 @@ - - - - - -
    - -
    - - - -

    Results

    - -
      - - - - diff --git a/experimental/clang_asm_normalize/as b/experimental/clang_asm_normalize/as deleted file mode 100755 index 45537cae..00000000 --- a/experimental/clang_asm_normalize/as +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - clang assembly normalizer -# ---------------------------------------------- -# -# Originally written by Michal Zalewski -# The idea for this wrapper comes from Ryan Govostes. -# -# Copyright 2013, 2014 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# This 'as' wrapper should allow you to instrument unruly, hand-written -# assembly with afl-as. -# -# Usage: -# -# export AFL_REAL_PATH=/path/to/directory/with/afl-as/ -# AFL_PATH=/path/to/this/directory/ make clean all - -if [ "$#" -lt "2" ]; then - echo "[-] Error: this utility can't be called directly." 1>&2 - exit 1 -fi - -if [ "$AFL_REAL_PATH" = "" ]; then - echo "[-] Error: AFL_REAL_PATH not set!" 1>&2 - exit 1 -fi - -if [ ! -x "$AFL_REAL_PATH/afl-as" ]; then - echo "[-] Error: AFL_REAL_PATH does not contain the 'afl-as' binary." 1>&2 - exit 1 -fi - -unset __AFL_AS_CMDLINE __AFL_FNAME - -while [ ! "$#" = "0" ]; do - - if [ "$#" = "1" ]; then - __AFL_FNAME="$1" - else - __AFL_AS_CMDLINE="${__AFL_AS_CMDLINE} $1" - fi - - shift - -done - -test "$TMPDIR" = "" && TMPDIR=/tmp - -TMPFILE=`mktemp $TMPDIR/.afl-XXXXXXXXXX.s` - -test "$TMPFILE" = "" && exit 1 - -clang -cc1as -filetype asm -output-asm-variant 0 "${__AFL_FNAME}" >"$TMPFILE" - -ERR="$?" - -if [ ! "$ERR" = "0" ]; then - rm -f "$TMPFILE" - exit $ERR -fi - -"$AFL_REAL_PATH/afl-as" ${__AFL_AS_CMDLINE} "$TMPFILE" - -ERR="$?" - -rm -f "$TMPFILE" - -exit "$ERR" diff --git a/experimental/crash_triage/triage_crashes.sh b/experimental/crash_triage/triage_crashes.sh deleted file mode 100755 index 6d026d61..00000000 --- a/experimental/crash_triage/triage_crashes.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/sh -# -# american fuzzy lop++ - crash triage utility -# ----------------------------------------- -# -# Originally written by Michal Zalewski -# -# Copyright 2013, 2014, 2017 Google Inc. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Note that this assumes that the targeted application reads from stdin -# and requires no other cmdline parameters. Modify as needed if this is -# not the case. -# -# Note that on OpenBSD, you may need to install a newer version of gdb -# (e.g., from ports). You can set GDB=/some/path to point to it if -# necessary. -# - -echo "crash triage utility for afl-fuzz by Michal Zalewski" -echo - -ulimit -v 100000 2>/dev/null -ulimit -d 100000 2>/dev/null - -if [ "$#" -lt "2" ]; then - echo "Usage: $0 /path/to/afl_output_dir /path/to/tested_binary [...target params...]" 1>&2 - echo 1>&2 - exit 1 -fi - -DIR="$1" -BIN="$2" -shift -shift - -if [ "$AFL_ALLOW_TMP" = "" ]; then - - echo "$DIR" | grep -qE '^(/var)?/tmp/' - T1="$?" - - echo "$BIN" | grep -qE '^(/var)?/tmp/' - T2="$?" - - if [ "$T1" = "0" -o "$T2" = "0" ]; then - echo "[-] Error: do not use shared /tmp or /var/tmp directories with this script." 1>&2 - exit 1 - fi - -fi - -if - [ "$GDB" = "" ]; then - GDB=gdb -fi - -if [ ! -f "$BIN" -o ! -x "$BIN" ]; then - echo "[-] Error: binary '$2' not found or is not executable." 1>&2 - exit 1 -fi - -if [ ! -d "$DIR/queue" ]; then - echo "[-] Error: directory '$1' not found or not created by afl-fuzz." 1>&2 - exit 1 -fi - -CCOUNT=$((`ls -- "$DIR/crashes" 2>/dev/null | wc -l`)) - -if [ "$CCOUNT" = "0" ]; then - echo "No crashes recorded in the target directory - nothing to be done." - exit 0 -fi - -echo - -for crash in $DIR/crashes/id:*; do - - id=`basename -- "$crash" | cut -d, -f1 | cut -d: -f2` - sig=`basename -- "$crash" | cut -d, -f2 | cut -d: -f2` - - # Grab the args, converting @@ to $crash - - use_args="" - use_stdio=1 - - for a in $@; do - - if [ "$a" = "@@" ] ; then - args="$use_args $crash" - unset use_stdio - else - args="$use_args $a" - fi - - done - - # Strip the trailing space - use_args="${use_args# }" - - echo "+++ ID $id, SIGNAL $sig +++" - echo - - if [ "$use_stdio" = "1" ]; then - $GDB --batch -q --ex "r $use_args <$crash" --ex 'back' --ex 'disass $pc, $pc+16' --ex 'info reg' --ex 'quit' "$BIN" 0&2 - exit 1 - fi - -fi - -rm -rf .sync_tmp 2>/dev/null -mkdir .sync_tmp || exit 1 - -while :; do - - # Pull data in... - - for host in $FUZZ_HOSTS; do - - echo "[*] Retrieving data from ${host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@${host}.$FUZZ_DOMAIN \ - "cd '$SYNC_DIR' && tar -czf - ${host}_*/[qf]*" >".sync_tmp/${host}.tgz" - - done - - # Distribute data. For large fleets, see tips in the docs/ directory. - - for dst_host in $FUZZ_HOSTS; do - - echo "[*] Distributing data to ${dst_host}.${FUZZ_DOMAIN}..." - - for src_host in $FUZZ_HOSTS; do - - test "$src_host" = "$dst_host" && continue - - echo " Sending fuzzer data from ${src_host}.${FUZZ_DOMAIN}..." - - ssh -o 'passwordauthentication no' ${FUZZ_USER}@$dst_host \ - "cd '$SYNC_DIR' && tar -xkzf -" <".sync_tmp/${src_host}.tgz" - - done - - done - - echo "[+] Done. Sleeping for $SYNC_INTERVAL seconds (Ctrl-C to quit)." - - sleep $SYNC_INTERVAL - -done - diff --git a/experimental/libpng_no_checksum/libpng-nocrc.patch b/experimental/libpng_no_checksum/libpng-nocrc.patch deleted file mode 100644 index 0a3793a0..00000000 --- a/experimental/libpng_no_checksum/libpng-nocrc.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- pngrutil.c.orig 2014-06-12 03:35:16.000000000 +0200 -+++ pngrutil.c 2014-07-01 05:08:31.000000000 +0200 -@@ -268,7 +268,11 @@ - if (need_crc != 0) - { - crc = png_get_uint_32(crc_bytes); -- return ((int)(crc != png_ptr->crc)); -+ -+ if (crc != png_ptr->crc) -+ fprintf(stderr, "NOTE: CRC in the file is 0x%08x, change to 0x%08x\n", crc, png_ptr->crc); -+ -+ return ((int)(1 != 1)); - } - - else diff --git a/experimental/persistent_demo/persistent_demo.c b/experimental/persistent_demo/persistent_demo.c deleted file mode 100644 index 7d8638fb..00000000 --- a/experimental/persistent_demo/persistent_demo.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - american fuzzy lop++ - persistent mode example - -------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This file demonstrates the high-performance "persistent mode" that may be - suitable for fuzzing certain fast and well-behaved libraries, provided that - they are stateless or that their internal state can be easily reset - across runs. - - To make this work, the library and this shim need to be compiled in LLVM - mode using afl-clang-fast (other compiler wrappers will *not* work). - - */ - -#include -#include -#include -#include -#include - -/* Main entry point. */ - -int main(int argc, char** argv) { - - ssize_t len; /* how much input did we read? */ - char buf[100]; /* Example-only buffer, you'd replace it with other global or - local variables appropriate for your use case. */ - - /* The number passed to __AFL_LOOP() controls the maximum number of - iterations before the loop exits and the program is allowed to - terminate normally. This limits the impact of accidental memory leaks - and similar hiccups. */ - - while (__AFL_LOOP(1000)) { - - /*** PLACEHOLDER CODE ***/ - - /* STEP 1: Fully re-initialize all critical variables. In our example, this - involves zeroing buf[], our input buffer. */ - - memset(buf, 0, 100); - - /* STEP 2: Read input data. When reading from stdin, no special preparation - is required. When reading from a named file, you need to close - the old descriptor and reopen the file first! - - Beware of reading from buffered FILE* objects such as stdin. Use - raw file descriptors or call fopen() / fdopen() in every pass. */ - - len = read(0, buf, 100); - - /* STEP 3: This is where we'd call the tested library on the read data. - We just have some trivial inline code that faults on 'foo!'. */ - - /* do we have enough data? */ - if (len < 4) return 0; - - if (buf[0] == 'f') { - - printf("one\n"); - if (buf[1] == 'o') { - - printf("two\n"); - if (buf[2] == 'o') { - - printf("three\n"); - if (buf[3] == '!') { - - printf("four\n"); - abort(); - - } - - } - - } - - } - - /*** END PLACEHOLDER CODE ***/ - - } - - /* Once the loop is exited, terminate normally - AFL will restart the process - when this happens, with a clean slate when it comes to allocated memory, - leftover file descriptors, etc. */ - - return 0; - -} - diff --git a/experimental/post_library/post_library.so.c b/experimental/post_library/post_library.so.c deleted file mode 100644 index 487b9a6d..00000000 --- a/experimental/post_library/post_library.so.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - american fuzzy lop++ - postprocessor library example - -------------------------------------------------- - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - Postprocessor libraries can be passed to afl-fuzz to perform final cleanup - of any mutated test cases - for example, to fix up checksums in PNG files. - - Please heed the following warnings: - - 1) In almost all cases, it is more productive to comment out checksum logic - in the targeted binary (as shown in ../libpng_no_checksum/). One possible - exception is the process of fuzzing binary-only software in QEMU mode. - - 2) The use of postprocessors for anything other than checksums is - questionable and may cause more harm than good. AFL is normally pretty good - about dealing with length fields, magic values, etc. - - 3) Postprocessors that do anything non-trivial must be extremely robust to - gracefully handle malformed data and other error conditions - otherwise, - they will crash and take afl-fuzz down with them. Be wary of reading past - *len and of integer overflows when calculating file offsets. - - In other words, THIS IS PROBABLY NOT WHAT YOU WANT - unless you really, - honestly know what you're doing =) - - With that out of the way: the postprocessor library is passed to afl-fuzz - via AFL_POST_LIBRARY. The library must be compiled with: - - gcc -shared -Wall -O3 post_library.so.c -o post_library.so - - AFL will call the afl_postprocess() function for every mutated output buffer. - From there, you have three choices: - - 1) If you don't want to modify the test case, simply return the original - buffer pointer ('in_buf'). - - 2) If you want to skip this test case altogether and have AFL generate a - new one, return NULL. Use this sparingly - it's faster than running - the target program with patently useless inputs, but still wastes CPU - time. - - 3) If you want to modify the test case, allocate an appropriately-sized - buffer, move the data into that buffer, make the necessary changes, and - then return the new pointer. You can update *len if necessary, too. - - Note that the buffer will *not* be freed for you. To avoid memory leaks, - you need to free it or reuse it on subsequent calls (as shown below). - - *** DO NOT MODIFY THE ORIGINAL 'in_buf' BUFFER. *** - - Aight. The example below shows a simple postprocessor that tries to make - sure that all input files start with "GIF89a". - - PS. If you don't like C, you can try out the unix-based wrapper from - Ben Nagy instead: https://github.com/bnagy/aflfix - - */ - -#include -#include -#include - -/* Header that must be present at the beginning of every test case: */ - -#define HEADER "GIF89a" - -/* The actual postprocessor routine called by afl-fuzz: */ - -const unsigned char* afl_postprocess(const unsigned char* in_buf, - unsigned int* len) { - - static unsigned char* saved_buf; - unsigned char* new_buf; - - /* Skip execution altogether for buffers shorter than 6 bytes (just to - show how it's done). We can trust *len to be sane. */ - - if (*len < strlen(HEADER)) return NULL; - - /* Do nothing for buffers that already start with the expected header. */ - - if (!memcmp(in_buf, HEADER, strlen(HEADER))) return in_buf; - - /* Allocate memory for new buffer, reusing previous allocation if - possible. */ - - new_buf = realloc(saved_buf, *len); - - /* If we're out of memory, the most graceful thing to do is to return the - original buffer and give up on modifying it. Let AFL handle OOM on its - own later on. */ - - if (!new_buf) return in_buf; - saved_buf = new_buf; - - /* Copy the original data to the new location. */ - - memcpy(new_buf, in_buf, *len); - - /* Insert the new header. */ - - memcpy(new_buf, HEADER, strlen(HEADER)); - - /* Return modified buffer. No need to update *len in this particular case, - as we're not changing it. */ - - return new_buf; - -} - diff --git a/experimental/post_library/post_library_png.so.c b/experimental/post_library/post_library_png.so.c deleted file mode 100644 index 43cb1101..00000000 --- a/experimental/post_library/post_library_png.so.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - american fuzzy lop++ - postprocessor for PNG - ------------------------------------------ - - Originally written by Michal Zalewski - - Copyright 2015 Google Inc. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - See post_library.so.c for a general discussion of how to implement - postprocessors. This specific postprocessor attempts to fix up PNG - checksums, providing a slightly more complicated example than found - in post_library.so.c. - - Compile with: - - gcc -shared -Wall -O3 post_library_png.so.c -o post_library_png.so -lz - - */ - -#include -#include -#include -#include -#include - -#include - -/* A macro to round an integer up to 4 kB. */ - -#define UP4K(_i) ((((_i) >> 12) + 1) << 12) - -const unsigned char* afl_postprocess(const unsigned char* in_buf, - unsigned int* len) { - - static unsigned char* saved_buf; - static unsigned int saved_len; - - unsigned char* new_buf = (unsigned char*)in_buf; - unsigned int pos = 8; - - /* Don't do anything if there's not enough room for the PNG header - (8 bytes). */ - - if (*len < 8) return in_buf; - - /* Minimum size of a zero-length PNG chunk is 12 bytes; if we - don't have that, we can bail out. */ - - while (pos + 12 <= *len) { - - unsigned int chunk_len, real_cksum, file_cksum; - - /* Chunk length is the first big-endian dword in the chunk. */ - - chunk_len = ntohl(*(uint32_t*)(in_buf + pos)); - - /* Bail out if chunk size is too big or goes past EOF. */ - - if (chunk_len > 1024 * 1024 || pos + 12 + chunk_len > *len) break; - - /* Chunk checksum is calculated for chunk ID (dword) and the actual - payload. */ - - real_cksum = htonl(crc32(0, in_buf + pos + 4, chunk_len + 4)); - - /* The in-file checksum is the last dword past the chunk data. */ - - file_cksum = *(uint32_t*)(in_buf + pos + 8 + chunk_len); - - /* If the checksums do not match, we need to fix the file. */ - - if (real_cksum != file_cksum) { - - /* First modification? Make a copy of the input buffer. Round size - up to 4 kB to minimize the number of reallocs needed. */ - - if (new_buf == in_buf) { - - if (*len <= saved_len) { - - new_buf = saved_buf; - - } else { - - new_buf = realloc(saved_buf, UP4K(*len)); - if (!new_buf) return in_buf; - saved_buf = new_buf; - saved_len = UP4K(*len); - memcpy(new_buf, in_buf, *len); - - } - - } - - *(uint32_t*)(new_buf + pos + 8 + chunk_len) = real_cksum; - - } - - /* Skip the entire chunk and move to the next one. */ - - pos += 12 + chunk_len; - - } - - return new_buf; - -} - diff --git a/experimental/socket_fuzzing/Makefile b/experimental/socket_fuzzing/Makefile deleted file mode 100644 index 0191ba53..00000000 --- a/experimental/socket_fuzzing/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# -# american fuzzy lop++ - socket_fuzz -# ---------------------------------- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# - -.PHONY: all install clean - -PREFIX ?= /usr/local -BIN_PATH = $(PREFIX)/bin -HELPER_PATH = $(PREFIX)/lib/afl - -CFLAGS = -fPIC -Wall -Wextra -LDFLAGS = -shared - -ifneq "$(filter Linux GNU%,$(shell uname))" "" - LDFLAGS += -ldl -endif - -all: socketfuzz32.so socketfuzz64.so - -socketfuzz32.so: socketfuzz.c - -$(CC) -m32 $(CFLAGS) $^ $(LDFLAGS) -o $@ || echo "socketfuzz32 build failure (that's fine)" - -socketfuzz64.so: socketfuzz.c - -$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ - -install: socketfuzz32.so socketfuzz64.so - install -d -m 755 $(DESTDIR)$(HELPER_PATH)/ - if [ -f socketfuzz32.so ]; then set -e; install -m 755 socketfuzz32.so $(DESTDIR)$(HELPER_PATH)/; fi - install -m 755 socketfuzz64.so $(DESTDIR)$(HELPER_PATH)/ - -clean: - rm -f socketfuzz32.so socketfuzz64.so diff --git a/experimental/socket_fuzzing/README.md b/experimental/socket_fuzzing/README.md deleted file mode 100644 index 79f28bea..00000000 --- a/experimental/socket_fuzzing/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# socketfuzz - -when you want to fuzz a network service and you can not/do not want to modify -the source (or just have a binary), then this LD_PRELOAD library will allow -for sending input to stdin which the target binary will think is coming from -a network socket. - -This is desock_dup.c from the amazing preeny project -https://github.com/zardus/preeny - -It is packaged in afl++ to have it at hand if needed diff --git a/experimental/socket_fuzzing/socketfuzz.c b/experimental/socket_fuzzing/socketfuzz.c deleted file mode 100644 index 3ec8383b..00000000 --- a/experimental/socket_fuzzing/socketfuzz.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This is desock_dup.c from the amazing preeny project - * https://github.com/zardus/preeny - * - * It is packaged in afl++ to have it at hand if needed - * - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include // -#include // -#include // -#include // -#include -#include -#include -#include -#include -#include -#include -//#include "logging.h" // switche from preeny_info() to fprintf(stderr, "Info: " - -// -// originals -// -int (*original_close)(int); -int (*original_dup2)(int, int); -__attribute__((constructor)) void preeny_desock_dup_orig() { - - original_close = dlsym(RTLD_NEXT, "close"); - original_dup2 = dlsym(RTLD_NEXT, "dup2"); - -} - -int close(int sockfd) { - - if (sockfd <= 2) { - - fprintf(stderr, "Info: Disabling close on %d\n", sockfd); - return 0; - - } else { - - return original_close(sockfd); - - } - -} - -int dup2(int old, int new) { - - if (new <= 2) { - - fprintf(stderr, "Info: Disabling dup from %d to %d\n", old, new); - return 0; - - } else { - - return original_dup2(old, new); - - } - -} - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating accept on %d\n", sockfd); - return 0; - -} - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { - - (void)sockfd; - (void)addr; - (void)addrlen; - fprintf(stderr, "Info: Emulating bind on port %d\n", - ntohs(((struct sockaddr_in *)addr)->sin_port)); - return 0; - -} - -int listen(int sockfd, int backlog) { - - (void)sockfd; - (void)backlog; - return 0; - -} - -int setsockopt(int sockfd, int level, int optid, const void *optdata, - socklen_t optdatalen) { - - (void)sockfd; - (void)level; - (void)optid; - (void)optdata; - (void)optdatalen; - return 0; - -} - 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[a-zA-Z:\-]+)\((?P.*?)\)" - 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("ffffzzzzzzzzzzzz") - in_2 = bytearray("") - out = fuzz(in_1, in_2) - print(out) - -- cgit 1.4.1