Example #1
0
    def choose_afl(self):
        """
        Chooses the right AFL and sets up some environment.
        """

        # set up the AFL path
        p = angr.Project(self.target)
        target_os = p.loader.main_object.os
        afl_dir = shellphish_afl.afl_dir(target_os)

        if target_os == 'cgc':
            afl_path_var = shellphish_afl.afl_path_var('cgc')
        else:
            afl_path_var = shellphish_afl.afl_path_var(p.arch.qemu_name)
            directory = None
            if p.arch.qemu_name == "aarch64":
                directory = "arm64"
            if p.arch.qemu_name == "i386":
                directory = "i386"
            if p.arch.qemu_name == "x86_64":
                directory = "x86_64"
            if p.arch.qemu_name == "mips":
                directory = "mips"
            if p.arch.qemu_name == "mipsel":
                directory = "mipsel"
            if p.arch.qemu_name == "ppc":
                directory = "powerpc"
            if p.arch.qemu_name == "arm":
                # some stuff qira uses to determine the which libs to use for arm
                with open(self.target, "rb") as f:
                    progdata = f.read(0x800)
                if "/lib/ld-linux.so.3" in progdata:
                    directory = "armel"
                elif "/lib/ld-linux-armhf.so.3" in progdata:
                    directory = "armhf"

            if directory is None:
                l.warning("architecture \"%s\" has no installed libraries",
                          p.arch.qemu_name)
            else:
                libpath = os.path.join(afl_dir, "..", "fuzzer-libs", directory)

                l.debug("exporting QEMU_LD_PREFIX of '%s'", libpath)
                os.environ['QEMU_LD_PREFIX'] = libpath

        # set environment variable for the AFL_PATH
        os.environ['AFL_PATH'] = afl_path_var

        # return the AFL path
        return shellphish_afl.afl_bin(target_os)
Example #2
0
    def init_afl_config(binary_path, is_multicb=False):
        """
        Returns AFL_PATH and AFL_DIR, if AFL_PATH is set in os.environ it returns that, if not it attempts to auto-detect
        :param binary_path:
        :return: afl_path_var, afl_dir, qemu_arch_name: afl_path_var is location of qemu_trace to use, afl_dir is the location of the afl binaries, qemu_arch_name is the name of the binary's architecture
        """

        if Phuzzer.afl_bin_dir is not None:
            return Phuzzer.afl_bin_dir, Phuzzer.qemu_arch_name

        if "AFL_PATH" in os.environ:
            Phuzzer.afl_bin_dir = os.environ["AFL_PATH"]
        else:

            if not ANGR_INSTALLED:
                raise ModuleNotFoundError(
                    "AFL_PATH was found in enviornment variables and angr is not installed."
                )
            if not SHELLPHISH_AFL_INSTALLED:
                raise ModuleNotFoundError(
                    "AFL_PATH was found in enviornment variables and either shellphish_afl is not installed."
                )
            try:
                p = angr.Project(binary_path)
                Phuzzer.qemu_arch_name = p.arch.qemu_name
                tracer_id = 'cgc' if p.loader.main_object.os == 'cgc' else p.arch.qemu_name
                if is_multicb:
                    tracer_id = 'multi-{}'.format(tracer_id)

                afl_path_var = shellphish_afl.afl_path_var(tracer_id)
                os.environ['AFL_PATH'] = afl_path_var

                Phuzzer.afl_bin_dir = shellphish_afl.afl_dir(tracer_id)
                print(f"afl_dir {Phuzzer.afl_bin_dir}")

            except Exception:

                traceback.format_exc()
                raise ModuleNotFoundError(
                    "AFL_PATH was found in enviornment variables and "
                    "either angr or shellphish_afl is not installed.")

        return Phuzzer.afl_bin_dir, Phuzzer.qemu_arch_name
