def analyze_crasher(cfg, crash): ''' Runs multiple analyses and collects data about a crash. Returns a list of other crashes encountered during the process of analyzing the current crash. @param cfg: A BFF config object @param crash: A crash object @return: a list of Crasher objects ''' other_crashers_found = [] dbg_out_file_orig = crash.dbg.file logger.debug('Original debugger file: %s', dbg_out_file_orig) if cfg.minimizecrashers: # try to reduce the Hamming Distance between the crasher file and the known good seedfile # crash.fuzzedfile will be replaced with the minimized result try: with Minimizer(cfg=cfg, crash=crash, bitwise=False, seedfile_as_target=True, confidence=0.999, tempdir=cfg.local_dir, maxtime=cfg.minimizertimeout) as minimizer: minimizer.go() other_crashers_found.extend(minimizer.other_crashes.values()) except MinimizerError, e: logger.warning( 'Unable to minimize %s, proceeding with original fuzzed crash file: %s', crash.signature, e) minimizer = None
def setUp(self): self.cfg = MockCfg() self.crash = MockCrasher() self.tempdir = tempfile.mkdtemp(prefix='minimizer_test_') self.crash_dst_dir = tempfile.mkdtemp(prefix='crash_', dir=self.tempdir) (fd, self.logfile) = tempfile.mkstemp(dir=self.tempdir) os.close(fd) os.remove(self.logfile) self.assertFalse(os.path.exists(self.logfile)) certfuzz.minimizer.minimizer_base.debuggers = MockDebugger() self.m = Minimizer(cfg=self.cfg, crash=self.crash, crash_dst_dir=self.crash_dst_dir, logfile=self.logfile, tempdir=self.tempdir)
def main(): debuggers.verify_supported_platform() from optparse import OptionParser hdlr = logging.StreamHandler() logger.addHandler(hdlr) usage = "usage: %prog [options] fuzzedfile" parser = OptionParser(usage) parser.add_option('', '--debug', dest='debug', action='store_true', help='Enable debug messages (overrides --verbose)') parser.add_option('', '--verbose', dest='verbose', action='store_true', help='Enable verbose messages') parser.add_option('-t', '--target', dest='target', help='the file to minimize to (typically the seedfile)') parser.add_option('-o', '--outdir', dest='outdir', help='dir to write output to') parser.add_option('-s', '--stringmode', dest='stringmode', action='store_true', help='minimize to a string rather than to a target file') parser.add_option('-x', '--preferx', dest='prefer_x_target', action='store_true', help='Minimize to \'x\' characters instead of Metasploit string pattern') parser.add_option('-f', '--faddr', dest='keep_uniq_faddr', action='store_true', help='Use exception faulting addresses as part of crash signature') parser.add_option('-b', '--bitwise', dest='bitwise', action='store_true', help='if set, use bitwise hamming distance. Default is bytewise') parser.add_option('-c', '--confidence', dest='confidence', help='The desired confidence level (default: 0.999)') parser.add_option('-g', '--target-size-guess', dest='initial_target_size', help='A guess at the minimal value (int)') parser.add_option('', '--config', default='configs/foe.yaml', dest='config', help='path to the configuration file to use') parser.add_option('', '--timeout', dest='timeout', metavar='N', type='int', default=0, help='Stop minimizing after N seconds (default is 0, never time out).') parser.add_option('-k', '--keepothers', dest='keep_other_crashes', action='store_true', help='Keep other crash hashes encountered during minimization') (options, args) = parser.parse_args() if options.debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) if options.config: cfg_file = options.config else: cfg_file = "../conf.d/bff.cfg" logger.debug('Config file: %s', cfg_file) if options.stringmode and options.target: parser.error('Options --stringmode and --target are mutually exclusive.') # Set some default options. Fast and loose if in string mode # More precise with minimize to seedfile if not options.confidence: if options.stringmode: options.confidence = 0.5 else: options.confidence = 0.999 if not options.initial_target_size: if options.stringmode: options.initial_target_size = 100 else: options.initial_target_size = 1 if options.confidence: try: options.confidence = float(options.confidence) except: parser.error('Confidence must be a float.') if not 0.0 < options.confidence < 1.0: parser.error('Confidence must be in the range 0.0 < c < 1.0') confidence = options.confidence if options.outdir: outdir = options.outdir else: mydir = os.path.dirname(os.path.abspath(__file__)) parentdir = os.path.abspath(os.path.join(mydir, '..')) outdir = os.path.abspath(os.path.join(parentdir, 'minimizer_out')) filetools.make_directories(outdir) if len(args) and os.path.exists(args[0]): fuzzed_file = BasicFile(args[0]) logger.info('Fuzzed file is %s', fuzzed_file) else: parser.error('fuzzedfile must be specified') config = Config(cfg_file).config cfg = _create_minimizer_cfg(config) if options.target: seedfile = BasicFile(options.target) else: seedfile = None min2seed = not options.stringmode filename_modifier = '' retries = 0 debugger_class = msec.MsecDebugger template = string.Template(config['target']['cmdline_template']) cmd_as_args = get_command_args_list(template, fuzzed_file.path)[1] with FoeCrash(template, seedfile, fuzzed_file, cmd_as_args, None, debugger_class, config['debugger'], outdir, options.keep_uniq_faddr, config['target']['program'], retries) as crash: filetools.make_directories(crash.tempdir) logger.info('Copying %s to %s', fuzzed_file.path, crash.tempdir) filetools.copy_file(fuzzed_file.path, crash.tempdir) minlog = os.path.join(outdir, 'min_log.txt') with Minimizer(cfg=cfg, crash=crash, crash_dst_dir=outdir, seedfile_as_target=min2seed, bitwise=options.bitwise, confidence=confidence, tempdir=outdir, logfile=minlog, maxtime=options.timeout, preferx=options.prefer_x_target) as minimize: minimize.save_others = options.keep_other_crashes minimize.target_size_guess = int(options.initial_target_size) minimize.go() if options.stringmode: logger.debug('x character substitution') length = len(minimize.fuzzed) if options.prefer_x_target: #We minimized to 'x', so we attempt to get metasploit as a freebie targetstring = list(text.metasploit_pattern_orig(length)) filename_modifier = '-mtsp' else: #We minimized to metasploit, so we attempt to get 'x' as a freebie targetstring = list('x' * length) filename_modifier = '-x' fuzzed = list(minimize.fuzzed) for idx in minimize.bytemap: logger.debug('Swapping index %d', idx) targetstring[idx] = fuzzed[idx] filename = ''.join((crash.fuzzedfile.root, filename_modifier, crash.fuzzedfile.ext)) metasploit_file = os.path.join(crash.tempdir, filename) f = open(metasploit_file, 'wb') try: f.writelines(targetstring) finally: f.close() crash.copy_files(outdir) crash.clean_tmpdir()
crash.signature, e) minimizer = None touch_watchdog_file(cfg) # calculate the hamming distances for this crash # between the original seedfile and the minimized fuzzed file crash.calculate_hamming_distances() if cfg.minimize_to_string: # Minimize to a string of 'x's # crash.fuzzedfile will be replaced with the minimized result try: with Minimizer(cfg=cfg, crash=crash, bitwise=False, seedfile_as_target=False, confidence=0.9, tempdir=cfg.local_dir, maxtime=cfg.minimizertimeout) as min2string: min2string.go() other_crashers_found.extend(min2string.other_crashes.values()) except MinimizerError, e: logger.warning( 'Unable to minimize %s, proceeding with original fuzzed crash file: %s', crash.signature, e) min2string = None touch_watchdog_file(cfg) # get one last debugger output for the newly minimized file if crash.pc_in_function: # change the debugger template