diff --git a/mayhem/Dockerfile b/mayhem/Dockerfile new file mode 100644 index 0000000..f383f83 --- /dev/null +++ b/mayhem/Dockerfile @@ -0,0 +1,8 @@ +# Build Stage +FROM bcapuano/atheris:2.1.1-python3.10 + +ADD . /src +WORKDIR /src +RUN python3 -m pip install . + +CMD ["/src/mayhem/fuzz_ioc.py"] diff --git a/mayhem/Mayhemfile b/mayhem/Mayhemfile new file mode 100644 index 0000000..4277188 --- /dev/null +++ b/mayhem/Mayhemfile @@ -0,0 +1,8 @@ +project: iocfinder +target: fuzz-ioc +testsuite: + - file://mayhem/testsuite + +cmds: + - cmd: /src/mayhem/fuzz_ioc.py + libfuzzer: true diff --git a/mayhem/fuzz_helpers.py b/mayhem/fuzz_helpers.py new file mode 100644 index 0000000..748bd15 --- /dev/null +++ b/mayhem/fuzz_helpers.py @@ -0,0 +1,156 @@ +# Atheris fuzzing utilities written by Bailey Capuano +import io +import tempfile +import atheris +import contextlib +from typing import List, Set, Dict, Tuple, Any + + +def _handle_type(fdp: atheris.FuzzedDataProvider, ty_queue: List[type]) -> Any: + """ + Handles the fuzzing of a single type. + :param fdp: FuzzedDataProvider object + :param ty_queue: The current stack of types to be used for fuzzing + :return: The fuzzed element + """ + if not ty_queue: + return None + ty = ty_queue.pop(0) + if ty is bytes: + return fdp.ConsumeBytes(fdp.ConsumeIntInRange(0, 100)) + elif ty is bytearray: + return bytearray(fdp.ConsumeBytes(fdp.ConsumeIntInRange(0, 100))) + elif ty is str: + return fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(0, 100)) + elif ty is float: + return fdp.ConsumeRegularFloat() + elif ty is bool: + return fdp.ConsumeBool() + elif ty is int: + return fdp.ConsumeInt(4) + elif ty is dict: + return build_fuzz_dict(fdp, ty_queue) + elif ty is list: + return build_fuzz_list(fdp, ty_queue) + elif ty is set: + return build_fuzz_set(fdp, ty_queue) + elif ty is tuple: + return build_fuzz_tuple(fdp, ty_queue) + else: + return None + + +def build_fuzz_list(fdp: atheris.FuzzedDataProvider, ty_queue: List[type]) -> List[Any]: + """ + Builds a list with fuzzer-defined elements. + :param fdp: FuzzedDataProvider object + :param ty_queue: The current stack of types to be used for fuzzing + :return: The list + """ + if not ty_queue: + return [] + elem_count = fdp.ConsumeIntInRange(1, 5) + gen_list = [] + + for _ in range(elem_count): + passed_queue = ty_queue.copy() + elem = _handle_type(fdp, passed_queue) + if elem is not None: + gen_list.append(elem) + ty_queue.pop(0) # Pop elem type + + return gen_list + + +def build_fuzz_set(fdp: atheris.FuzzedDataProvider, ty_queue: List[type]) -> Set[Any]: + """ + Builds a set with fuzzer-defined elements. + :param fdp: FuzzedDataProvider object + :param ty_queue: The current stack of types to be used for fuzzing + :return: The set + """ + if not ty_queue: + return set() + ty_queue.insert(0, list) + + fuzz_list = _handle_type(fdp, ty_queue) + return set(fuzz_list) + + +def build_fuzz_tuple(fdp: atheris.FuzzedDataProvider, ty_queue: List[type]) -> Tuple[Any]: + """ + Builds a tuple with fuzzer-defined elements. + :param fdp: FuzzedDataProvider object + :param ty_queue: The current stack of types to be used for fuzzing + :return: The tuple + """ + if not ty_queue: + return tuple() + ty_queue.insert(0, list) + + fuzz_list = _handle_type(fdp, ty_queue) + return tuple(fuzz_list) + + +def build_fuzz_dict(fdp: atheris.FuzzedDataProvider, ty_queue: List[type]) -> Dict[Any, Any]: + """ + Builds a dictionary with fuzzer-defined keys and values. + :param fdp: FuzzedDataProvider object + :param ty_queue: The current stack of types to be used for fuzzing + :return: The dictionary + """ + if not ty_queue: + return {} + + ty_queue.insert(0, list) # handle key + key_list = _handle_type(fdp, ty_queue) + ty_queue.insert(0, list) # handle key + val_list = _handle_type(fdp, ty_queue) + + # Shrink lists to match + if len(key_list) > len(val_list): + key_list = key_list[:len(val_list)] + elif len(val_list) > len(key_list): + val_list = val_list[:len(key_list)] + + return dict(zip(key_list, val_list)) + + +class EnhancedFuzzedDataProvider(atheris.FuzzedDataProvider): + def ConsumeRandomBytes(self) -> bytes: + return self.ConsumeBytes(self.ConsumeIntInRange(0, self.remaining_bytes())) + + def ConsumeRandomString(self) -> str: + return self.ConsumeUnicodeNoSurrogates(self.ConsumeIntInRange(0, self.remaining_bytes())) + + def ConsumeRemainingString(self) -> str: + return self.ConsumeUnicodeNoSurrogates(self.remaining_bytes()) + + def ConsumeRemainingBytes(self) -> bytes: + return self.ConsumeBytes(self.remaining_bytes()) + + @contextlib.contextmanager + def ConsumeMemoryFile(self, all_data: bool = False, as_bytes: bool = True) -> io.BytesIO: + if all_data: + file_data = self.ConsumeRemainingBytes() if as_bytes else self.ConsumeRemainingString() + else: + file_data = self.ConsumeRandomBytes() if as_bytes else self.ConsumeRandomString() + + file = io.BytesIO(file_data) if as_bytes else io.StringIO(file_data) + yield file + file.close() + + @contextlib.contextmanager + def ConsumeTemporaryFile(self, suffix: str, all_data: bool = False, as_bytes: bool = True) -> io.StringIO: + if all_data: + file_data = self.ConsumeRandomBytes() if as_bytes else self.ConsumeRemainingString() + else: + file_data = self.ConsumeRandomBytes() if as_bytes else self.ConsumeRandomString() + + mode = 'w+b' if as_bytes else 'w+' + tfile = tempfile.NamedTemporaryFile(mode=mode, suffix=suffix) + tfile.write(file_data) + tfile.seek(0) + tfile.flush() + yield tfile.name + tfile.close() \ No newline at end of file diff --git a/mayhem/fuzz_ioc.py b/mayhem/fuzz_ioc.py new file mode 100755 index 0000000..68ad008 --- /dev/null +++ b/mayhem/fuzz_ioc.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +import atheris +import sys +import fuzz_helpers + + +with atheris.instrument_imports(): + from user_agents import parse +def TestOneInput(data): + fdp = fuzz_helpers.EnhancedFuzzedDataProvider(data) + user_agent = parse(fdp.ConsumeRemainingString()) + user_agent.is_mobile + user_agent.is_tablet + user_agent.is_touch_capable + user_agent.is_pc + user_agent.is_bot + str(user_agent) +def main(): + atheris.Setup(sys.argv, TestOneInput) + atheris.Fuzz() + + +if __name__ == "__main__": + main() diff --git a/mayhem/testsuite/seed b/mayhem/testsuite/seed new file mode 100644 index 0000000..85a66a2 --- /dev/null +++ b/mayhem/testsuite/seed @@ -0,0 +1 @@ +Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30