Example #3
0
    def __init__(self, binary_path, testcase):
        """
        :param binary_path: path to the binary which the testcase applies to
        :param testcase: string representing the contents of the testcase
        """

        self.binary_path = binary_path
        self.testcase = testcase

        Fuzzer._perform_env_checks()

        self.base = Fuzzer._get_base()
        l.debug("got base dir %s", self.base)

        # unfortunately here is some code reuse between Fuzzer and Minimizer
        p = angr.Project(self.binary_path)
        tracer_id = 'cgc' if p.loader.main_object.os == 'cgc' else p.arch.qemu_name
        self.tmin_path = os.path.join(shellphish_afl.afl_dir(tracer_id),
                                      "afl-tmin")
        self.afl_path_var = shellphish_afl.afl_path_var(tracer_id)

        l.debug("tmin_path: %s", self.tmin_path)
        l.debug("afl_path_var: %s", self.afl_path_var)

        os.environ['AFL_PATH'] = self.afl_path_var

        # create temp
        self.work_dir = tempfile.mkdtemp(prefix='tmin-', dir='/tmp/')

        # flag for work directory removal
        self._removed = False

        self.input_testcase = os.path.join(self.work_dir, 'testcase')
        self.output_testcase = os.path.join(self.work_dir, 'minimized_result')

        l.debug("input_testcase: %s", self.input_testcase)
        l.debug("output_testcase: %s", self.output_testcase)

        # populate contents of input testcase
        with open(self.input_testcase, 'wb') as f:
            f.write(testcase)
Example #4
0
    def cmin(self, fuzzer_prefix="fuzzer-"):
        afl_dir = shellphish_afl.afl_dir(self.target_os)
        afl_path_var = shellphish_afl.afl_path_var(self.target_arch)
        os.environ['AFL_PATH'] = afl_path_var

        # create queue.all dir
        if os.path.exists(self.queue_all_dir):
            shutil.rmtree(self.queue_all_dir)
        os.makedirs(self.queue_all_dir)

        # copy all seeds
        for seed in glob(f"{self.work_dir}/{fuzzer_prefix}*/queue/id:*"):
            id = seed.split("/")[-1]
            fuzzer = seed.split("/queue")[0].split("/")[-1]
            shutil.copy(seed, f"{self.queue_all_dir}/{{{fuzzer}}}{id}")

        # create queue.cmin
        if os.path.exists(self.queue_min_dir):
            shutil.rmtree(self.queue_min_dir)
        os.makedirs(self.queue_min_dir)

        # create cmd args
        cmin_path = os.path.join(afl_dir, "afl-cmin")
        args = [cmin_path]

        args += ["-i", self.queue_all_dir]
        args += ["-o", self.queue_min_dir]
        args += ["-m", self.memory]
        args += ["-Q"]
        args += ["--"]
        args += [self.target]
        args += self.target_opts

        #l.debug(f"execing: AFL_PATH={afl_path_var} {' '.join(args)}")
        return subprocess.Popen(args,
                                env=os.environ,
                                stdout=subprocess.DEVNULL,
                                stderr=subprocess.DEVNULL,
                                close_fds=True)
