Example #1
0
    def loadPayloadFilter(self):
        if self.project.payload_filter == None:
            return True
        if not os.path.exists(self.project.payload_filter):
            log.warn("Payload filter (file: '%s') does not exist!" %
                     self.project.payload_filter)
            return False
        saved_sys_path = sys.path
        try:
            payload_filter_file_without_ext = os.path.splitext(
                self.project.payload_filter)[0]
            payload_filter_module_path = os.path.dirname(
                payload_filter_file_without_ext)
            payload_filter_module_name = os.path.basename(
                payload_filter_file_without_ext)
            sys.path.insert(0, payload_filter_module_path)
            payload_filter_module = __import__(payload_filter_module_name)
            self.payload_filter_function = payload_filter_module.payload_filter_function
            sys.path = saved_sys_path
        except Exception as e:
            sys.path = saved_sys_path
            log.warn("loadPayloadFilter: " + str(e))
            log.debug("Full Stack Trace:\n" + traceback.format_exc())
            return False

        sys.path = saved_sys_path
        return True
Example #2
0
    def checkAndCreateSubfolders(self):
        """
        Check whether alls necessary subdirectories exist in the
        project folder. Create them if necessary.
        """
        if not os.path.exists(self.project_dir):
            log.warn("Project directory '%s' does not exist." % self.project_dir)
            return False

        if not os.path.exists(self.debug_dir):
            os.mkdir(self.debug_dir)

        if os.path.exists(self.debug_dir + "/history"):
            log.debug("Deleting old Debug file: " + self.debug_dir + "/history")
            os.remove(self.debug_dir + "/history")

        #if not os.path.exists(self.coverage_dir):
        #    os.mkdir(self.coverage_dir)

        if not os.path.exists(self.crash_dir):
            os.mkdir(self.crash_dir)

        if not os.path.exists(self.corpus_dir):
            os.mkdir(self.corpus_dir)

        if not os.path.exists(self.corpus_trash_dir):
            os.mkdir(self.corpus_trash_dir)

        return True
Example #3
0
def fuzz(args, fuzzer):
    #TODO (maybe? save the old history file.. do we need this?)
    #    history_file = self.project_dir + "/frida_fuzzer.history"
    #    if os.path.exists(history_file):
    #        backup_file = self.project_dir + time.strftime("/%Y%m%d_%H%M%S_frida_fuzzer.history")
    #        shutil.move(history_file, backup_file)
    #        log.info("Found old history file. Moving it to %s" % backup_file)

    if fuzzer.buildCorpus():
        log.debug("Corpus: " + str(fuzzer.corpus))
        fuzzer.fuzzerLoop()
Example #4
0
    def doReplay(self):
        """
        This function replays the last Session. This function will later implement also probes to test when the process is crashing
        """
        log.info("Starting the full Replay")
        with open(self.project.project_dir + "/frida_fuzzer.history") as fp:
            for line in fp:
                pkt_file, seed = line.split("|")
                try:
                    fuzz_pkt = self.getMutatedPayload(pkt_file,
                                                      int(seed.strip()))
                    if fuzz_pkt == None:
                        continue
                    if self.project.debug_mode:
                        open(self.project.debug_dir + "/history",
                             "a").write("file: {} seed: {} \n{}\n".format(
                                 pkt_file,
                                 seed,
                                 fuzz_pkt,
                             ))
                    coverage = self.getCoverageOfPayload(fuzz_pkt)
                    log.info("Current iteration: " +
                             time.strftime("%Y-%m-%d %H:%M:%S") +
                             " [seed=%d] [file=%s]" %
                             (int(seed.strip()), pkt_file))
                except (frida.TransportError, frida.InvalidOperationError,
                        frida.core.RPCException) as e:
                    log.finish_update("doReplay: Got a frida error: " + str(e))
                    log.debug("Full Stack Trace:\n" + traceback.format_exc())
                    log.finish_update("Current iteration: " +
                                      time.strftime("%Y-%m-%d %H:%M:%S") +
                                      " [seed=%d] [file=%s]" %
                                      (int(seed.strip()), pkt_file))
                    log.finish_update("Server Crashed! Lets narrow it down")
                    #crash_file = self.crash_dir + time.strftime("/%Y%m%d_%H%M%S_crash")
                    #with open(crash_file, "wb") as f:
                    #    f.write(fuzz_pkt)
                    #log.info("Payload is written to " + crash_file)
                    return False

                if coverage == None:
                    log.warn("No coverage was generated for [%d] %s!" %
                             (seed, pkt_file))

        log.warn("Replay did not crash the Server!")
        return False
Example #5
0
    def runPayloadFilterFunction(self, fuzz_pkt):
        """
        Returns a filtered version of the fuzz payload (as bytes object)
        or None if the payload should not be used by the fuzzer.
        The payload is first passed through the user-provided payload
        filter (if specified). The filter may modify the payload before
        returning or decide to not return any payload (None) in which
        case the fuzzer should skip the payload.
        """
        if self.payload_filter_function != None:
            try:
                fuzz_pkt = self.payload_filter_function(fuzz_pkt)
            except Exception as e:
                log.warn("The payload filter '%s' caused an exception: %s" %
                         (self.project.payload_filter, str(e)))
                log.debug("Full Stack Trace:\n" + traceback.format_exc())
            if not isinstance(fuzz_pkt, bytes) and fuzz_pkt != None:
                log.warn(
                    "The payload filter '%s' returned unsupported type: '%s'."
                    % (self.project.payload_filter, str(type(fuzz_pkt))))
                return None

        return fuzz_pkt
