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('-c', '--config', default='conf.d/bff.cfg', 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) 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 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 = read_config_options(cfg_file) cmd_as_args = config.get_command_list(fullpath_fuzzed_file) program = cmd_as_args[0] if not os.path.exists(program): # edb wants a full path to the target app, so let's find it for path in os.environ["PATH"].split(":"): if os.path.exists(os.path.join(path, program)): program = os.path.join(path, program) # Recreate command args list with full path to target cmd_as_args = [] cmd_as_args.append(program) cmd_as_args.extend(config.get_command_args_list(fullpath_fuzzed_file)) 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' else: debugger_app = 'gdb' args.append(debugger_app) if options.use_edb: args.append('--run') else: # 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(): 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('-c', '--config', default='conf.d/bff.cfg', 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) 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 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 = read_config_options(cfg_file) cmd_as_args = config.get_command_list(fullpath_fuzzed_file) program = cmd_as_args[0] if not os.path.exists(program): # edb wants a full path to the target app, so let's find it for path in os.environ["PATH"].split(":"): if os.path.exists(os.path.join(path, program)): program = os.path.join(path, program) # Recreate command args list with full path to target cmd_as_args = [] cmd_as_args.append(program) cmd_as_args.extend(config.get_command_args_list(fullpath_fuzzed_file)) 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' else: debugger_app = 'gdb' args.append(debugger_app) if options.use_edb: args.append('--run') else: # 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(): 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()
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()
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)', 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).') (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("../conf.d/bff.cfg"): cfg_file = "../conf.d/bff.cfg" elif os.path.isfile("conf.d/bff.cfg"): cfg_file = "conf.d/bff.cfg" 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" 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) else: parser.error('fuzzedfile must be specified') cfg = read_config_options(cfg_file) if options.target: seedfile = BasicFile(options.target) else: seedfile = None min2seed = not options.stringmode filename_modifier = '' crashers_dir = '.' with BffCrash(cfg, seedfile, fuzzed_file, cfg.program, cfg.debugger_timeout, cfg.killprocname, cfg.backtracelevels, crashers_dir, options.keep_uniq_faddr) 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) with Minimizer(cfg=cfg, crash=crash, crash_dst_dir=outdir, seedfile_as_target=min2seed, bitwise=options.bitwise, confidence=confidence, logfile='./min_log.txt', tempdir=crash.tempdir, maxtime=options.timeout, preferx=options.prefer_x_target, keep_uniq_faddr=options.keep_uniq_faddr) as minimize: minimize.save_others = False 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) with open(metasploit_file, 'wb') as f: f.writelines(targetstring) crash.copy_files(outdir) crash.clean_tmpdir()
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('-c', '--config', default='configs/foe.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('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) else: parser.error('fuzzedfile must be specified') config = Config(cfg_file).config iterationpath = '' template = string.Template(config['target']['cmdline_template']) 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(template, fuzzed_file.path)[1] targetdir = os.path.dirname(cmd_as_args[0]) 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: # 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, cwd=targetdir, universal_newlines=True) p.wait()
def main(): global START_SEED hashes = [] # give up if we don't have a debugger debuggers.verify_supported_platform() setup_logging_to_console(logger, logging.INFO) logger.info("Welcome to BFF!") scriptpath = os.path.dirname(sys.argv[0]) logger.info('Scriptpath is %s', scriptpath) # parse command line options logger.info('Parsing command line options') parser = OptionParser() parser.add_option('', '--debug', dest='debug', help='Turn on debugging output', action='store_true') parser.add_option('-c', '--config', dest='cfg', help='Config file location') (options, args) = parser.parse_args() #@UnusedVariable # Get the cfg file name if options.cfg: remote_cfg_file = options.cfg else: remote_cfg_file = get_config_file(scriptpath) # die unless the remote config is present assert os.path.exists( remote_cfg_file ), 'Cannot find remote config file: %s, Please create it or use --config option to specify a different location.' % remote_cfg_file # copy remote config to local: local_cfg_file = os.path.expanduser('~/bff.cfg') filetools.copy_file(remote_cfg_file, local_cfg_file) # Read the cfg file logger.info('Reading config from %s', local_cfg_file) cfg = cfg_helper.read_config_options(local_cfg_file) # set up local logging setup_logfile(cfg.local_dir, log_basename='bff.log', level=logging.DEBUG, max_bytes=1e8, backup_count=3) # set up remote logging setup_logfile(cfg.output_dir, log_basename='bff.log', level=logging.INFO, max_bytes=1e7, backup_count=5) try: check_for_script(cfg) except: logger.warning("Please configure BFF to fuzz a binary. Exiting...") sys.exit() z.setup_dirs_and_files(local_cfg_file, cfg) # make sure we cache it for the next run # cache_state(cfg.campaign_id, 'cfg', cfg, cfg.cached_config_file) sr = get_cached_state('seedrange', cfg.campaign_id, cfg.cached_seedrange_file) if not sr: sr = SeedRange(cfg.start_seed, cfg.seed_interval, cfg.max_seed) # set START_SEED global for timestamping START_SEED = sr.s1 start_process_killer(scriptpath, cfg) z.set_unbuffered_stdout() # set up the seedfile set so we can pick seedfiles for everything else... seedfile_set = get_cached_state('seedfile_set', cfg.campaign_id, cfg.cached_seedfile_set) if not seedfile_set: logger.info('Building seedfile set') sfs_logfile = os.path.join(cfg.seedfile_output_dir, 'seedfile_set.log') with SeedfileSet( campaign_id=cfg.campaign_id, originpath=cfg.seedfile_origin_dir, localpath=cfg.seedfile_local_dir, outputpath=cfg.seedfile_output_dir, logfile=sfs_logfile, ) as sfset: seedfile_set = sfset # set up the watchdog timeout within the VM and restart the daemon if cfg.watchdogtimeout: watchdog = WatchDog(cfg.watchdogfile, cfg.watchdogtimeout) touch_watchdog_file(cfg) watchdog.go() cache_state(cfg.campaign_id, 'seedfile_set', seedfile_set, cfg.cached_seedfile_set) sf = seedfile_set.next_item() # Run the program once to cache it into memory z.cache_program_once(cfg, sf.path) # Give target time to die time.sleep(1) # flag to indicate whether this is a fresh script start up or not first_chunk = True # remember our parent process id so we can tell if it changes later _last_ppid = os.getppid() # campaign.go while sr.in_max_range(): # wipe the tmp dir clean to try to avoid filling the VM disk TmpReaper().clean_tmp() sf = seedfile_set.next_item() r = sf.rangefinder.next_item() sr.set_s2() while sr.in_range(): # interval.go logger.debug('Starting interval %d-%d', sr.s1, sr.s2) # Prevent watchdog from rebooting VM. If /tmp/fuzzing exists and is stale, the machine will reboot touch_watchdog_file(cfg) # check parent process id _ppid_now = os.getppid() if not _ppid_now == _last_ppid: logger.warning('Parent process ID changed from %d to %d', _last_ppid, _ppid_now) _last_ppid = _ppid_now # do the fuzz cmdline = cfg.get_command(sf.path) if first_chunk: # disable the --quiet option in zzuf # on the first chunk only quiet_flag = False first_chunk = False else: quiet_flag = True zzuf = Zzuf( cfg.local_dir, sr.s1, sr.s2, cmdline, sf.path, cfg.zzuf_log_file, cfg.copymode, r.min, r.max, cfg.progtimeout, quiet_flag, ) saw_crash = zzuf.go() if not saw_crash: # we must have made it through this chunk without a crash # so go to next chunk try_count = sr.s1_s2_delta() sf.record_tries(tries=try_count) r.record_tries(tries=try_count) # emit a log entry crashcount = z.get_crashcount(cfg.crashers_dir) rate = get_rate(sr.s1) seed_str = "seeds=%d-%d" % (sr.s1, sr.s2) range_str = "range=%.6f-%.6f" % (r.min, r.max) rate_str = "Rate=(%.2f/s %.1f/m %.0f/h %.0f/d)" % ( rate, rate * 60, rate * 3600, rate * 86400) expected_density = seedfile_set.expected_crash_density xd_str = "expected=%.9f" % expected_density xr_str = 'expected_rate=%.6f uniq/day' % (expected_density * rate * 86400) logger.info('Fuzzing %s %s %s %s %s %s crash_count=%d', sf.path, seed_str, range_str, rate_str, xd_str, xr_str, crashcount) # set s1 to s2 so that as soon as we continue we'll break out of the sr.in_range() loop sr.set_s1_to_s2() continue # we must have seen a crash # get the results zzuf_log = ZzufLog(cfg.zzuf_log_file, cfg.zzuf_log_out(sf.output_dir)) # Don't generate cases for killed process or out-of-memory # In the default mode, zzuf will report a signal. In copy (and exit code) mode, zzuf will # report the exit code in its output log. The exit code is 128 + the signal number. crash_status = zzuf_log.crash_logged(cfg.copymode) sr.bookmark_s1() sr.s1 = zzuf_log.seed # record the fact that we've made it this far try_count = sr.s1_delta() sf.record_tries(tries=try_count) r.record_tries(tries=try_count) new_uniq_crash = False if crash_status: logger.info('Generating testcase for %s', zzuf_log.line) # a true crash zzuf_range = zzuf_log.range # create the temp dir for the results cfg.create_tmpdir() outfile = cfg.get_testcase_outfile(sf.path, sr.s1) logger.debug('Output file is %s', outfile) testcase = zzuf.generate_test_case(sf.path, sr.s1, zzuf_range, outfile) # Do internal verification using GDB / Valgrind / Stderr fuzzedfile = file_handlers.basicfile.BasicFile(outfile) with BffCrash(cfg, sf, fuzzedfile, cfg.program, cfg.debugger_timeout, cfg.killprocname, cfg.backtracelevels, cfg.crashers_dir, sr.s1, r) as c: if c.is_crash: new_uniq_crash = verify_crasher( c, hashes, cfg, seedfile_set) # record the zzuf log line for this crash if not c.logger: c.get_logger() c.logger.debug("zzuflog: %s", zzuf_log.line) c.logger.info('Command: %s', testcase.cmdline) cfg.clean_tmpdir() sr.increment_seed() # cache objects in case of reboot cache_state(cfg.campaign_id, 'seedrange', sr, cfg.cached_seedrange_file) pickled_seedfile_file = os.path.join(cfg.cached_objects_dir, sf.pkl_file()) cache_state(cfg.campaign_id, sf.cache_key(), sf, pickled_seedfile_file) cache_state(cfg.campaign_id, 'seedfile_set', seedfile_set, cfg.cached_seedfile_set) if new_uniq_crash: # we had a hit, so break the inner while() loop # so we can pick a new range. This is to avoid # having a crash-rich range run away with the # probability before other ranges have been tried break
def main(): global START_SEED hashes = [] # give up if we don't have a debugger debuggers.verify_supported_platform() setup_logging_to_console(logger, logging.INFO) logger.info("Welcome to BFF!") scriptpath = os.path.dirname(sys.argv[0]) logger.info('Scriptpath is %s', scriptpath) # parse command line options logger.info('Parsing command line options') parser = OptionParser() parser.add_option('', '--debug', dest='debug', help='Turn on debugging output', action='store_true') parser.add_option('-c', '--config', dest='cfg', help='Config file location') (options, args) = parser.parse_args() #@UnusedVariable # Get the cfg file name if options.cfg: remote_cfg_file = options.cfg else: remote_cfg_file = get_config_file(scriptpath) # die unless the remote config is present assert os.path.exists(remote_cfg_file), 'Cannot find remote config file: %s, Please create it or use --config option to specify a different location.' % remote_cfg_file # copy remote config to local: local_cfg_file = os.path.expanduser('~/bff.cfg') filetools.copy_file(remote_cfg_file, local_cfg_file) # Read the cfg file logger.info('Reading config from %s', local_cfg_file) cfg = cfg_helper.read_config_options(local_cfg_file) # set up local logging setup_logfile(cfg.local_dir, log_basename='bff.log', level=logging.DEBUG, max_bytes=1e8, backup_count=3) # set up remote logging setup_logfile(cfg.output_dir, log_basename='bff.log', level=logging.INFO, max_bytes=1e7, backup_count=5) try: check_for_script(cfg) except: logger.warning("Please configure BFF to fuzz a binary. Exiting...") sys.exit() z.setup_dirs_and_files(local_cfg_file, cfg) # make sure we cache it for the next run # cache_state(cfg.campaign_id, 'cfg', cfg, cfg.cached_config_file) sr = get_cached_state('seedrange', cfg.campaign_id, cfg.cached_seedrange_file) if not sr: sr = SeedRange(cfg.start_seed, cfg.seed_interval, cfg.max_seed) # set START_SEED global for timestamping START_SEED = sr.s1 start_process_killer(scriptpath, cfg) z.set_unbuffered_stdout() # set up the seedfile set so we can pick seedfiles for everything else... seedfile_set = get_cached_state('seedfile_set', cfg.campaign_id, cfg.cached_seedfile_set) if not seedfile_set: logger.info('Building seedfile set') sfs_logfile = os.path.join(cfg.seedfile_output_dir, 'seedfile_set.log') with SeedfileSet(campaign_id=cfg.campaign_id, originpath=cfg.seedfile_origin_dir, localpath=cfg.seedfile_local_dir, outputpath=cfg.seedfile_output_dir, logfile=sfs_logfile, ) as sfset: seedfile_set = sfset # set up the watchdog timeout within the VM and restart the daemon if cfg.watchdogtimeout: watchdog = WatchDog(cfg.watchdogfile, cfg.watchdogtimeout) touch_watchdog_file(cfg) watchdog.go() cache_state(cfg.campaign_id, 'seedfile_set', seedfile_set, cfg.cached_seedfile_set) sf = seedfile_set.next_item() # Run the program once to cache it into memory z.cache_program_once(cfg, sf.path) # Give target time to die time.sleep(1) # flag to indicate whether this is a fresh script start up or not first_chunk = True # remember our parent process id so we can tell if it changes later _last_ppid = os.getppid() # campaign.go while sr.in_max_range(): # wipe the tmp dir clean to try to avoid filling the VM disk TmpReaper().clean_tmp() sf = seedfile_set.next_item() r = sf.rangefinder.next_item() sr.set_s2() while sr.in_range(): # interval.go logger.debug('Starting interval %d-%d', sr.s1, sr.s2) # Prevent watchdog from rebooting VM. If /tmp/fuzzing exists and is stale, the machine will reboot touch_watchdog_file(cfg) # check parent process id _ppid_now = os.getppid() if not _ppid_now == _last_ppid: logger.warning('Parent process ID changed from %d to %d', _last_ppid, _ppid_now) _last_ppid = _ppid_now # do the fuzz cmdline = cfg.get_command(sf.path) if first_chunk: # disable the --quiet option in zzuf # on the first chunk only quiet_flag = False first_chunk = False else: quiet_flag = True zzuf = Zzuf(cfg.local_dir, sr.s1, sr.s2, cmdline, sf.path, cfg.zzuf_log_file, cfg.copymode, r.min, r.max, cfg.progtimeout, quiet_flag, ) saw_crash = zzuf.go() if not saw_crash: # we must have made it through this chunk without a crash # so go to next chunk try_count = sr.s1_s2_delta() sf.record_tries(tries=try_count) r.record_tries(tries=try_count) # emit a log entry crashcount = z.get_crashcount(cfg.crashers_dir) rate = get_rate(sr.s1) seed_str = "seeds=%d-%d" % (sr.s1, sr.s2) range_str = "range=%.6f-%.6f" % (r.min, r.max) rate_str = "Rate=(%.2f/s %.1f/m %.0f/h %.0f/d)" % (rate, rate * 60, rate * 3600, rate * 86400) expected_density = seedfile_set.expected_crash_density xd_str = "expected=%.9f" % expected_density xr_str = 'expected_rate=%.6f uniq/day' % (expected_density * rate * 86400) logger.info('Fuzzing %s %s %s %s %s %s crash_count=%d', sf.path, seed_str, range_str, rate_str, xd_str, xr_str, crashcount) # set s1 to s2 so that as soon as we continue we'll break out of the sr.in_range() loop sr.set_s1_to_s2() continue # we must have seen a crash # get the results zzuf_log = ZzufLog(cfg.zzuf_log_file, cfg.zzuf_log_out(sf.output_dir)) # Don't generate cases for killed process or out-of-memory # In the default mode, zzuf will report a signal. In copy (and exit code) mode, zzuf will # report the exit code in its output log. The exit code is 128 + the signal number. crash_status = zzuf_log.crash_logged(cfg.copymode) sr.bookmark_s1() sr.s1 = zzuf_log.seed # record the fact that we've made it this far try_count = sr.s1_delta() sf.record_tries(tries=try_count) r.record_tries(tries=try_count) new_uniq_crash = False if crash_status: logger.info('Generating testcase for %s', zzuf_log.line) # a true crash zzuf_range = zzuf_log.range # create the temp dir for the results cfg.create_tmpdir() outfile = cfg.get_testcase_outfile(sf.path, sr.s1) logger.debug('Output file is %s', outfile) testcase = zzuf.generate_test_case(sf.path, sr.s1, zzuf_range, outfile) # Do internal verification using GDB / Valgrind / Stderr fuzzedfile = file_handlers.basicfile.BasicFile(outfile) with BffCrash(cfg, sf, fuzzedfile, cfg.program, cfg.debugger_timeout, cfg.killprocname, cfg.backtracelevels, cfg.crashers_dir, sr.s1, r) as c: if c.is_crash: new_uniq_crash = verify_crasher(c, hashes, cfg, seedfile_set) # record the zzuf log line for this crash if not c.logger: c.get_logger() c.logger.debug("zzuflog: %s", zzuf_log.line) c.logger.info('Command: %s', testcase.cmdline) cfg.clean_tmpdir() sr.increment_seed() # cache objects in case of reboot cache_state(cfg.campaign_id, 'seedrange', sr, cfg.cached_seedrange_file) pickled_seedfile_file = os.path.join(cfg.cached_objects_dir, sf.pkl_file()) cache_state(cfg.campaign_id, sf.cache_key(), sf, pickled_seedfile_file) cache_state(cfg.campaign_id, 'seedfile_set', seedfile_set, cfg.cached_seedfile_set) if new_uniq_crash: # we had a hit, so break the inner while() loop # so we can pick a new range. This is to avoid # having a crash-rich range run away with the # probability before other ranges have been tried break
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)', 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).') (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("../conf.d/bff.cfg"): cfg_file = "../conf.d/bff.cfg" elif os.path.isfile("conf.d/bff.cfg"): cfg_file = "conf.d/bff.cfg" 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" 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) else: parser.error('fuzzedfile must be specified') cfg = read_config_options(cfg_file) if options.target: seedfile = BasicFile(options.target) else: seedfile = None min2seed = not options.stringmode filename_modifier = '' crashers_dir = '.' with BffCrash(cfg, seedfile, fuzzed_file, cfg.program, cfg.debugger_timeout, cfg.killprocname, cfg.backtracelevels, crashers_dir, options.keep_uniq_faddr) 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) with Minimizer(cfg=cfg, crash=crash, crash_dst_dir=outdir, seedfile_as_target=min2seed, bitwise=options.bitwise, confidence=confidence, logfile='./min_log.txt', tempdir=crash.tempdir, maxtime=options.timeout, preferx=options.prefer_x_target, keep_uniq_faddr=options.keep_uniq_faddr) as minimize: minimize.save_others = False 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) with open(metasploit_file, 'wb') as f: f.writelines(targetstring) crash.copy_files(outdir) crash.clean_tmpdir()
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('-c', '--config', default='configs/foe.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('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) else: parser.error('fuzzedfile must be specified') config = Config(cfg_file).config iterationpath = '' template = string.Template(config['target']['cmdline_template']) 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(template, fuzzed_file.path)[1] targetdir = os.path.dirname(cmd_as_args[0]) 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: # 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, cwd=targetdir, universal_newlines=True) p.wait()