Example #5
0
    def __init__(self, binary_path, work_dir, afl_count=1, library_path=None, time_limit=None,
            target_opts=None, extra_opts=None, create_dictionary=False,
            seeds=None, crash_mode=False, never_resume=False):
        '''
        :param binary_path: path to the binary to fuzz. List or tuple for multi-CB.
        :param work_dir: the work directory which contains fuzzing jobs, our job directory will go here
        :param afl_count: number of AFL jobs total to spin up for the binary
        :param library_path: library path to use, if none is specified a default is chosen
        :param timelimit: amount of time to fuzz for, has no effect besides returning True when calling timed_out
        :param seeds: list of inputs to seed fuzzing with
        :param target_opts: extra options to pass to the target
        :param extra_opts: extra options to pass to AFL when starting up
        :param crash_mode: if set to True AFL is set to crash explorer mode, and seed will be expected to be a crashing input
        :param never_resume: never resume an old fuzzing run, even if it's possible
        '''

        self.binary_path    = binary_path
        self.work_dir       = work_dir
        self.afl_count      = afl_count
        self.time_limit     = time_limit
        self.library_path   = library_path
        self.target_opts    = [ ] if target_opts is None else target_opts
        self.crash_mode     = crash_mode

        Fuzzer._perform_env_checks()

        if isinstance(binary_path,basestring):
            self.is_multicb = False
            self.binary_id = os.path.basename(binary_path)
        elif isinstance(binary_path,(list,tuple)):
            self.is_multicb = True
            self.binary_id = os.path.basename(binary_path[0])
        else:
            raise ValueError("Was expecting either a string or a list/tuple for binary_path! It's {} instead.".format(type(binary_path)))

        # sanity check crash mode
        if self.crash_mode:
            if seeds is None:
                raise ValueError("Seeds must be specified if using the fuzzer in crash mode")
            l.info("AFL will be started in crash mode")

        self.seeds          = ["fuzz"] if seeds is None or len(seeds) == 0 else seeds

        self.job_dir  = os.path.join(self.work_dir, self.binary_id)
        self.in_dir   = os.path.join(self.job_dir, "input")
        self.out_dir  = os.path.join(self.job_dir, "sync")

        # sanity check extra opts
        self.extra_opts = extra_opts
        if self.extra_opts is not None:
            if not isinstance(self.extra_opts, list):
                raise ValueError("extra_opts must be a list of command line arguments")

        # base of the fuzzer package
        self.base = Fuzzer._get_base()

        self.start_time       = int(time.time())
        # create_dict script
        self.create_dict_path = os.path.join(self.base, "bin", "create_dict.py")
        # afl dictionary
        self.dictionary       = None
        # processes spun up
        self.procs            = [ ]
        # start the fuzzer ids at 0
        self.fuzz_id          = 0
        # test if we're resuming an old run
        self.resuming         = bool(os.listdir(self.out_dir)) if os.path.isdir(self.out_dir) else False
        # has the fuzzer been turned on?
        self._on = False

        if never_resume and self.resuming:
            l.info("could resume, but starting over upon request")
            shutil.rmtree(self.job_dir)
            self.resuming = False

        if self.is_multicb:
            # Where cgc/setup's Dockerfile checks it out
            # NOTE: 'afl/fakeforksrv' serves as 'qemu', as far as AFL is concerned
            #       Will actually invoke 'fakeforksrv/multicb-qemu'
            #       This QEMU cannot run standalone (always speaks the forkserver "protocol"),
            #       but 'fakeforksrv/run_via_fakeforksrv' allows it.
            # XXX: There is no driller/angr support, and probably will never be.
            self.afl_path = shellphish_afl.afl_bin('multi-cgc')
            self.afl_path_var = shellphish_afl.afl_path_var('multi-cgc')
        else:

            p = angr.Project(binary_path)

            self.os = p.loader.main_bin.os

            afl_dir               = shellphish_afl.afl_dir(self.os)

            # the path to AFL capable of calling driller
            self.afl_path         = shellphish_afl.afl_bin(self.os)

            self.afl_path_var     = shellphish_afl.afl_path_var(p.arch.qemu_name)

            # set up libraries
            self._export_library_path(p)

        self.qemu_dir         = self.afl_path_var

        l.debug("self.start_time: %r", self.start_time)
        l.debug("self.afl_path: %s", self.afl_path)
        l.debug("self.afl_path_var: %s", self.afl_path_var)
        l.debug("self.qemu_dir: %s", self.qemu_dir)
        l.debug("self.binary_id: %s", self.binary_id)
        l.debug("self.work_dir: %s", self.work_dir)
        l.debug("self.resuming: %s", self.resuming)

        # if we're resuming an old run set the input_directory to a '-'
        if self.resuming:
            l.info("[%s] resuming old fuzzing run", self.binary_id)
            self.in_dir = "-"

        else:
            # create the work directory and input directory
            try:
                os.makedirs(self.in_dir)
            except OSError:
                l.warning("unable to create in_dir \"%s\"", self.in_dir)

            # populate the input directory
            self._initialize_seeds()

        # look for a dictionary
        dictionary_file = os.path.join(self.job_dir, "%s.dict" % self.binary_id)
        if os.path.isfile(dictionary_file):
            self.dictionary = dictionary_file

        # if a dictionary doesn't exist and we aren't resuming a run, create a dict
        elif not self.resuming:
            # call out to another process to create the dictionary so we can
            # limit it's memory
            if create_dictionary:
                if self._create_dict(dictionary_file):
                    self.dictionary = dictionary_file
                else:
                    # no luck creating a dictionary
                    l.warning("[%s] unable to create dictionary", self.binary_id)

        # set environment variable for the AFL_PATH
        os.environ['AFL_PATH'] = self.afl_path_var
