def point_to_flag(self): ''' Create a testcase which points an arbitrary-read crash at the flag page. ''' if not self.one_of( [Vulnerability.ARBITRARY_READ, Vulnerability.ARBITRARY_TRANSMIT]): raise CannotExploit( "only arbitrary-reads can be exploited this way") violating_actions = [] if self.one_of([Vulnerability.ARBITRARY_READ]): if self.violating_action: violating_actions.append( (self.state, self.violating_action.addr)) zp = self.state.get_plugin('zen_plugin') if zp is not None: for st, addr in zp.controlled_transmits: st.preconstrainer.remove_preconstraints() violating_actions.append((st, addr)) for st, va in violating_actions: try: cp = self._get_state_pointing_to_flag(st, va) self._reconstrain_flag_data(cp) yield ChallRespInfo.atoi_dumps(cp) except CannotExploit: l.warning("crash couldn't be pointed at flag skipping") pass # look for contiguous flag bytes of length 4 or longer and try to leak only one max_tries = 20 num_tries = 0 for start, length in self.flag_mem.items(): if length < 4: continue data = self.state.memory.load(start, length) four_flag_offset = self._four_flag_bytes_offset(data) if four_flag_offset is not None: leak_addr = start + four_flag_offset l.debug("found flag at addr %#x", leak_addr) for st, va in violating_actions: if num_tries > max_tries: l.warning("passed the maximum number of tries") break num_tries += 1 try: cp = self._get_state_pointing_to_addr( st, va, leak_addr) self._reconstrain_flag_data(cp) l.debug("pointed successfully") yield ChallRespInfo.atoi_dumps(cp) # okay we got one we are done return except CannotExploit: l.warning("crash couldn't be pointed at flag skipping") pass
def _generate_formula(self, extra_vars_to_solve=None): """ This function is used to generate the equations which are inserted inside C exploits """ if extra_vars_to_solve is None: extra_vars_to_solve = [] st = self.crash.state.copy() self._prepare_chall_resp(st) # get variables representing stdin stdin = st.posix.get_file(0) length = st.se.eval(stdin.pos) ft = st.se._solver._merged_solver_for(lst=[self._mem] + extra_vars_to_solve) # filter out constants the_vars = set() split = ft.split() for solver in split: if len(solver.variables) > 1: the_vars.update(solver.variables) ft = st.se._solver._merged_solver_for(names=the_vars) self._payload_len = length self._raw_payload = ChallRespInfo.atoi_dumps(st) self._create_solvers(ft, extra_vars_to_solve)
def _explore_arbitrary_read(self, path_file=None): # crash type was an arbitrary-read, let's point the violating address at a # symbolic memory region largest_regions = sorted(self.symbolic_mem.items(), key=operator.itemgetter(1), reverse=True) min_read = self.state.se.min(self.violating_action.addr) max_read = self.state.se.max(self.violating_action.addr) largest_regions = map(operator.itemgetter(0), largest_regions) # filter addresses which fit between the min and max possible address largest_regions = filter(lambda x: (min_read <= x) and (x <= max_read), largest_regions) # populate the rest of the list with addresses from the binary min_addr = self.project.loader.main_object.min_addr max_addr = self.project.loader.main_object.max_addr pages = range(min_addr, max_addr, 0x1000) pages = filter(lambda x: (min_read <= x) and (x <= max_read), pages) read_addr = None constraint = None for addr in largest_regions + pages: read_addr = addr constraint = self.violating_action.addr == addr if self.state.se.satisfiable(extra_constraints=(constraint, )): break constraint = None if constraint is None: raise CannotExploit( "unable to find suitable read address, cannot explore") self.state.add_constraints(constraint) l.debug("constraining input to read from address %#x", read_addr) l.info( "starting a new crash exploration phase based off the crash at address 0x%x", self.violating_action.ins_addr) new_input = ChallRespInfo.atoi_dumps(self.state) if path_file is not None: l.info("dumping new crash evading input into file '%s'", path_file) with open(path_file, 'w') as f: f.write(new_input) # create a new crash object starting here use_rop = False if self.rop is None else True self.__init__(self.binary, new_input, explore_steps=self.explore_steps + 1, constrained_addrs=self.constrained_addrs + [self.violating_action], use_rop=use_rop, angrop_object=self.rop)
def _explore_arbitrary_write(self, path_file=None): # crash type was an arbitrary-write, this routine doesn't care about taking advantage # of the write it just wants to try to find a more valuable crash by pointing the write # at some writable memory # find a writable data segment elf_objects = self.project.loader.all_elf_objects assert len(elf_objects ) > 0, "target binary is not ELF or CGC, unsupported by rex" min_write = self.state.solver.min(self.violating_action.addr) max_write = self.state.solver.max(self.violating_action.addr) segs = [] for eobj in elf_objects: segs.extend(filter(lambda s: s.is_writable, eobj.segments)) segs = [ s for s in segs if s.min_addr <= min_write <= s.max_addr or min_write <= s.min_addr <= max_write ] write_addr = None constraint = None for seg in segs: for page in range(seg.min_addr, seg.max_addr, 0x1000): write_addr = page constraint = self.violating_action.addr == page if self.state.solver.satisfiable( extra_constraints=(constraint, )): break constraint = None if constraint is None: raise CannotExploit("Cannot point write at any writeable segments") self.state.add_constraints(constraint) l.debug("constraining input to write to address %#x", write_addr) l.info( "starting a new crash exploration phase based off the crash at address %#x", self.violating_action.ins_addr) new_input = ChallRespInfo.atoi_dumps(self.state) if path_file is not None: l.info("dumping new crash evading input into file '%s'", path_file) with open(path_file, 'w') as f: f.write(new_input) use_rop = False if self.rop is None else True self.__init__(self.binary, new_input, explore_steps=self.explore_steps + 1, constrained_addrs=self.constrained_addrs + [self.violating_action], use_rop=use_rop, angrop_object=self.rop)
def _generate_formula(self, extra_vars_to_solve=None): """ This function is used to generate the equations which are inserted inside C exploits """ if extra_vars_to_solve is None: extra_vars_to_solve = [] st = self.crash.state.copy() self._prepare_chall_resp(st) # get variables representing stdin length = st.solver.eval(st.posix.fd[0].read_pos) ft = st.solver._solver._merged_solver_for(lst=[self._mem] + extra_vars_to_solve) # filter out constants the_vars = set() split = ft.split() for solver in split: if len(solver.variables) > 1: the_vars.update(solver.variables) ft = st.solver._solver._merged_solver_for(names=the_vars) # try to use original crash_input instead of random ones if some input is still symbolic at this stage size = st.solver.eval(st.posix.stdin.size) # there is a weird crash type called FakeCrash which is totally dummy # have no idea why we still keep it if hasattr( self.crash, 'use_crash_input') and self.crash.use_crash_input and hasattr( self.crash, 'crash'): for i in range(size): byte_value = st.posix.stdin.load(i, 1) if not st.solver.symbolic(byte_value): continue constraint = (byte_value == self.crash.crash[i]) if not st.solver.satisfiable(extra_constraints=[constraint]): continue st.solver.add(constraint) self._payload_len = length self._raw_payload = ChallRespInfo.atoi_dumps(st) self._create_solvers(ft, extra_vars_to_solve)