def test_cgc_se1_palindrome_raw(): b = os.path.join(bin_location, "tests/cgc/sc1_0b32aa01_01") # test a valid palindrome r = tracer.QEMURunner(binary=b, input="racecar\n") p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content="racecar\n", magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() # make sure the heap base is correct and hasn't been altered from the default nose.tools.assert_true('traced' in simgr.stashes) nose.tools.assert_equal(simgr.traced[0].cgc.allocation_base, 0xb8000000) # make sure there is no crash state nose.tools.assert_true('crashed' not in simgr.stashes) # make sure angr modeled the correct output stdout_dump = simgr.traced[0].posix.dumps(1) nose.tools.assert_true( stdout_dump.startswith("\nWelcome to Palindrome Finder\n\n" "\tPlease enter a possible palindrome: " "\t\tYes, that's a palindrome!\n\n" "\tPlease enter a possible palindrome: ")) # make sure there were no 'Nope's from non-palindromes nose.tools.assert_false("Nope" in stdout_dump) # now test crashing input r = tracer.QEMURunner(binary=b, input="A" * 129) s = p.factory.tracer_state(input_content="A" * 129, magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() nose.tools.assert_true('crashed' in simgr.stashes)
def test_cache_stall(): # test a valid palindrome b = os.path.join(bin_location, "tests/cgc/CROMU_00071") blob = "0c0c492a53acacacacacacacacacacacacac000100800a0b690e0aef6503697d660a0059e20afc0a0a332f7d66660a0059e20afc0a0a332f7fffffff16fb1616162516161616161616166a7dffffff7b0e0a0a6603697d660a0059e21c".decode( 'hex') r = tracer.QEMURunner(binary=b, input=blob) p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content=blob, magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) rex.trace_additions.ZenPlugin.prep_tracer(s) simgr.run() crash_path = t.predecessors[-1] crash_state = simgr.crashed[0] nose.tools.assert_not_equal(crash_path, None) nose.tools.assert_not_equal(crash_state, None) # load it again r = tracer.QEMURunner(binary=b, input=blob) s = p.factory.tracer_state(input_content=blob, magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) rex.trace_additions.ZenPlugin.prep_tracer(s) simgr.run() crash_path = t.predecessors[-1] crash_state = simgr.one_crashed nose.tools.assert_not_equal(crash_path, None) nose.tools.assert_not_equal(crash_state, None)
def __init__(self, binary, crash=None): """ :param binary: path to the binary which crashed :param crash: string of input which crashed the binary """ self.binary = binary self.crash = crash # verify it actually crashes the binary r = tracer.QEMURunner(self.binary, input=self.crash, record_core=True) if not r.crash_mode: raise CrashFuzzerException("input did not crash the binary") self._p = angr.Project(self.binary) self.orig_regs = r.reg_vals self.pool = None self.byte_analysis = dict() self._bases = dict() self.skip_bytes = set() self.skip_sets = set() self.regs_to_numbers = dict() self.used_bytes = set() self.byte_translation_funcs = list() self.byte_translation_calls = dict() self._bit_patterns = dict() self.make_bases() self.run()
def do_trace(proj, test_name, input_data, **kwargs): """ trace, magic, crash_mode, crash_addr = load_cached_trace(proj, "test_blurble") """ fname = os.path.join( bin_location, "tests_data", "runner_traces", "%s_%s_%s.p" % (test_name, os.path.basename(proj.filename), proj.arch.name), ) if os.path.isfile(fname): try: with open(fname, "rb") as f: r = pickle.load(f) if type(r) is tuple and len(r) == 2 and r[1] == TRACE_VERSION: return r[0] except (pickle.UnpicklingError, UnicodeDecodeError): print("Can't unpickle trace - rerunning") if tracer is None: raise Exception( "Tracer is not installed and cached data is not present - cannot run test" ) runner = tracer.QEMURunner(project=proj, input=input_data, **kwargs) r = (runner.trace, runner.magic, runner.crash_mode, runner.crash_addr) with open(fname, "wb") as f: pickle.dump((r, TRACE_VERSION), f, -1) return r
def _concrete_leak_info(self, seed=None): if seed is None: seed = random.randint(0, 2**32) r1 = tracer.QEMURunner(self.binary, input=self.payload, record_magic=True, record_stdout=True, seed=seed) return (r1.stdout, r1.magic)
def __init__(self, bin_input): """bin_input == concrete input to trace execution from.""" self.input = bin_input self._tracer = tracer.QEMURunner(binary=GlobalConfig.target, input=self.input, argv=GlobalConfig.argv, record_stdout=True)
def _get_reg_vals(binary_input_byte): binary, test_input, c = binary_input_byte r = tracer.QEMURunner(binary, input=test_input, record_core=True) if not r.crash_mode: return [c, None] else: reg_vals = dict() for reg in CGC_GENERAL_REGS + ["eip"]: reg_vals[reg] = r.reg_vals[reg] return [c, r.reg_vals]
def test_symbolic_sized_receives(): b = os.path.join(bin_location, "tests/cgc/CROMU_00070") r = tracer.QEMURunner(binary=b, input="hello") p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content="hello", magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() # will except if failed nose.tools.assert_true('crashed' not in simgr.stashes) nose.tools.assert_true('traced' in simgr.stashes) r = tracer.QEMURunner(binary=b, input="\x00" * 20) s = p.factory.tracer_state(input_content="\x00" * 20) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() # will except if failed nose.tools.assert_true('crashed' not in simgr.stashes) nose.tools.assert_true('traced' in simgr.stashes)
def __init__(self, binary, payload, format_infos=None): """ :param binary: path to the binary which is suspect of leaking :param payload: concrete input string to feed to the binary :param format_infos: a list of atoi FormatInfo objects that should be used when analyzing the crash """ self.binary = binary self.payload = payload if not os.access(self.binary, os.X_OK): raise ValueError("\"%s\" binary does not exist or is not executable" % self.binary) # will be set by causes_leak self._leak_path = None remove_options = {so.SUPPORT_FLOATING_POINT} self._runner = tracer.QEMURunner(binary=binary, input=payload) p = angr.Project(binary) p.simos.syscall_library.update(angr.SIM_LIBRARIES['cgcabi_tracer']) s = p.factory.tracer_state(input_content=payload, magic_content=self._runner.magic, preconstrain_input=False, remove_options=remove_options) self._simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=self._runner.crash_mode) self._t = angr.exploration_techniques.Tracer(trace=self._runner.trace) c = angr.exploration_techniques.CrashMonitor(trace=self._runner.trace, crash_mode=self._runner.crash_mode, crash_addr=self._runner.crash_addr) self._simgr.use_technique(c) self._simgr.use_technique(self._t) self._simgr.use_technique(angr.exploration_techniques.Oppologist()) s = self._simgr.one_active ZenPlugin.prep_tracer(s) backing = SimSymbolicMemory(memory_id='file_colorguard') backing.set_state(s) backing.store(0, s.se.BVV(payload)) s.posix.files[0] = SimFile('/dev/stdin', 'r', content=backing, size=len(payload)) # will be overwritten by _concrete_difference if the input was filtered # this attributed is used exclusively for testing at the moment self._no_concrete_difference = not self._concrete_difference() self.leak_ast = None
def set_input(self, arg): self.arg1 = self.make_symbolic_char_args(arg) self.initial_state = self.project.factory.entry_state( args=[self.exe, self.arg1], add_options=angr.options.unicorn, remove_options=angr.options.simplification) self.constrain_input_chars(self.initial_state, self.arg1a, arg) self.string_terminate(self.initial_state, self.arg1a, arg) self.simgr = self.project.factory.simgr(self.initial_state, mode='tracing') self.runner = tracer.QEMURunner(binary=self.exe, input=b'', project=self.project, argv=[self.exe, arg]) self.simgr.use_technique( angr.exploration_techniques.Tracer(trace=self.runner.trace)) self.seen = {} self.reset_comparisons()
def do_trace(proj, test_name, input_data): """ trace, magic, crash_mode, crash_addr = load_cached_trace(proj, "test_blurble") """ fname = os.path.join(bin_location, 'tests_data', 'runner_traces', '%s_%s_%s.p' % (test_name, os.path.basename(proj.filename), proj.arch.name)) if os.path.isfile(fname): with open(fname, 'rb') as f: r = pickle.load(f) assert type(r) is tuple and len(r) == 4 return r if tracer is None: raise Exception("Tracer is not installed and cached data is not present - cannot run test") runner = tracer.QEMURunner(project=proj, input=input_data) r = (runner.trace, runner.magic, runner.crash_mode, runner.crash_addr) with open(fname, 'wb') as f: pickle.dump(r, f, -1) return r
def test_fauxware(): b = os.path.join(bin_location, "tests/x86_64/fauxware") r = tracer.QEMURunner(binary=b, input="A") p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content="A", magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() nose.tools.assert_true('traced' in simgr.stashes)
def test_manual_recursion(): b = os.path.join(bin_location, "tests/cgc", "CROMU_00071") blob = open(os.path.join(bin_location, 'tests_data/', 'crash2731')).read() r = tracer.QEMURunner(binary=b, input=blob) p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content=blob, magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() print simgr.stashes, c.last_state, t.predecessors[0]
def test_crash_addr_detection(): b = os.path.join(bin_location, "tests/i386/call_symbolic") r = tracer.QEMURunner(binary=b, input="A" * 700) p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content="A" * 700, magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() nose.tools.assert_true('crashed' in simgr.stashes) nose.tools.assert_true(simgr.crashed[0].se.symbolic( simgr.crashed[0].regs.ip))
def test_allocation_base_continuity(): correct_out = 'prepare for a challenge\nb7fff000\nb7ffe000\nb7ffd000\nb7ffc000\nb7ffb000\nb7ffa000\nb7ff9000\nb7ff8000\nb7ff7000\nb7ff6000\nb7ff5000\nb7ff4000\nb7ff3000\nb7ff2000\nb7ff1000\nb7ff0000\nb7fef000\nb7fee000\nb7fed000\nb7fec000\ndeallocating b7ffa000\na: b7ffb000\nb: b7fff000\nc: b7ff5000\nd: b7feb000\ne: b7fe8000\ne: b7fa8000\na: b7ffe000\nb: b7ffd000\nc: b7ff7000\nd: b7ff6000\ne: b7ff3000\ne: b7f68000\nallocate: 3\na: b7fef000\n' b = os.path.join(bin_location, "tests/i386/cgc_allocations") r = tracer.QEMURunner(binary=b, input="") p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content="", magic_content=r.magic) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() nose.tools.assert_equal(simgr.traced[0].posix.dumps(1), correct_out)
def test_recursion(): blob = "00aadd114000000000000000200000001d0000000005000000aadd2a1100001d0000000001e8030000aadd21118611b3b3b3b3b3e3b1b1b1adb1b1b1b1b1b1118611981d8611".decode( 'hex') b = os.path.join(os.path.dirname(__file__), "../../binaries/tests/cgc/NRFIN_00075") r = tracer.QEMURunner(binary=b, input=blob) p = angr.make_tracer_project(binary=b) s = p.factory.tracer_state(input_content=blob) simgr = p.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) t = angr.exploration_techniques.Tracer(trace=r.trace) c = angr.exploration_techniques.CrashMonitor(trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(c) simgr.use_technique(t) simgr.use_technique(angr.exploration_techniques.Oppologist()) simgr.run() print simgr.stashes, c.last_state, t.predecessors[0]
from angr.storage.file import SimFileStream import logging logging.getLogger('angr.exploration_techniques').setLevel('DEBUG') path = '/home/kylebot/src/angr-dev/binaries/tests/cgc/cfe_CADET_00003' inp = b'A'*0x80 proj = angr.Project(path) input_file = SimFileStream(name='stdin', ident='aeg_stdin') state = proj.factory.full_init_state(stdin=input_file) state.preconstrainer.preconstrain_file(inp, input_file, set_length=True) #通过插件指定预约束,执行指定的输入 simgr = proj.factory.simgr(state) runner = tracer.QEMURunner(path, inp) #通过qemu获取执行的tracer块地址 tracer_tech = angr.exploration_techniques.Tracer(trace=runner.trace, crash_addr=runner.crash_addr) #通过使用angr的tracer插件,不断记录angr在预约束下的路径是否与qemu相同 simgr.use_technique(tracer_tech) simgr.run() #获取指定state后通过添加约束找到新的路径或者crash内容 found = simgr.traced[0] print('preconstraint:', len(found.preconstrainer.preconstraints)) print('before simplification:', len(found.solver.constraints)) found.solver.simplify() print('after simplification:', len(found.solver.constraints)) found.preconstrainer.remove_preconstraints() print('after remove preconstraints:', len(found.solver.constraints))
def post_analysis(self): regs_to_check = [] if "AST" in self.orig_regs: regs_to_check.append("AST") else: regs_to_check = CGC_GENERAL_REGS for reg in regs_to_check: flag_bits = CGC_FLAG_PAGE >> 12 # shouldn't unset any already good bits orig_bits = self.orig_regs[reg] >> 12 orig_matching_bits = (~(flag_bits ^ orig_bits)) & 0xfffff curr_best_matches = [] for i in self.byte_analysis: ast_vals = [x["AST"] for x in self.byte_analysis[i].reg_vals.values()] for a in ast_vals: bits = a >> 12 matching_bits = ~(flag_bits ^ bits) & 0xfffff if matching_bits & orig_matching_bits != orig_matching_bits: continue else: is_better_than_curr = True for b in list(curr_best_matches): matching_bits_b = ~(flag_bits ^ b) & 0xfffff if matching_bits & matching_bits_b == matching_bits: is_better_than_curr = False elif matching_bits & matching_bits_b == matching_bits_b: curr_best_matches.remove(b) if is_better_than_curr: curr_best_matches.append(bits) # verify it can be pointed at flag page all_bits = reduce(operator.__or__, [~(x ^ flag_bits) & 0xfffff for x in curr_best_matches]) if bin(all_bits).count("1") < 20: continue match_dict = defaultdict(set) # now get all bytes that match each best for i in self.byte_analysis: for b in self.byte_analysis[i].reg_vals: a = self.byte_analysis[i].reg_vals[b][reg] bits = a >> 12 if bits in curr_best_matches: match_dict[bits].add((i, b)) # now pick a random from each set, dump an input and see if we get more output choices = [] for bits in match_dict: choices.append(random.choice(list(match_dict[bits]))) new_input = self.crash for index, b in choices: new_input = self._replace_indices(new_input, b, [index]) r = tracer.QEMURunner(self.binary, input=new_input, record_stdout=True, record_magic=True) new_stdout = r.stdout if len(new_stdout) > len(self.orig_stdout): # okay we have a leak # now we should try to guess what we leaked leak_idx = None for i in range(len(new_stdout)): if new_stdout[i:i+4] in r.magic: leak_idx = i break if leak_idx is None: # need to send to colorguard... l.warning("need to send to colorguard but not implemented") self._raw_payload = new_input else: self._raw_payload = new_input self.output_leak_idx = leak_idx num_good = 0 if self.test_binary(enable_randomness=False): num_good +=1 l.warning("works with no randomness") for i in range(5): if self.test_binary(enable_randomness=True): num_good += 1 l.warning("worked %d/6 times", num_good) if num_good > 0: break return None
def __init__(self, binary, crash=None, pov_file=None, aslr=None, constrained_addrs=None, crash_state=None, prev_path=None, hooks=None, format_infos=None, rop_cache_tuple=None, use_rop=True, explore_steps=0, angrop_object=None): ''' :param binary: path to the binary which crashed :param crash: string of input which crashed the binary :param pov_file: CGC PoV describing a crash :param aslr: analyze the crash with aslr on or off :param constrained_addrs: list of addrs which have been constrained during exploration :param crash_state: an already traced crash state :param prev_path: path leading up to the crashing block :param hooks: dictionary of simprocedure hooks, addresses to simprocedures :param format_infos: a list of atoi FormatInfo objects that should be used when analyzing the crash :param rop_cache_tuple: a angrop tuple to load from :param use_rop: whether or not to use rop :param explore_steps: number of steps which have already been explored, should only set by exploration methods :param angrop_object: an angrop object, should only be set by exploration methods ''' self.binary = binary self.crash = crash self.constrained_addrs = [] if constrained_addrs is None else constrained_addrs self.hooks = {} if hooks is None else hooks self.explore_steps = explore_steps if self.explore_steps > 10: raise CannotExploit( "Too many steps taken during crash exploration") self.project = angr.Project(binary) for addr, proc in self.hooks.iteritems(): self.project.hook(addr, proc) l.debug("Hooking %#x -> %s...", addr, proc.display_name) if self.project.loader.main_object.os == 'cgc': self.project._simos.syscall_library.update( angr.SIM_LIBRARIES['cgcabi_tracer']) # we search for ROP gadgets now to avoid the memory exhaustion bug in pypy # hash binary contents for rop cache name binhash = hashlib.md5(open(self.binary).read()).hexdigest() rop_cache_path = os.path.join( "/tmp", "%s-%s-rop" % (os.path.basename(self.binary), binhash)) if use_rop: if angrop_object is not None: self.rop = angrop_object else: self.rop = self.project.analyses.ROP() if rop_cache_tuple is not None: l.info("loading rop gadgets from cache tuple") self.rop._load_cache_tuple(rop_cache_tuple) elif os.path.exists(rop_cache_path): l.info("loading rop gadgets from cache '%s'", rop_cache_path) self.rop.load_gadgets(rop_cache_path) else: self.rop.find_gadgets(show_progress=False) self.rop.save_gadgets(rop_cache_path) else: self.rop = None self.os = self.project.loader.main_object.os # determine the aslr of a given os and arch if aslr is None: if self.os == "cgc": # cgc has no ASLR, but we don't assume a stackbase self.aslr = False else: # we assume linux is going to enfore stackbased aslr self.aslr = True else: self.aslr = aslr if crash_state is None: # run the tracer, grabbing the crash state remove_options = { so.TRACK_REGISTER_ACTIONS, so.TRACK_TMP_ACTIONS, so.TRACK_JMP_ACTIONS, so.ACTION_DEPS, so.TRACK_CONSTRAINT_ACTIONS, so.LAZY_SOLVES } add_options = { so.MEMORY_SYMBOLIC_BYTES_MAP, so.TRACK_ACTION_HISTORY, so.CONCRETIZE_SYMBOLIC_WRITE_SIZES, so.CONCRETIZE_SYMBOLIC_FILE_READ_SIZES, so.TRACK_MEMORY_ACTIONS } # faster place to check for non-crashing inputs # optimized crash check if self.project.loader.main_object.os == 'cgc': if not tracer.QEMURunner(binary, input=self.crash).crash_mode: if not tracer.QEMURunner(binary, input=self.crash, report_bad_args=True).crash_mode: l.warning("input did not cause a crash") raise NonCrashingInput if pov_file is None and self.crash is None: raise ValueError("must specify crash or pov_file") if pov_file is not None and self.crash is not None: raise ValueError("cannot specify both a pov_file and an crash") if pov_file is not None: input = TracerPoV(pov_file) else: input = self.crash r = tracer.QEMURunner(binary=binary, input=input) s = self.project.factory.tracer_state( input_content=input, magic_content=r.magic, add_options=add_options, remove_options=remove_options, constrained_addrs=self.constrained_addrs) simgr = self.project.factory.simgr(s, save_unsat=True, hierarchy=False, save_unconstrained=r.crash_mode) self._t = angr.exploration_techniques.Tracer(trace=r.trace, resiliency=False, keep_predecessors=2) self._c = angr.exploration_techniques.CrashMonitor( trace=r.trace, crash_mode=r.crash_mode, crash_addr=r.crash_addr) simgr.use_technique(self._c) simgr.use_technique(self._t) simgr.use_technique(angr.exploration_techniques.Oppologist()) s = simgr.one_active ChallRespInfo.prep_tracer(s, format_infos) ZenPlugin.prep_tracer(s) simgr.run() # if there was no crash we'll have to use the previous path's state if 'crashed' in simgr.stashes: # the state at crash time self.state = simgr.crashed[0] # a path leading up to the crashing basic block self.prev = self._t.predecessors[-1] else: self.state = simgr.traced[0] self.prev = self.state zp = self.state.get_plugin('zen_plugin') if 'crashed' not in simgr.stashes and (zp is not None and len( zp.controlled_transmits) == 0): l.warning("input did not cause a crash") raise NonCrashingInput l.debug("done tracing input") else: self.state = crash_state self.prev = prev_path self._t = None self._c = None # list of actions added during exploitation, probably better object for this attribute to belong to self.added_actions = [] # hacky trick to get all bytes #memory_writes = [ ] #for var in self.state.memory.mem._name_mapping.keys(): # memory_writes.extend(self.state.memory.addrs_for_name(var)) memory_writes = sorted(self.state.memory.mem.get_symbolic_addrs()) l.debug("filtering writes") memory_writes = [m for m in memory_writes if m / 0x1000 != 0x4347c] user_writes = [ m for m in memory_writes if any( "stdin" in v for v in self.state.memory.load(m, 1).variables) ] flag_writes = [ m for m in memory_writes if any( v.startswith("cgc-flag") for v in self.state.memory.load(m, 1).variables) ] l.debug("done filtering writes") self.symbolic_mem = self._segment(user_writes) self.flag_mem = self._segment(flag_writes) # crash type self.crash_types = [] # action (in case of a bad write or read) which caused the crash self.violating_action = None l.debug("triaging crash") self._triage_crash()
def __init__(self, binary, crash=None, pov_file=None, aslr=None, constrained_addrs=None, crash_state=None, prev_path=None, hooks=None, format_infos=None, rop_cache_tuple=None, use_rop=True, fast_mode=False, explore_steps=0, angrop_object=None, argv=None, concrete_fs=False, chroot=None, rop_cache_path=None, trace_timeout=10, input_type=CrashInputType.STDIN, port=None, use_crash_input=False, tracer_args=None, initial_state=None): """ :param binary: Path to the binary which crashed. :param crash: String of input which crashed the binary. :param pov_file: CGC PoV describing a crash. :param aslr: Analyze the crash with aslr on or off. :param constrained_addrs: List of addrs which have been constrained during exploration. :param crash_state: An already traced crash state. :param prev_path: Path leading up to the crashing block. :param hooks: Dictionary of simprocedure hooks, addresses to simprocedures. :param format_infos: A list of atoi FormatInfo objects that should be used when analyzing the crash. :param rop_cache_tuple: A angrop tuple to load from. :param use_rop: Whether or not to use rop. :param explore_steps: Number of steps which have already been explored, should only set by exploration methods. :param angrop_object: An angrop object, should only be set by exploration methods. :param argv: Optionally specify argv params (i,e,: ['./calc', 'parm1']). :param concrete_fs: Use the host's filesystem for analysis :param chroot: For concrete_fs: use this host directory as the guest root :param trace_timeout: Time the tracing operation out after this number of seconds """ self.binary = binary self.crash = crash self.constrained_addrs = [] if constrained_addrs is None else constrained_addrs self.hooks = {} if hooks is None else hooks self.explore_steps = explore_steps self.use_crash_input = use_crash_input self.input_type = input_type self.initial_state = initial_state if tracer_args is None: tracer_args = {} if self.explore_steps > 10: raise CannotExploit( "Too many steps taken during crash exploration") self.project = angr.Project(binary) for addr, proc in self.hooks.items(): self.project.hook(addr, proc) l.debug("Hooking %#x -> %s...", addr, proc.display_name) # we search for ROP gadgets now to avoid the memory exhaustion bug in pypy # hash binary contents for rop cache name binhash = hashlib.md5(open(self.binary, 'rb').read()).hexdigest() if not rop_cache_path: rop_cache_path = os.path.join( "/tmp", "%s-%s-rop" % (os.path.basename(self.binary), binhash)) if use_rop: if angrop_object is not None: self.rop = angrop_object else: self.rop = self.project.analyses.ROP(fast_mode=fast_mode) if rop_cache_tuple is not None: l.info("loading rop gadgets from cache tuple") self.rop._load_cache_tuple(rop_cache_tuple) elif os.path.exists(rop_cache_path): l.info("loading rop gadgets from cache '%s'", rop_cache_path) self.rop.load_gadgets(rop_cache_path) else: if angr.misc.testing.is_testing: self.rop.find_gadgets_single_threaded( show_progress=False) else: self.rop.find_gadgets(show_progress=False) self.rop.save_gadgets(rop_cache_path) else: self.rop = None if self.project.loader.main_object.os == 'cgc': self.project.simos.syscall_library.update( angr.SIM_LIBRARIES['cgcabi_tracer']) self.os = self.project.loader.main_object.os if self.os.startswith('UNIX'): self.os = 'unix' # determine the aslr of a given os and arch if aslr is None: if self.os == "cgc": # cgc has no ASLR, but we don't assume a stackbase self.aslr = False else: # we assume linux is going to enforce stack-based ASLR self.aslr = True else: self.aslr = aslr if crash_state is None: # run the tracer, grabbing the crash state remove_options = { so.TRACK_REGISTER_ACTIONS, so.TRACK_TMP_ACTIONS, so.TRACK_JMP_ACTIONS, so.ACTION_DEPS, so.TRACK_CONSTRAINT_ACTIONS, so.LAZY_SOLVES, so.SIMPLIFY_MEMORY_WRITES, so.ALL_FILES_EXIST } add_options = { so.MEMORY_SYMBOLIC_BYTES_MAP, so.TRACK_ACTION_HISTORY, so.CONCRETIZE_SYMBOLIC_WRITE_SIZES, so.CONCRETIZE_SYMBOLIC_FILE_READ_SIZES, so.TRACK_MEMORY_ACTIONS } # faster place to check for non-crashing inputs # optimized crash check if self.os == 'cgc': if not tracer.QEMURunner( binary, input=self.crash, **tracer_args).crash_mode: if not tracer.QEMURunner(binary, input=self.crash, report_bad_args=True, **tracer_args).crash_mode: l.warning("input did not cause a crash") raise NonCrashingInput if pov_file is None and self.crash is None: raise ValueError("must specify crash or pov_file") if pov_file is not None and self.crash is not None: raise ValueError("cannot specify both a pov_file and an crash") if pov_file is not None: input_data = TracerPoV(pov_file) else: input_data = self.crash if input_type == CrashInputType.TCP: # Feed input to the QEMURunner _ = NetworkFeeder("tcp", "localhost", port, input_data) elif input_type == CrashInputType.UDP: raise NotImplementedError() r = tracer.QEMURunner(binary=binary, input=input_data, argv=argv, trace_timeout=trace_timeout, **tracer_args) kwargs = {} if self.project.loader.main_object.os == 'cgc': cgc = True elif self.project.loader.main_object.os.startswith('UNIX'): if argv is None: argv = ['./binary'] kwargs['args'] = argv cgc = False kwargs['concrete_fs'] = concrete_fs kwargs['chroot'] = chroot else: raise ValueError("Can't analyze binary for OS %s" % self.project.loader.main_object.os) socket_queue = None stdin_file = None # the file that will be fd 0 input_file = None # the file that we want to preconstrain if input_type == CrashInputType.TCP: input_file = input_sock = SimFileStream(name="aeg_tcp_in", ident='aeg_stdin') output_sock = SimFileStream(name="aeg_tcp_out") socket_queue = [None, None, None, (input_sock, output_sock) ] # FIXME THIS IS A HACK else: input_file = stdin_file = SimFileStream(name='stdin', ident='aeg_stdin') if initial_state is None: initial_state = self.project.factory.full_init_state( mode='tracing', add_options=add_options, remove_options=remove_options, **kwargs) initial_state.register_plugin( 'posix', SimSystemPosix( stdin=stdin_file, stdout=SimFileStream(name='stdout'), stderr=SimFileStream(name='stderr'), argc=initial_state.posix.argc, argv=initial_state.posix.argv, environ=initial_state.posix.environ, auxv=initial_state.posix.auxv, socket_queue=socket_queue, )) initial_state.register_plugin( 'preconstrainer', SimStatePreconstrainer(self.constrained_addrs)) initial_state.preconstrainer.preconstrain_file(input_data, input_file, set_length=True) if cgc: initial_state.preconstrainer.preconstrain_flag_page( r.magic) # Loosen certain libc limits on symbolic input initial_state.libc.buf_symbolic_bytes = 3000 initial_state.libc.max_symbolic_strchr = 3000 initial_state.libc.max_str_len = 3000 initial_state.libc.max_buffer_size = 16384 simgr = self.project.factory.simulation_manager( initial_state, save_unsat=False, hierarchy=False, save_unconstrained=r.crash_mode) self.initial_state = initial_state self._t = angr.exploration_techniques.Tracer( trace=r.trace, resiliency=False, keep_predecessors=2, crash_addr=r.crash_addr) simgr.use_technique(self._t) simgr.use_technique(angr.exploration_techniques.Oppologist()) if cgc: s = simgr.one_active ChallRespInfo.prep_tracer(s, format_infos) ZenPlugin.prep_tracer(s) simgr.run() # if there was no crash we'll have to use the previous path's state if 'crashed' in simgr.stashes: # the state at crash time self.state = simgr.crashed[0] # a path leading up to the crashing basic block self.prev = self._t.predecessors[-1] else: self.state = simgr.traced[0] self.prev = self.state zp = self.state.get_plugin('zen_plugin') if cgc else None if 'crashed' not in simgr.stashes and zp is not None and len( zp.controlled_transmits) == 0: l.warning("input did not cause a crash") raise NonCrashingInput l.debug("done tracing input") else: self.state = crash_state self.prev = prev_path self._t = None # list of actions added during exploitation, probably better object for this attribute to belong to self.added_actions = [] # hacky trick to get all bytes #memory_writes = [ ] #for var in self.state.memory.mem._name_mapping.keys(): # memory_writes.extend(self.state.memory.addrs_for_name(var)) memory_writes = sorted(self.state.memory.mem.get_symbolic_addrs()) l.debug("filtering writes") memory_writes = [m for m in memory_writes if m // 0x1000 != 0x4347c] user_writes = [ m for m in memory_writes if any("aeg_stdin" in v for v in self.state.memory.load(m, 1).variables) ] flag_writes = [ m for m in memory_writes if any( v.startswith("cgc-flag") for v in self.state.memory.load(m, 1).variables) ] l.debug("done filtering writes") self.symbolic_mem = self._segment(user_writes) self.flag_mem = self._segment(flag_writes) # crash type self.crash_types = [] # action (in case of a bad write or read) which caused the crash self.violating_action = None l.debug("triaging crash") self._triage_crash()
def __init__(self, binary, crash=None): """ :param binary: path to the binary which crashed :param crash: string of input which crashed the binary """ self.binary = binary self.crash = crash # verify it actually crashes the binary r = tracer.QEMURunner(self.binary, input=self.crash, record_stdout=True, record_core=True) if not r.crash_mode: raise CrashFuzzerException("input did not crash the binary") self.orig_stdout = r.stdout self.addr_ast = None try: self._p = angr.Project(self.binary) self.orig_regs = r.reg_vals s = rop_utils.make_symbolic_state(self._p, reg_list=CGC_GENERAL_REGS) self._reg_asts = dict() for r in CGC_GENERAL_REGS: ast = s.se.BVS(r, 32, explicit_name=True) self._reg_asts[r] = ast s.registers.store(r, ast) s.ip = self.orig_regs["eip"] all_succ = self._p.factory.successors(s, num_inst=1).all_successors if len(all_succ) == 0: raise CannotExploit("no successors") succ = all_succ[0] for a in succ.history.recent_actions: if a.type == "mem" and a.action == "read": dependencies = a.addr.ast.variables self.addr_ast = a.addr.ast self.reg_deps = dependencies | {"AST"} self.orig_regs = self._fix_reg_vals(self.orig_regs) l.debug("REG DEPS: %s", self.reg_deps) except CLEError as e: l.warning("CLEError: %s", e) pass if self.addr_ast is None: self.reg_deps = set(CGC_GENERAL_REGS) l.warning("couldn't find read addr depenency") self.pool = None self.byte_analysis = dict() self._bases = dict() self.skip_bytes = set() self.skip_sets = set() self.regs_to_numbers = dict() self.used_bytes = set() self.byte_translation_funcs = list() self.byte_translation_calls = dict() self._bit_patterns = dict() self._raw_payload = None self.output_leak_idx = None self.cgc_type = 2 self.make_bases() self.run() self.post_filter() self.post_analysis()
def _quick_triage(self, binary, crash): l.debug("quick triaging crash against '%s'", binary) arbitrary_syscall_arg = False r = tracer.QEMURunner(binary, crash, record_trace=True, use_tiny_core=True, record_core=True) self.bb_count = len(r.trace) if not r.crash_mode: # try again to catch bad args r = tracer.QEMURunner(binary, crash, report_bad_args=True, record_core=True) arbitrary_syscall_arg = True if not r.crash_mode: raise NonCrashingInput("input did not cause a crash") l.debug("detected an arbitrary transmit or receive") if r.os != "cgc": raise ValueError("QuickCrash is only available for CGC binaries") if r.is_multicb: project = angr.Project(binary[r.crashed_binary]) else: project = angr.Project(binary) # triage the crash based of the register values and memory at crashtime # look for the most valuable crashes first pc = r.reg_vals['eip'] l.debug('crash occured at %#x', pc) if arbitrary_syscall_arg: l.debug("checking which system call had bad args") syscall_num = r.reg_vals['eax'] vulns = {2: Vulnerability.ARBITRARY_TRANSMIT, \ 3: Vulnerability.ARBITRARY_RECEIVE} # shouldn't ever happen but in case it does if syscall_num not in vulns: return pc, None return pc, vulns[syscall_num] l.debug("checking if ip is null") if pc < 0x1000: return pc, Vulnerability.NULL_DEREFERENCE l.debug("checking if ip register points to executable memory") start_state = project.factory.entry_state( addr=pc, add_options={so.TRACK_MEMORY_ACTIONS}) # was ip mapped? ip_overwritten = False try: perms = start_state.memory.permissions(pc) # check if the execute bit is marked, this is an AST l.debug("ip points to mapped memory") if not perms.symbolic and not ((perms & 4) == 4).args[0]: l.debug("ip appears to be uncontrolled") return pc, Vulnerability.UNCONTROLLED_IP_OVERWRITE except angr.SimMemoryError: ip_overwritten = True if ip_overwritten: # let's see if we can classify it as a partial overwrite # this is done by seeing if the most signifigant bytes of # pc could be a mapping cgc_object = project.loader.all_elf_objects[0] base = cgc_object.min_addr & 0xff000000 while base < cgc_object.max_addr: if pc & 0xff000000 == base: l.debug("ip appears to only be partially controlled") return pc, Vulnerability.PARTIAL_IP_OVERWRITE base += 0x01000000 l.debug("ip appears to be completely controlled") return pc, Vulnerability.IP_OVERWRITE # wasn't an ip overwrite, check reads and writes l.debug("checking if a read or write caused the crash") # set registers start_state.regs.eax = r.reg_vals['eax'] start_state.regs.ebx = r.reg_vals['ebx'] start_state.regs.ecx = r.reg_vals['ecx'] start_state.regs.edx = r.reg_vals['edx'] start_state.regs.esi = r.reg_vals['esi'] start_state.regs.edi = r.reg_vals['edi'] start_state.regs.esp = r.reg_vals['esp'] start_state.regs.ebp = r.reg_vals['ebp'] next_pth = project.factory.successors(start_state, num_inst=1).successors[0] posit = None for a in next_pth.history.recent_actions: if a.type == 'mem': target_addr = start_state.se.eval(a.addr) if target_addr < 0x1000: l.debug("attempt to write or read to address of NULL") return pc, Vulnerability.NULL_DEREFERENCE # we will take the last memory action, so things like an `add` instruction # are triaged as a 'write' opposed to a 'read' if a.action == 'write': l.debug("write detected") posit = Vulnerability.WRITE_WHAT_WHERE # if it's trying to write to a non-writeable address which is mapped # it's most likely uncontrolled if target_addr & 0xfff00000 == 0: l.debug( "write attempt at a suspiciously small address, assuming uncontrolled" ) return pc, Vulnerability.UNCONTROLLED_WRITE try: perms = start_state.memory.permissions(target_addr) if not perms.symbolic and not ( (perms & 2) == 2).args[0]: l.debug( "write attempt at a read-only page, assuming uncontrolled" ) return pc, Vulnerability.UNCONTROLLED_WRITE except angr.SimMemoryError: pass elif a.action == 'read': l.debug("read detected") posit = Vulnerability.ARBITRARY_READ else: # sanity checking raise ValueError( "unrecognized memory action encountered %s" % a.action) if posit is None: l.debug("crash was not able to be triaged") posit = 'unknown' # returning 'unknown' if crash does not fall into one of our obvious categories return pc, posit
def __init__(self, binary, payload): """ :param binary: path to the binary which is suspect of leaking :param payload: concrete input string to feed to the binary """ self.binary = binary self.payload = payload if not os.access(self.binary, os.X_OK): raise ValueError( "\"%s\" binary does not exist or is not executable" % self.binary) # will be set by causes_leak self._leak_path = None self._runner = tracer.QEMURunner(binary=binary, input=payload) # load the binary self.project = angr.Project(binary) self.project.simos.syscall_library.update( angr.SIM_LIBRARIES['cgcabi_tracer']) # set up the state for analysis remove_options = {angr.options.SUPPORT_FLOATING_POINT} add_options = angr.options.unicorn | { angr.options.CGC_NO_SYMBOLIC_RECEIVE_LENGTH, angr.options.UNICORN_THRESHOLD_CONCRETIZATION, angr.options.REPLACEMENT_SOLVER } state = self.project.factory.full_init_state( remove_options=remove_options, add_options=add_options) # Make our own special posix state.register_plugin( 'posix', SimSystemPosix(stdin=SimFileStream('stdin', content=payload), stdout=SimFileStream('stdout'), stderr=SimFileStream('stderr'))) # Create the preconstrainer plugin state.register_plugin('preconstrainer', SimStatePreconstrainer()) state.preconstrainer.preconstrain_flag_page(self._runner.magic) # Set up zen ZenPlugin.prep_tracer(state) # Make the simulation manager self._simgr = self.project.factory.simulation_manager( state, save_unsat=True, hierarchy=False, save_unconstrained=self._runner.crash_mode) self._t = angr.exploration_techniques.Tracer(trace=self._runner.trace, resiliency=False) self._simgr.use_technique(self._t) self._simgr.use_technique(angr.exploration_techniques.Oppologist()) # will be overwritten by _concrete_difference if the input was filtered # this attributed is used exclusively for testing at the moment self._no_concrete_difference = not self._concrete_difference() self.leak_ast = None