Example #6
0
import glob
import shutil
import hashlib
#import tarfile
import subprocess
import shellphish_afl

if __name__ == "__main__":
    # minimizing code
    p = os.path.join("/tmp/", "afl_seeds")
    try:
        os.mkdir(p)
    except OSError:
        pass

    afl_path_var = shellphish_afl.afl_path_var('cgc')
    afl_path = shellphish_afl.afl_bin('cgc')
    BIN = sys.argv[1]
    IN_DIR = sys.argv[2]
    OUT_DIR = sys.argv[3]
    os.environ['AFL_PATH'] = afl_path_var

    # set afl-showmap (super hacky)
    shutil.copy2(os.path.join(afl_path_var, "../../afl-showmap"), afl_path_var)

    # run afl-cmin
    print "### cmin time (binary %s)" % BIN

    args = [os.path.join(os.path.dirname(__file__), "../afl-cmin")]

    print "collecting seeds"
Example #7
0
    def __init__(self, binary_path, testcase, timeout=None):
        """
        :param binary_path: path to the binary which the testcase applies to
        :param testcase: string representing the contents of the testcase
        :param timeout: millisecond timeout
        """

        self.binary_path = binary_path
        self.testcase = testcase
        self.timeout = None

        if isinstance(binary_path, str):
            self.is_multicb = False
            self.binaries = [binary_path]
        elif isinstance(binary_path, (list, tuple)):
            self.is_multicb = True
            self.binaries = binary_path
        else:
            raise ValueError(
                "Was expecting either a string or a list/tuple for binary_path! "
                "It's {} instead.".format(type(binary_path)))

        if timeout is not None:
            if isinstance(timeout, int):
                self.timeout = str(timeout)
            elif isinstance(timeout, (str)):
                self.timeout = timeout
            elif isinstance(timeout, (bytes)):
                self.timeout = timeout.decode('utf-8')
            else:
                raise ValueError("timeout param must be of type int or str")

        # will be set by showmap's return code
        self.causes_crash = False

        AFL.check_environment()

        # unfortunately here is some code reuse between Phuzzer and Minimizer (and Showmap!)
        p = angr.Project(self.binaries[0])
        tracer_id = 'cgc' if p.loader.main_object.os == 'cgc' else p.arch.qemu_name
        if self.is_multicb:
            tracer_id = 'multi-{}'.format(tracer_id)

        self.showmap_path = os.path.join(shellphish_afl.afl_dir(tracer_id),
                                         "afl-showmap")
        self.afl_path_var = shellphish_afl.afl_path_var(tracer_id)

        l.debug("showmap_path: %s", self.showmap_path)
        l.debug("afl_path_var: %s", self.afl_path_var)

        os.environ['AFL_PATH'] = self.afl_path_var

        # create temp
        self.work_dir = tempfile.mkdtemp(prefix='showmap-', dir='/tmp/')

        # flag for work directory removal
        self._removed = False

        self.input_testcase = os.path.join(self.work_dir, 'testcase')
        self.output = os.path.join(self.work_dir, 'out')

        l.debug("input_testcase: %s", self.input_testcase)
        l.debug("output: %s", self.output)

        # populate contents of input testcase
        with open(self.input_testcase, 'wb') as f:
            f.write(testcase)
