def _cache_app(self): logger.debug('cache program') sf = self.seedfile_set.next_item() # Run the program once to cache it into memory fullpathorig = self._full_path_original(sf.path) cmdargs = get_command_args_list( self.config['target']['cmdline_template'], infile=fullpathorig)[1] if 'copyfuzzedto' in self.config['target']: from shutil import copyfile copyfuzzedto = str(self.config['target'].get('copyfuzzedto', '')) logger.debug("Copying seed file to " + copyfuzzedto) copyfile(fullpathorig, copyfuzzedto) if 'postprocessfuzzed' in self.config['target']: postprocessfuzzed = str(self.config['target']['postprocessfuzzed']) logger.debug("Executing postprocess " + postprocessfuzzed) os.system(postprocessfuzzed) logger.info('Invoking %s' % cmdargs) subp.run_with_timer( cmdargs, self.config['runner']['runtimeout'] * 8, self.config['target']['program'], use_shell=False, seeoutput=True, ) # Give target time to die logger.info( 'Please ensure that the target program has just executed successfully' ) time.sleep(10)
def __init__(self, cfg, testcase, outfile=None, timeout=None, **options): logger.debug('Initializing %s', self.__class__.__name__) self.cfg = cfg self.testcase = testcase self.cmdargs = get_command_args_list( self.cfg['target']['cmdline_template'], testcase.fuzzedfile.path)[1] self.outfile = outfile self.timeout = float(timeout) self.progname = self.cmdargs[1] self.options = options self.preserve_stderr = False self.tmpdir = testcase.fuzzedfile.dirname # child classes should explicitly set this to True if they need it: self.empty_output_ok = False self.missing_output_ok = False # keep track of retries self.retry_count = 0 self.max_retries = 3 # catch stderr if we're not already redirecting it # typically outfile is just going into a temp dir so we'll # drop a stderr file there (so it will just get deleted when # bff does its normal cleanup). It's really only useful if # there is a problem that raises an exception, which therefore # leaves the temp dir behind. if self.options.get('stderr'): self.preserve_stderr = True else: self._set_stderrpath()
def _cache_app(self): logger.debug( 'Caching application %s and determining if we need to watch the CPU...', self.program) sf = self.seedfile_set.next_item() cmdargs = get_command_args_list( self.config['target']['cmdline_template'], infile=sf.path)[1] if 'copyfuzzedto' in self.config['target']: from shutil import copyfile copyfuzzedto = str(self.config['target'].get('copyfuzzedto', '')) logger.debug("Copying seed file to " + copyfuzzedto) copyfile(sf.path, copyfuzzedto) if 'postprocessfuzzed' in self.config['target']: postprocessfuzzed = str(self.config['target']['postprocessfuzzed']) logger.debug("Executing postprocess " + postprocessfuzzed) os.system(postprocessfuzzed) logger.info('Invoking %s' % cmdargs) # Use overriden Popen that uses a job object to make sure that # child processes are killed p = Popen(cmdargs) runtimeout = self.config['runner']['runtimeout'] logger.debug('...Timer: %f', runtimeout) t = Timer(runtimeout, self.kill, args=[p]) logger.debug('...timer start') t.start() p.wait() logger.debug('...timer stop') t.cancel() if not self.gui_app: logger.debug('This seems to be a CLI application.') try: runner_watchcpu = str(self.config['runner']['watchcpu']).lower() debugger_watchcpu = runner_watchcpu except KeyError: self.config['runner']['watchcpu'] = 'auto' self.config['debugger']['watchcpu'] = 'auto' runner_watchcpu = 'auto' debugger_watchcpu = 'auto' if runner_watchcpu == 'auto': logger.debug('Disabling runner CPU monitoring for dynamic timeout') self.config['runner']['watchcpu'] = False if debugger_watchcpu == 'auto': logger.debug( 'Disabling debugger CPU monitoring for dynamic timeout') self.config['debugger']['watchcpu'] = False elif debugger_watchcpu == 'true': logger.debug('Manually enabling CPU watching for debugger') self.config['debugger']['watchcpu'] = True logger.info( 'Please ensure that the target program has just executed successfully' ) time.sleep(10)
def update_crash_details(self): # We might be updating crash details because we have a new fuzzedfile # (with a different path) self.cmdlist = get_command_args_list(self.cmd_template, infile=self.fuzzedfile.path)[1] self.cmdargs = self.cmdlist[1:] self.tempdir = tempfile.mkdtemp(prefix=self._tmp_pfx, suffix=self._tmp_sfx, dir=self.workdir_base) self.copy_files_to_temp()
def test_get_command_args_list(self): cmd = 'program arg1 arg2 $SEEDFILE arg3 arg4' cmd_template = string.Template(cmd) infile = 'foobar' result = clt.get_command_args_list(cmd_template, infile) self.assertEqual(2, len(result)) (as_string, as_list) = result for substring in ['program', 'arg1', 'arg2', 'arg3', 'arg4', 'foobar']: self.assertTrue(substring in as_string) self.assertTrue(substring in as_list)
def _create_minimizer_cfg(cfg): class DummyCfg(object): pass config = DummyCfg() config.backtracelevels = 5 # doesn't matter what this is, we don't use it config.debugger_timeout = cfg['runner']['runtimeout'] * 2 if config.debugger_timeout < 10: config.debugger_timeout = 10 template = string.Template(cfg['target']['cmdline_template']) config.get_command_args_list = lambda x: get_command_args_list( template, x)[1] config.program = cfg['target']['program'] config.exclude_unmapped_frames = False config.watchdogfile = os.devnull return config
def __init__(self, options, cmd_template, fuzzed_file, workingdir_base): RunnerBase.__init__(self, options, cmd_template, fuzzed_file, workingdir_base) logger.debug('Initialize Runner') self.exceptions = [ 0x80000001, # STATUS_GUARD_PAGE_VIOLATION 0x80000002, # EXCEPTION_DATATYPE_MISALIGNMENT 0x80000005, # STATUS_BUFFER_OVERFLOW 0xC0000005, # STATUS_ACCESS_VIOLATION 0xC0000009, # STATUS_BAD_INITIAL_STACK 0xC000000A, # STATUS_BAD_INITIAL_PC 0xC000001D, # STATUS_ILLEGAL_INSTRUCTION 0xC0000025, # EXCEPTION_NONCONTINUABLE_EXCEPTION 0xC0000026, # EXCEPTION_INVALID_DISPOSITION 0xC000008C, # EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008D, # STATUS_FLOAT_DENORMAL_OPERAND 0xC000008E, # EXCEPTION_FLT_DIVIDE_BY_ZERO 0xC000008F, # EXCEPTION_FLOAT_INEXACT_RESULT 0xC0000090, # EXCEPTION_FLT_INVALID_OPERATION 0xC0000091, # EXCEPTION_FLT_OVERFLOW 0xC0000092, # EXCEPTION_FLT_STACK_CHECK 0xC0000093, # EXCEPTION_FLT_UNDERFLOW 0xC0000094, # EXCEPTION_INT_OVERFLOW 0xC0000095, # EXCEPTION_INT_OVERFLOW 0xC0000096, # STATUS_PRIVILEGED_INSTRUCTION 0xC00000FD, # STATUS_STACK_OVERFLOW 0xC00002B4, # STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B5, # STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002C5, # STATUS_DATATYPE_MISALIGNMENT_ERROR 0xC00002C9, # STATUS_REG_NAT_CONSUMPTION ] self.watchcpu = options.get('watchcpu', False) (self.cmd, self.cmdlist) = get_command_args_list(cmd_template, fuzzed_file) logger.debug('Command: %s', self.cmd) find_or_create_dir(self.workingdir) self.t = None self.returncode = None self.remembered = [] if not hasattr(self, 'verify_architecture'): # check the architecture unless our options have already set it self.verify_architecture = True
def _construct_testcase(self): with WindowsTestcase( cfg=self.cfg, seedfile=self.seedfile, fuzzedfile=BasicFile(self.fuzzer.output_file_path), program=self.cfg['target']['program'], cmd_template=self.cmd_template, debugger_timeout=self.cfg['debugger']['runtimeout'], cmdlist=get_command_args_list(self.cmd_template, self.fuzzer.output_file_path)[1], dbg_opts=self.cfg['debugger'], workdir_base=self.working_dir, keep_faddr=self.cfg['runoptions'].get('keep_unique_faddr', False), heisenbug_retries=self.retries, copy_fuzzedfile=self.fuzzer.fuzzed_changes_input) as testcase: # put it on the list for the analysis pipeline self.testcases.append(testcase)
def _cache_app(self): logger.debug('cache program') sf = self.seedfile_set.next_item() # Run the program once to cache it into memory fullpathorig = self._full_path_original(sf.path) cmdargs = get_command_args_list( self.config['target']['cmdline_template'], infile=fullpathorig)[1] logger.info('Invoking %s' % cmdargs) subp.run_with_timer( cmdargs, self.config['runner']['runtimeout'] * 8, self.config['target']['program'], use_shell=False, seeoutput=True, ) # Give target time to die logger.info( 'Please ensure that the target program has just executed successfully' ) time.sleep(10)
def run_debugger(self, infile, outfile): self.debugger_runs += 1 cmd_args = get_command_args_list( self.cfg['target']['cmdline_template'], infile)[1] cmd = cmd_args[0] cmd_args = cmd_args[1:] exclude_unmapped_frames = self.cfg['analyzer'].get( 'exclude_unmapped_frames', True) dbg = self._debugger_cls( cmd, cmd_args, outfile, self.debugger_timeout, template=self.testcase.debugger_template, exclude_unmapped_frames=exclude_unmapped_frames, keep_uniq_faddr=self.keep_uniq_faddr, workingdir=self.tempdir, watchcpu=self.watchcpu) parsed_debugger_output = dbg.go() return parsed_debugger_output
def _construct_testcase(self): with LinuxTestcase( cfg=self.cfg, seedfile=self.seedfile, fuzzedfile=BasicFile(self.fuzzer.output_file_path), program=self.cfg['target']['program'], cmd_template=self.cmd_template, debugger_timeout=self.cfg['debugger']['runtimeout'], cmdlist=get_command_args_list( self.cmd_template, infile=self.fuzzer.output_file_path, posix=True)[1], backtrace_lines=self.cfg['debugger']['backtracelevels'], crashers_dir=self.testcase_base_dir, workdir_base=self.working_dir, keep_faddr=self.cfg['runoptions'].get('keep_unique_faddr', False), save_failed_asserts=self.cfg['analyzer'].get( 'savefailedasserts', False), exclude_unmapped_frames=self.cfg['analyzer'] ['exclude_unmapped_frames']) as testcase: # put it on the list for the analysis pipeline self.testcases.append(testcase)
def main(): 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('-c', '--config', default='configs/bff.yaml', dest='config', help='path to the configuration file to use') parser.add_option('-e', '--edb', dest='use_edb', action='store_true', help='Use edb instead of gdb') parser.add_option('-p', '--debugger', dest='debugger', help='Use specified debugger') parser.add_option('-f', '--filepath', dest='filepath', action='store_true', help='Recreate original file path') (options, args) = parser.parse_args() if options.debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) cfg_file = options.config logger.debug('Config file: %s', cfg_file) if len(args) and os.path.exists(args[0]): fullpath_fuzzed_file = os.path.abspath(args[0]) fuzzed_file = BasicFile(fullpath_fuzzed_file) logger.info('Fuzzed file is: %s', fuzzed_file.path) else: parser.error('fuzzedfile must be specified') iterationpath = '' if options.filepath: # Recreate same file path as fuzz iteration resultdir = os.path.dirname(fuzzed_file.path) for gdbfile in all_files(resultdir, '*.gdb'): print '** using gdb: %s' % gdbfile iterationpath = getiterpath(gdbfile) break if iterationpath: iterationdir = os.path.dirname(iterationpath) iterationfile = os.path.basename(iterationpath) if iterationdir: mkdir_p(iterationdir) copy_file(fuzzed_file.path, os.path.join(iterationdir, iterationfile)) fullpath_fuzzed_file = iterationpath config = load_and_fix_config(cfg_file) cmd_as_args = get_command_args_list(config['target']['cmdline_template'], fullpath_fuzzed_file)[1] args = [] if options.use_edb and options.debugger: parser.error('Options --edb and --debugger are mutually exclusive.') if options.debugger: debugger_app = options.debugger elif options.use_edb: debugger_app = 'edb' elif platform.system() == 'Darwin': debugger_app = 'lldb' else: debugger_app = 'gdb' args.append(debugger_app) if options.use_edb: args.append('--run') elif debugger_app == 'gdb': # Using gdb args.append('--args') args.extend(cmd_as_args) logger.info('args %s' % args) p = Popen(args, universal_newlines=True) p.wait()
def main(): 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('-c', '--config', default='configs/bff.yaml', dest='config', help='path to the configuration file to use') parser.add_option('-w', '--windbg', dest='use_windbg', action='store_true', help='Use windbg instead of cdb') parser.add_option('-b', '--break', dest='break_on_start', action='store_true', help='Break on start of debugger session') parser.add_option('-d', '--debugheap', dest='debugheap', action='store_true', help='Use debug heap') parser.add_option('-p', '--debugger', dest='debugger', help='Use specified debugger') parser.add_option('-f', '--filepath', dest='filepath', action='store_true', help='Recreate original file path') (options, args) = parser.parse_args() if options.debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) cfg_file = options.config logger.debug('WindowsConfig file: %s', cfg_file) if len(args) and os.path.exists(args[0]): fullpath_fuzzed_file = os.path.abspath(args[0]) fuzzed_file = BasicFile(fullpath_fuzzed_file) logger.info('Fuzzed file is: %s', fuzzed_file.path) else: parser.error('fuzzedfile must be specified') config = load_and_fix_config(cfg_file) iterationpath = '' if options.filepath: # Recreate same file path as fuzz iteration resultdir = os.path.dirname(fuzzed_file.path) for msecfile in all_files(resultdir, '*.msec'): print '** using msecfile: %s' % msecfile iterationpath = getiterpath(msecfile) break if iterationpath: iterationdir = os.path.dirname(iterationpath) iterationfile = os.path.basename(iterationpath) mkdir_p(iterationdir) copy_file(fuzzed_file.path, os.path.join(iterationdir, iterationfile)) fuzzed_file.path = iterationpath cmd_as_args = get_command_args_list(config['target']['cmdline_template'], fuzzed_file.path)[1] args = [] if options.use_windbg and options.debugger: parser.error('Options --windbg and --debugger are mutually exclusive.') if options.debugger: debugger_app = options.debugger elif options.use_windbg: debugger_app = 'windbg' else: debugger_app = 'cdb' args.append(debugger_app) if not options.debugger: # Using cdb or windbg args.append('-amsec.dll') if options.debugheap or config['debugger']['debugheap']: # do not use hd, xd options if debugheap is set pass else: args.extend(('-hd', '-xd', 'gp')) if not options.break_on_start: args.extend(('-xd', 'bpe', '-G')) args.extend(( '-xd', 'wob', '-o', )) args.extend(cmd_as_args) logger.info('args %s' % cmd_as_args) p = Popen(args, universal_newlines=True) p.wait()
def main(): 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 testcase 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)', type='float') parser.add_option('-g', '--target-size-guess', dest='initial_target_size', help='A guess at the minimal value (int)', type='int') parser.add_option('', '--config', 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 testcase hashes encountered during minimization') (options, args) = parser.parse_args() if options.debug: logger.setLevel(logging.DEBUG) elif options.verbose: logger.setLevel(logging.INFO) if options.config: cfg_file = os.path.expanduser(options.config) else: if os.path.isfile("../configs/bff.yaml"): cfg_file = "../configs/bff.yaml" elif os.path.isfile("configs/bff.yaml"): cfg_file = "configs/bff.yaml" else: parser.error( 'Configuration file (--config) option must be specified.') 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: outdir = "./minimizer_out" outdir = os.path.abspath(outdir) if not os.path.exists(outdir): filetools.make_directories(outdir) if not os.path.isdir(outdir): parser.error('--outdir must either already be a dir or not exist: %s' % outdir) if len(args) and os.path.exists(args[0]): fuzzed_file = BasicFile(args[0]) logger.info('Fuzzed file is: %s', fuzzed_file.path) else: parser.error('fuzzedfile must be specified') if options.target: seedfile = BasicFile(options.target) else: seedfile = None min2seed = not options.stringmode filename_modifier = '' crashers_dir = '.' cfg = load_and_fix_config(cfg_file) debugger_timeout = cfg['runner']['runtimeout'] * 2 if debugger_timeout < 10: debugger_timeout = 10 proc_compat = platform.system() == 'Linux' cfg['debugger']['proc_compat'] = proc_compat cfg['debugger']['runtimeout'] = debugger_timeout with LinuxTestcase(cfg=cfg, seedfile=seedfile, fuzzedfile=fuzzed_file, program=cfg['target']['program'], cmd_template=cfg['target']['cmdline_template'], debugger_timeout=debugger_timeout, cmdlist=get_command_args_list( cfg['target']['cmdline_template'], fuzzed_file.path)[1], backtrace_lines=cfg['debugger']['backtracelevels'], crashers_dir=crashers_dir, workdir_base=outdir, keep_faddr=options.keep_uniq_faddr) as testcase: filetools.make_directories(testcase.tempdir) logger.info('Copying %s to %s', fuzzed_file.path, testcase.tempdir) filetools.copy_file(fuzzed_file.path, testcase.tempdir) minlog = os.path.join(outdir, 'min_log.txt') with Minimizer(cfg=cfg, testcase=testcase, crash_dst_dir=outdir, seedfile_as_target=min2seed, bitwise=options.bitwise, confidence=confidence, logfile=minlog, tempdir=testcase.tempdir, maxtime=options.timeout, preferx=options.prefer_x_target, keep_uniq_faddr=options.keep_uniq_faddr) 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_content) 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_content) for idx in minimize.bytemap: logger.debug('Swapping index %d', idx) targetstring[idx] = fuzzed[idx] filename = ''.join( (testcase.fuzzedfile.root, filename_modifier, testcase.fuzzedfile.ext)) metasploit_file = os.path.join(testcase.tempdir, filename) with open(metasploit_file, 'wb') as f: f.writelines(targetstring) testcase.copy_files(outdir) testcase.clean_tmpdir()
def main(): 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('-c', '--config', default='configs/bff.yaml', dest='config', help='path to the configuration file to use') parser.add_option('-a', '--args', dest='print_args', action='store_true', help='Print function arguments') parser.add_option('-o', '--out', dest='outfile', help='PIN output file') parser.add_option('-f', '--filepath', dest='filepath', action='store_true', help='Recreate original file path') (options, args) = parser.parse_args() if options.debug: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) if options.outfile: outfile = options.outfile else: outfile = 'calltrace.log' cfg_file = options.config logger.debug('Config file: %s', cfg_file) if len(args) and os.path.exists(args[0]): fullpath_fuzzed_file = os.path.abspath(args[0]) fuzzed_file = BasicFile(fullpath_fuzzed_file) logger.info('Fuzzed file is: %s', fuzzed_file.path) else: parser.error('fuzzedfile must be specified') iterationpath = '' if options.filepath: # Recreate same file path as fuzz iteration resultdir = os.path.dirname(fuzzed_file.path) for gdbfile in all_files(resultdir, '*.gdb'): print '** using gdb: %s' % gdbfile iterationpath = getiterpath(gdbfile) break if iterationpath: iterationdir = os.path.dirname(iterationpath) iterationfile = os.path.basename(iterationpath) if iterationdir: mkdir_p(iterationdir) copy_file(fuzzed_file.path, os.path.join(iterationdir, iterationfile)) fullpath_fuzzed_file = iterationpath config = load_and_fix_config(cfg_file) cmd_as_args = get_command_args_list( config['target']['cmdline_template'], fullpath_fuzzed_file)[1] args = [] pin = os.path.expanduser('~/pin/pin') pintool = os.path.expanduser('~/pintool/calltrace.so') args = [pin, '-injection', 'child', '-t', pintool, '-o', outfile] if options.print_args: args.append('-a') args.append('--') args.extend(cmd_as_args) logger.info('args %s' % args) p = Popen(args, universal_newlines=True) p.wait()