Example #6
0
    def doIteration(self, seed=None, corpus=None):
        if seed == None:
            seed = self.project.seed
        if corpus == None:
            corpus = self.corpus

        start_time = time.time()
        for pkt_file in corpus:
            log.update("[seed=%d] " % seed +
                       time.strftime("%Y-%m-%d %H:%M:%S") + " %s" % pkt_file)
            fuzz_pkt = self.getMutatedPayload(pkt_file, seed)
            if fuzz_pkt == None:
                continue

            # Writing History file for replaying
            open(self.project.project_dir + "/frida_fuzzer.history",
                 "a").write(str(pkt_file) + "|" + str(seed) + "\n")

            try:
                coverage = self.getCoverageOfPayload(fuzz_pkt)
            except (frida.TransportError, frida.InvalidOperationError,
                    frida.core.RPCException) as e:
                log.warn("doIteration: Got a frida error: " + str(e))
                log.debug("Full Stack Trace:\n" + traceback.format_exc())
                log.info("Current iteration: " +
                         time.strftime("%Y-%m-%d %H:%M:%S") +
                         " [seed=%d] [file=%s]" % (seed, pkt_file))
                crash_file = self.project.crash_dir + time.strftime(
                    "/%Y%m%d_%H%M%S_crash")
                with open(
                        crash_file + "_" + str(self.active_target.process_pid),
                        "wb") as f:
                    f.write(fuzz_pkt)
                log.info("Payload is written to " + crash_file)
                self.project.crashes += 1
                return False

            if coverage == None:
                log.warn("No coverage was generated for [%d] %s!" %
                         (seed, pkt_file))
                continue

            if not coverage.issubset(self.accumulated_coverage):
                # New basic blocks covered!
                log.info("Found new path: [%d] %s" % (seed, pkt_file))
                newfile = open(
                    self.project.corpus_dir + "/" + str(seed) + "_" +
                    pkt_file.split("/")[-1], "wb")
                newfile.write(fuzz_pkt)
                newfile.close()

                cov_file = self.project.coverage_dir + "/" + str(
                    seed) + "_" + pkt_file.split("/")[-1]
                write_drcov_file(self.active_target.modules, coverage,
                                 cov_file)
                write_drcov_file(
                    self.active_target.modules,
                    coverage.difference(self.accumulated_coverage),
                    cov_file + "_diff")

                self.project.last_new_path = seed
                self.accumulated_coverage = self.accumulated_coverage.union(
                    coverage)
                write_drcov_file(
                    self.active_target.modules, self.accumulated_coverage,
                    self.project.coverage_dir + "/accumulated_coverage.drcov")

            self.total_executions += 1

        end_time = time.time()
        speed = len(corpus) / (end_time - start_time)
        avg_speed = self.total_executions / (end_time - self.start_time)

        log.finish_update(
            "[seed=%d] speed=[%3d exec/sec (avg: %d)] coverage=[%d bblocks] corpus=[%d files] "
            "last new path: [%d] crashes: [%d]" %
            (seed, speed, avg_speed, len(self.accumulated_coverage),
             len(corpus), self.project.last_new_path, self.project.crashes))
        return True
Example #7
0
    def getCoverageOfPayload(self, payload, timeout=0.04, retry=0):
        """
        Sends of the payload and checks the returned coverage.
        If the payload_filter was specified by the user, the payload
        will first be passed through it.
        All targets will then be checked for coverage. The function only
        succeeds if just one target has produced a coverage.

        Important:
            Frida appears to have a bug sometimes in collecting traces with the
            stalker.. no idea how to fix this yet.. hence we do a retry. This
            can however screw up the replay functionality and should be fixed
            in the future.

        Arguments:
            payload {bytes} -- payload which shall be sent to the target

        Keyword Arguments:
            timeout {float} -- [description] (default: {0.1})
            retry {int} -- [description] (default: {5})

        Returns:
            {set} -- set of basic blocks covered by the payload
        """

        payload = self.runPayloadFilterFunction(payload)
        if payload == None:
            return set()

        cov = None
        cnt = 0
        while cnt <= retry:
            # Clear coverage info in all targets:
            for target in self.targets:
                target.frida_script.exports.clearcoverage()

            # Send payload
            if self.project.fuzz_in_process:
                self.sendFuzzPayloadInProcess(payload)
            else:
                self.sendFuzzPayload(payload)

            # Wait for timeout seconds for any of the stalkers to get attached
            target, stalker_attached, stalker_finished = self.waitForCoverage(
                timeout)

            if target != None:
                # Found a target that has attached their stalker. Wait for the stalker
                # to finish and then extract the coverage.
                # Wait for 1 second <- maybe this should be adjusted / configurable ?
                start = time.time()
                while not stalker_finished and (time.time() - start) < 1:
                    stalker_attached, stalker_finished = target.frida_script.exports.checkstalker(
                    )

                if not stalker_finished:
                    log.info(
                        "getCoverageOfPayload: Stalker did not finish after 1 second!"
                    )
                    break

                cov = target.frida_script.exports.getcoverage()
                if cov != None and len(cov) > 0:
                    break

            else:
                # None of the targets' function was hit. next try..
                cnt += 1

        if cov == None or len(cov) == 0:
            log.debug("getCoverageOfPayload: got nothing!")
            return set()

        return parse_coverage(cov, self.active_target.watched_modules)