Example #8
0
    def choose_afl(self):
        """
        Chooses the right AFL and sets up some environment.
        """

        # set up the AFL path
        self.proj = self.proj or angr.Project(self.target,
                                              auto_load_libs=False)
        self.target_os = self.proj.loader.main_object.os
        self.target_arch = self.proj.arch.qemu_name

        afl_dir = shellphish_afl.afl_dir(self.target_os)
        afl_path_var = shellphish_afl.afl_path_var(self.target_arch)

        directory = None
        if self.target_arch == "i386":
            directory = "i386"
            qemu_base = 0x40000000
        if self.target_arch == "x86_64":
            directory = "x86_64"
            qemu_base = 0x4000000000

        if directory is None:
            l.warning("architecture \"%s\" has no installed libraries",
                      self.target_arch)
        elif False:  # for now, avoid mixing up libs
            libpath = os.path.join(afl_dir, "..", "fuzzer-libs", directory)

            l.debug("exporting QEMU_LD_PREFIX=%s", libpath)
            os.environ['QEMU_LD_PREFIX'] = libpath

        l.debug("exporting AFL_PATH=%s", afl_path_var)
        os.environ['AFL_PATH'] = afl_path_var

        libcompcovpath = os.path.join(afl_dir, "libcompcov.so")
        libdislocatorpath = os.path.join(afl_dir, "libdislocator.so")
        afl_preload = " ".join([libcompcovpath, libdislocatorpath])
        afl_preload = f"'{afl_preload}'"
        l.debug("exporting AFL_PRELOAD=%s", afl_preload)
        os.environ['AFL_PRELOAD'] = afl_preload

        l.debug("exporting AFL_COMPCOV_LEVEL=2")
        os.environ[
            'AFL_COMPCOV_LEVEL'] = "2"  # CompareCoverage instrumentation

        # set entrypoint
        main = self.proj.loader.find_symbol('main')
        entrypoint = main.rebased_addr if main else self.proj.entry

        if self.proj.loader.main_object.pic:
            entrypoint = self.proj.loader.main_object.addr_to_offset(
                entrypoint)
            entrypoint += qemu_base

        #l.debug("exporting QEMU_SET_ENV=%s", f"QEMU_GUEST_BASE={hex(qemu_base)}")
        os.environ['QEMU_SET_ENV'] = f"QEMU_GUEST_BASE={hex(qemu_base)}"

        l.debug("exporting AFL_ENTRYPOINT=%s", hex(entrypoint))
        os.environ['AFL_ENTRYPOINT'] = hex(entrypoint)

        #if not self.proj.loader.main_object.pic:
        #l.debug("exporting AFL_QEMU_PERSISTENT_ADDR=%s", hex(entrypoint))
        #os.environ['AFL_QEMU_PERSISTENT_ADDR'] = hex(entrypoint)
        #os.environ['AFL_QEMU_PERSISTENT_GPR'] = "1"

        # return the AFL path
        return shellphish_afl.afl_bin(self.target_os)
Example #9
0
 def choose_afl(self):
     os.environ['AFL_PATH'] = shellphish_afl.afl_path_var('multi-cgc')
     return shellphish_afl.afl_bin('multi-cgc')
Example #10
0
 def __init__(self, targets, **kwargs):
     super().__init__(targets[0], **kwargs)
     self.afl_path = shellphish_afl.afl_bin('multi-cgc')
     self.afl_path_var = shellphish_afl.afl_path_var('multi-cgc')
     self.timeout = 1000 * len(targets)
     self.target_opts = targets[1:]