class Test(unittest.TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() self.emptymd5 = hashlib.md5('').hexdigest() (fd1, self.f1) = tempfile.mkstemp(dir=self.tempdir) os.close(fd1) self.emptybasicfile = BasicFile(self.f1) (fd2, self.f2) = tempfile.mkstemp(dir=self.tempdir) self.content = "I'm here and I'm ready. They're not. Bring it." os.write(fd2, self.content) os.close(fd2) self.basicfile = BasicFile(self.f2) def tearDown(self): shutil.rmtree(self.tempdir) def test_basicfile_init(self): self.assertEqual(self.emptybasicfile.md5, self.emptymd5) self.assertEqual(self.emptybasicfile.len, 0) self.assertEqual(self.emptybasicfile.bitlen, 0) self.assertEqual(self.basicfile.md5, 'b8a17b44dec164d67685a9fe9817da90') self.assertEqual(self.basicfile.len, len(self.content)) self.assertEqual(self.basicfile.bitlen, 8 * len(self.content)) def test_refresh(self): fd = open(self.emptybasicfile.path, 'w') fd.write('Boom, crush. Night, losers. Winning, duh. ') fd.close() self.assertEqual(self.emptybasicfile.md5, self.emptymd5) self.assertEqual(self.emptybasicfile.len, 0) self.assertEqual(self.emptybasicfile.bitlen, 0) self.emptybasicfile.refresh() self.assertEqual(self.emptybasicfile.md5, '0281570ea703d7e39dab89319fe96202') self.assertEqual(self.emptybasicfile.len, 42) self.assertEqual(self.emptybasicfile.bitlen, 8 * 42) def test_read(self): self.assertEqual(self.basicfile.read(), self.content) # nonexistent file should raise an exception os.remove(self.basicfile.path) self.assertFalse(os.path.exists(self.basicfile.path)) self.assertRaises(Exception, self.basicfile.read) def test_exists(self): self.assertTrue(self.emptybasicfile.exists()) self.assertTrue(self.basicfile.exists()) os.remove(self.f1) self.assertFalse(self.emptybasicfile.exists()) self.assertTrue(self.basicfile.exists()) os.remove(self.f2) self.assertFalse(self.emptybasicfile.exists()) self.assertFalse(self.basicfile.exists())
def setUp(self): self.tempdir = tempfile.mkdtemp() self.emptymd5 = hashlib.md5('').hexdigest() (fd1, self.f1) = tempfile.mkstemp(dir=self.tempdir) os.close(fd1) self.emptybasicfile = BasicFile(self.f1) (fd2, self.f2) = tempfile.mkstemp(dir=self.tempdir) self.content = "I'm here and I'm ready. They're not. Bring it." os.write(fd2, self.content) os.close(fd2) self.basicfile = BasicFile(self.f2)
def copy_files_to_temp(self): if self.fuzzedfile and self.copy_fuzzedfile: filetools.copy_file(self.fuzzedfile.path, self.tempdir) else: # We're in verify mode. Set the fuzzedfile to be the seedfile, # since we didn't mutate anything self.fuzzedfile = self.seedfile if self.seedfile: filetools.copy_file(self.seedfile.path, self.tempdir) # TODO: This seems hacky. Should be better way to have # minimizer_log.txt and core files survive update_crash_details minlog = os.path.join(self.fuzzedfile.dirname, 'minimizer_log.txt') if os.path.exists(minlog): filetools.copy_file(minlog, self.tempdir) corefile = os.path.join(self.workdir_base, 'core') if os.path.exists(corefile): filetools.copy_file(corefile, self.tempdir) calltracefile = os.path.join(self.fuzzedfile.dirname, '%s.calltrace' % self.fuzzedfile.basename) if os.path.exists(calltracefile): filetools.copy_file(calltracefile, self.tempdir) new_fuzzedfile = os.path.join(self.tempdir, self.fuzzedfile.basename) self.fuzzedfile = BasicFile(new_fuzzedfile)
def _crash_builder(self): self.logger.debug('Building new testcase object.') # copy our original testcase as the basis for the new testcase new_testcase = copy.deepcopy(self.testcase) # get a new dir for the next crasher newcrash_tmpdir = tempfile.mkdtemp(prefix='minimizer_crash_builder_', dir=self.tempdir) # get a new filename for the next crasher sfx = self.testcase.fuzzedfile.ext if self.testcase.seedfile: pfx = '%s-' % self.testcase.seedfile.root else: pfx = 'string-' (fd, f) = tempfile.mkstemp(suffix=sfx, prefix=pfx, dir=newcrash_tmpdir) os.close(fd) delete_files(f) outfile = f if os.path.exists(outfile): self._raise('Outfile should not already exist: %s' % outfile) self.logger.debug('\tCopying %s to %s', self.tempfile, outfile) filetools.copy_file(self.tempfile, outfile) if 'copyfuzzedto' in self.cfg['target']: copyfuzzedto = str(self.cfg['target'].get('copyfuzzedto', '')) logger.debug("Copying fuzzed file to " + copyfuzzedto) filetools.copy_file(self.tempfile, copyfuzzedto) if 'postprocessfuzzed' in self.cfg['target']: postprocessfuzzed = str(self.cfg['target']['postprocessfuzzed']) logger.debug("Executing postprocess " + postprocessfuzzed) os.system(postprocessfuzzed) new_testcase.fuzzedfile = BasicFile(outfile) self.logger.debug('\tNew fuzzed_content file: %s %s', new_testcase.fuzzedfile.path, new_testcase.fuzzedfile.md5) # clear out the copied testcase signature so that it will be # regenerated new_testcase.signature = None # replace old testcase details with new info specific to this testcase self.logger.debug('\tUpdating testcase details') new_testcase.update_crash_details() # the tempdir we created is no longer needed because # update_crash_details creates a fresh one shutil.rmtree(newcrash_tmpdir) if os.path.exists(newcrash_tmpdir): logger.warning("Failed to remove temp dir %s", newcrash_tmpdir) return new_testcase
def refresh(self): ''' Gets all the file paths from self.dir then creates corresponding BasicFile objects in self.files ''' self._verify_dir() dir_listing = [ os.path.join(self.dir, f) for f in os.listdir(self.dir) if not f in blocklist ] self.files = [ BasicFile(path) for path in dir_listing if os.path.isfile(path) ]
def __init__(self, output_base_dir, path): ''' Creates an output dir for this seedfile based on its md5 hash. @param output_base_dir: The base directory for output files @raise SeedFileError: zero-length files will raise a SeedFileError ''' BasicFile.__init__(self, path) if not self.len > 0: raise SeedFileError( 'You cannot do bitwise fuzzing on a zero-length file: %s' % self.path) # use len for bytewise, bitlen for bitwise if self.len > 1: self.range_min = 1.0 / self.len self.range_max = 1.0 - self.range_min else: self.range_min = 0 self.range_max = 1 self.tries = 0 self.rangefinder = RangeFinder(self.range_min, self.range_max)
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 _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 __init__(self): fd, f = tempfile.mkstemp(suffix='.ext', prefix='fileroot') os.close(fd) self.fuzzedfile = BasicFile(f) self.debugger_template = 'foo'
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(): 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 go(self): # start by copying the fuzzed_content file since as of now it's our # best fit filetools.copy_file(self.testcase.fuzzedfile.path, self.outputfile) # replace the fuzzedfile object in testcase with the minimized copy self.testcase.fuzzedfile = BasicFile(self.outputfile) self.logger.info('Attempting to minimize testcase(es) [%s]', self._crash_hashes_string()) # keep going until either: # a. we find a minimum hd of 1 # b. we run out of discard_chances # c. our discard_chance * minimum hd is less than one (we won't discard anything) # d. we've exhaustively searched all the possible files with hd less # than self.min_distance while not self.min_found and not self.try_exhaustive: if not self.set_discard_chance(): break if not self.set_n_misses(): break got_hit = False while self.consecutive_misses <= self.n_misses_allowed: if self.use_watchdog: # touch the watchdog file so we don't reboot during long # minimizations open(self.watchdogfile, 'w').close() # Fix for BFF-208 if self._time_exceeded(): logger.info( 'Max time for minimization exceeded, ending minimizer early.' ) self.min_found = True break if not self.set_discard_chance(): break if not self.set_n_misses(): break self.swap_bytes() self.total_tries += 1 # have we been at this level before? if not self.files_tried_at_hd.get(self.min_distance): # we've reached a new minimum, so create new sets self.files_tried_at_hd[self.min_distance] = set() self.files_tried_singlebyte_at_hd[ self.min_distance] = set() # have we exhausted all the possible files with smaller hd? possible_files = (2**self.min_distance) - 2 seen_files = len(self.files_tried_at_hd[self.min_distance]) # maybe we're done? if seen_files == possible_files: # we've exhaustively searched everything with hd < # self.min_distance self.logger.info( 'Exhaustively searched all files shorter than %d', self.min_distance) self.min_found = True break # have we exhausted all files that are 1 byte smaller hd? possible_singlebyte_diff_files = self.min_distance singlebyte_diff_files_seen = len( self.files_tried_singlebyte_at_hd[self.min_distance]) # maybe we're done? if singlebyte_diff_files_seen == possible_singlebyte_diff_files: self.logger.info( 'We have tried all %d files that are one byte closer than the current minimum', self.min_distance) self.min_found = True break # remember this file for next time around self.files_tried_at_hd[self.min_distance].add( self.newfuzzed_md5) if self.newfuzzed_hd == (self.min_distance - 1): self.files_tried_singlebyte_at_hd[self.min_distance].add( self.newfuzzed_md5) self.print_intermediate_log() if self.newfuzzed_md5 in self.files_tried: # we've already seen this attempt, so skip ahead to the next one # but still count it as a miss since our math assumes we're putting # the marbles back in the jar after each draw self.consecutive_misses += 1 self.total_misses += 1 continue # we didn't skip ahead, so it must have been new. Remember it # now self.files_tried.add(self.newfuzzed_md5) # we have a better match, write it to a file if not len(self.newfuzzed): raise MinimizerError( 'New fuzzed_content content is empty.') self._write_file() if self.is_same_crash(): # record the result # 1. copy the tempfile filetools.best_effort_move(self.tempfile, self.outputfile) # 2. replace the fuzzed_content file in the crasher with # the current one self.testcase.fuzzedfile = BasicFile(self.outputfile) # 3. replace the current fuzzed_content with newfuzzed self.fuzzed_content = self.newfuzzed self.min_distance = self.newfuzzed_hd got_hit = True if self.min_distance == 1: # we are done self.min_found = True elif self.newfuzzed_hd <= self.exhaustivesearch_threshold: self._set_bytemap() logger.info( 'Exhaustively checking remaining %s bytes' % self.newfuzzed_hd) self.try_exhaustive = True break else: # set up for next iteration self.consecutive_misses = 0 if not self.set_discard_chance(): break if not self.set_n_misses(): break else: # we missed. increment counter and try again self.total_misses += 1 self.consecutive_misses += 1 # Fix for BFF-225 # There may be some situation that causes testcase uniqueness # hashing to break. (e.g. BFF-224 ). Minimizer should bail # if the number of unique crashes encountered exceeds some # threshold. e.g. 20 maybe? if len(self.other_crashes ) > MAX_OTHER_CRASHES and self.seedfile_as_target: logger.info( 'Exceeded maximum number of other crashes (%d), ending minimizer early.', MAX_OTHER_CRASHES) self.min_found = True break if not got_hit: # we are self.confidence_level sure that self.target_size_guess is wrong # so increment it by 1 self.target_size_guess += 1 if self.try_exhaustive: for offset in list(self.bytemap): logger.debug('Verifying byte location: %s' % hex(offset)) self.revert_byte(offset) self._write_file() if self.is_same_crash(): logger.debug('Fuzzed byte at offset %s is not relevant' % hex(offset)) filetools.best_effort_move(self.tempfile, self.outputfile) self.testcase.fuzzedfile = BasicFile(self.outputfile) self.fuzzed_content = self.newfuzzed self.bytemap.remove(offset) # We're done minimizing. Set the bytemap (kept bytes) self._set_bytemap() self.logger.info('We were looking for [%s] ...', self._crash_hashes_string()) for (md5, count) in self.crash_sigs_found.items(): self.logger.info('\t...and found %s\t%d times', md5, count) if self.bytemap: hex_bytemap = [] for offset in self.bytemap: hex_bytemap.append(hex(offset)) self.logger.info('Bytemap: %s', hex_bytemap)
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('-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('-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(): 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(): 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)') 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/bff.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 testcase 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 = "../configs/bff.yaml" logger.debug('WindowsConfig 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) 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.path) else: parser.error('fuzzedfile must be specified') cfg = load_and_fix_config(cfg_file) if options.target: seedfile = BasicFile(options.target) else: seedfile = None min2seed = not options.stringmode filename_modifier = '' retries = 0 debugger_class = msec.MsecDebugger cmd_as_args = get_command_args_list( cfg['target']['cmdline_template'], fuzzed_file.path)[1] # Figure out an appropriate timeout to use based on the config winver = sys.getwindowsversion().major machine = platform.machine() hook_incompatible = winver > 5 or machine == 'AMD64' debugger_timeout = cfg['runner']['runtimeout'] if not hook_incompatible: # Assume the user has tuned timeout to the hook. # Allow extra time for the debugger to run debugger_timeout *= 2 if debugger_timeout < 10: debugger_timeout = 10 cfg['debugger']['runtimeout'] = debugger_timeout with WindowsTestcase(cfg=cfg, seedfile=seedfile, fuzzedfile=fuzzed_file, program=cfg['target']['program'], cmd_template=cfg['target']['cmdline_template'], debugger_timeout=cfg['debugger']['runtimeout'], cmdlist=cmd_as_args, dbg_opts=cfg['debugger'], workdir_base=outdir, keep_faddr=options.keep_uniq_faddr, heisenbug_retries=retries ) 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, 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_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) f = open(metasploit_file, 'wb') try: f.writelines(targetstring) finally: f.close() 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()