def call(funcName, parsedArgs, constraint, assertion): # Get target system if (Arch.currentBinType == Arch.BinaryType.X86_ELF): syscall = Linux32.available.get(funcName) system = sysLinux32 elif (Arch.currentBinType == Arch.BinaryType.X64_ELF): syscall = Linux64.available.get(funcName) system = sysLinux64 else: error("Binary type '{}' not supported yet".format(Arch.currentBinType)) return if (not syscall): error("Syscall '{}' not supported for system '{}'".format(\ funcName, system)) return if (len(parsedArgs) != len(syscall.args)): error("Error. Wrong number of arguments") return # Build syscall res = _build_syscall(syscall.buildFunc, parsedArgs, constraint, assertion) # Print result if (not res): print(string_bold("\n\tNo matching ROPChain found")) else: print(string_bold("\n\tFound matching ROPChain\n")) badBytes = constraint.getBadBytes() if (OUTPUT == OUTPUT_CONSOLE): print(res.strConsole(Arch.bits(), badBytes)) elif (OUTPUT == OUTPUT_PYTHON): print(res.strPython(Arch.bits(), badBytes))
def __init__(self, num, ind=0, size=None): Expr.__init__(self) if (isinstance(num, SSAReg)): self.reg = SSAReg(num.num, num.ind) else: self.reg = SSAReg(num, ind) if (size is None): size = Arch.bits() self.size = size
def addOffset(self, offset): new_addresses = [] for addr in self.addrList: new = addr + offset # Check if offset isn't too big if( new >= (0x1 << Arch.bits())\ or new < 0 ): return False new_addresses.append(new) self.addrList = new_addresses return True
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 pwn(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON global EXPLOIT_LMAX if( not args ): print_help() return # Check if gadgets have been loaded in the DB if( not Database.gadgets ): error("Oops. You have to load gadgets before pwning ;) ") return # Set default output format OUTPUT = OUTPUT_CONSOLE clmax = EXPLOIT_LMAX seenOutput = False seenLmax = False seenBadBytes = False seenVerbose = False seenOutfile = False seenPaddingByte = False seenPaddingLen = False constraint = Constraint() assertion = Assertion() subcommand = None outfile = None paddingByteStr = None paddingLen = 0 i = 0 while i < len(args): # Parse options if( args[i][0] == '-' ): if( args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]): if( seenBadBytes ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing bad bytes after option '"+args[i]+"'") return seenBadBytes = True (success, res) = parse_bad_bytes(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(BadBytes(res)) elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ): if( seenLmax ): error("Error. '" + arg + "' option should be used only once.") return if( i+1 >= len(args)): error("Error. Missing output format after option '"+arg+"'") return try: clmax = int(args[i+1]) if( clmax < Arch.octets() ): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: error("Error. '" + args[i+1] +"' bytes is not valid") return i = i +2 seenLmax = True elif( args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]): if( seenOutput ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing output format after option '"+args[i]+"'") return if( args[i+1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i+1] seenOutput = True i += 2 else: error("Error. Unknown output format: {}".format(args[i+1])) return elif( args[i] in [OPTION_OUTFILE_SHORT, OPTION_OUTFILE] ): if( seenOutfile ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing file name after option '"+args[i]+"'") return seenOutfile = True outfile = args[i+1] i += 2 elif( args[i] in [OPTION_PADDING_BYTE, OPTION_PADDING_BYTE_SHORT] ): if( seenPaddingByte ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing byte after option '"+args[i]+"'") return seenPaddingByte = True try: paddingByte = int(args[i+1], 16) except: error("Error. '{}' is not a valid byte".format(args[i+1])) return if( paddingByte > 0xff ): error("Error. '{}' can't be stored in one byte".format(args[i+1])) return paddingByteStr = "\\x"+format(paddingByte, "02x") i += 2 elif( args[i] in [OPTION_PADDING_LEN, OPTION_PADDING_LEN_SHORT] ): if( seenPaddingLen ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing length after option '"+args[i]+"'") return seenPaddingLen = True try: paddingLen = int(args[i+1], 10) except: try: paddingLen = int(args[i+1], 16) except: error("Error. '{}' is not a valid byte".format(args[i+1])) return if( paddingLen < 0 ): error("Error. Padding length must be > 0") return i += 2 elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return elif( args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]): seenVerbose = True i += 1 else: error("Error. Unknown option '{}'".format(args[i])) return # Parse a subcommand else: # Before parsing, check arguments # Check arguments if( seenPaddingByte and not seenPaddingLen ): error("Error. You have to specify padding length if you provide a padding byte. See the '-pl,--padding-length' option") return # Parse subcommand verbose_mode(seenVerbose) payload = None if( args[i] == CMD_DELIVER_SHELLCODE ): payload = dshell(args[i+1:], constraint, assertion, lmax=clmax) else: error("Error. Unknown subcommand : '{}'".format(args[i])) verbose_mode(False) return verbose_mode(False) # Print result if( payload == "help"): return elif( not payload is None ): print(string_bold("\n\tBuilt exploit - {} bytes\n".format(payload.len_bytes))) # Normal output if( OUTPUT == OUTPUT_CONSOLE ): payload_string = payload.strConsole(Arch.bits(), constraint.getBadBytes()) elif( OUTPUT == OUTPUT_PYTHON ): payload_string = payload.strPython(Arch.bits(), constraint.getBadBytes(), \ noTab=seenOutfile, paddingByteStr=paddingByteStr, paddingLen=paddingLen) # Output to file if( outfile ): try: f = open(outfile, "w") f.write(remove_colors(payload_string)) f.close() print(string_bold("\n\tWrote payload in file '{}'\n".format(outfile))) except: error("Error. Couldn't not write payload in file '{}'".format(outfile)) else: print(payload_string) else: error("Couldn't generate exploit") return # Test parsing result if( subcommand is None ): error("Missing subcommand") return
def pwn(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON global EXPLOIT_LMAX if (not args): print_help() return # Check if gadgets have been loaded in the DB if (not Database.gadgets): error("Oops. You have to load gadgets before pwning ;) ") return # Set default output format OUTPUT = OUTPUT_CONSOLE clmax = EXPLOIT_LMAX seenOutput = False seenLmax = False seenBadBytes = False seenVerbose = False constraint = Constraint() assertion = Assertion() subcommand = None i = 0 while i < len(args): # Parse options if (args[i][0] == '-'): if (args[i] in [OPTION_BAD_BYTES, OPTION_BAD_BYTES_SHORT]): if (seenBadBytes): error("Error. '" + args[i] + "' option should be used only once") return if (i + 1 >= len(args)): error("Error. Missing bad bytes after option '" + args[i] + "'") return seenBadBytes = True (success, res) = parse_bad_bytes(args[i + 1]) if (not success): error(res) return i = i + 2 constraint = constraint.add(BadBytes(res)) elif (args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT): if (seenLmax): error("Error. '" + arg + "' option should be used only once.") return if (i + 1 >= len(args)): error("Error. Missing output format after option '" + arg + "'") return try: clmax = int(args[i + 1]) if (clmax < Arch.octets()): raise Exception() # Convert number of bytes into number of ropchain elements clmax /= Arch.octets() except: error("Error. '" + args[i + 1] + "' bytes is not valid") return i = i + 2 seenLmax = True elif (args[i] in [OPTION_OUTPUT, OPTION_OUTPUT_SHORT]): if (seenOutput): error("Option '{}' should be used only once".format( args[i])) return if (i + 1 >= len(args)): error("Error. Missing output format after option '" + args[i] + "'") return if (args[i + 1] in [OUTPUT_CONSOLE, OUTPUT_PYTHON]): OUTPUT = args[i + 1] seenOutput = True i += 2 else: error("Error. Unknown output format: {}".format(args[i + 1])) return elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return elif (args[i] in [OPTION_VERBOSE, OPTION_VERBOSE_SHORT]): seenVerbose = True i += 1 else: error("Error. Unknown option '{}'".format(args[i])) return # Parse a subcommand else: verbose_mode(seenVerbose) payload = None if (args[i] == CMD_DELIVER_SHELLCODE): payload = dshell(args[i + 1:], constraint, assertion, lmax=clmax) else: error("Error. Unknown subcommand : '{}'".format(args[i])) verbose_mode(False) return verbose_mode(False) # Print result if (payload): print( string_bold("\n\tBuilt exploit - {} bytes\n".format( payload.len_bytes))) if (OUTPUT == OUTPUT_CONSOLE): print( payload.strConsole(Arch.bits(), constraint.getBadBytes())) elif (OUTPUT == OUTPUT_PYTHON): print( payload.strPython(Arch.bits(), constraint.getBadBytes())) else: error("Couldn't generate exploit") return # Test parsing result if (subcommand is None): error("Missing subcommand") return
def _adjust_ret(qtype, arg1, arg2, env, n): """ Search with basic but adjust the bad returns they have """ global LMAX ID = StrategyType.ADJUST_RET ## Test for special cases # Test lmax if (env.getLmax() <= 0): return FailRecord(lmax=True) # Limit number of calls to ... elif (env.nbCalls(ID) >= 2): return FailRecord() # Test for ip # Reason: can not adjust ip if ip is the # target of the query :/ elif (arg1 == Arch.ipNum()): return FailRecord() # Set env env.addCall(ID) saved_adjust_ret = env.getImpossible_adjust_ret().copy() ######################################## res = [] res_fail = FailRecord() padding = env.getConstraint().getValidPadding(Arch.octets()) # Get possible gadgets constraint = env.getConstraint() env.setConstraint(constraint.add(Chainable(jmp=True, call=True))) possible = _basic(qtype, arg1, arg2, env, 10 * n) env.setConstraint(constraint) if (not possible): res_fail.merge(possible) possible = [] # Try to adjust them for chain in possible: g = chain.chain[0] ret_reg = g.retValue.reg.num # Check if we already know that ret_reg can't be adjusted if (env.checkImpossible_adjust_ret(ret_reg)): continue #Check if ret_reg not modified within the gadget elif (ret_reg in g.modifiedRegs()): continue # Check if stack is preserved elif (g.spInc is None): continue # Find adjustment if (g.spInc < 0): offset = -1 * g.spInc padding_length = 0 else: padding_length = g.spInc / Arch.octets() if (g.retType == RetType.JMP): offset = 0 else: offset = Arch.octets() if (isinstance(arg1, int)): arg1_reg = arg1 else: arg1_reg = arg1[0] # Get adjustment gadgets env.setConstraint(constraint.add(RegsNotModified([arg1_reg]))) saved_lmax = env.getLmax() env.setLmax(LMAX) adjust_gadgets = _search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), env, n=1) env.setConstraint(constraint) env.setLmax(saved_lmax) if (not adjust_gadgets): res_fail.merge(adjust_gadgets) continue else: adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\ constraint.getBadBytes(), Arch.bits()), 16) # Find gadgets to put the gadget address in the jmp/call register if (isinstance(arg2, int)): arg2_reg = arg2 else: arg2_reg = arg2[0] env.setConstraint(constraint.add(RegsNotModified([arg2_reg]))) env.subLmax(1 + padding_length) env.pushComment( StrategyType.CSTtoREG_POP, "Address of " + string_bold(str(adjust_gadgets[0].chain[0]))) adjust = _search(QueryType.CSTtoREG, ret_reg, adjust_addr, env, n=1) env.popComment(StrategyType.CSTtoREG_POP) env.addLmax(1 + padding_length) env.setConstraint(constraint) if (adjust): res.append(adjust[0].addGadget(g).addPadding(padding, n=padding_length)) if (len(res) >= n): break else: # Update the search record to say that reg_ret cannot be adjusted env.addImpossible_adjust_ret(ret_reg) res_fail.merge(adjust) ######################################## # Restore env env.impossible_adjust_ret = saved_adjust_ret env.removeCall(ID) return res if res else res_fail
def _adjust_ret(qtype, arg1, arg2, constraint, assertion, n, clmax=LMAX, record=None, comment=""): """ Search with basic but adjust the bad returns they have """ # Test clmax if (clmax <= 0): return [] # Test for ip if (arg1 == Arch.ipNum()): return [] # Test for search record if (record is None): record = SearchRecord() res = [] possible = _basic(qtype, arg1, arg2, \ constraint.add(Chainable(jmp=True, call=True)), assertion, n) padding = constraint.getValidPadding(Arch.currentArch.octets) for chain in possible: g = chain.chain[0] ret_reg = g.retValue.reg.num # Check if we already know that ret_reg can't be adjusted if (record.impossible_AdjustRet.check(ret_reg)): continue #Check if ret_reg not modified within the gadget if (ret_reg in g.modifiedRegs()): continue # Check if stack is preserved if (g.spInc is None): continue # Find adjustment if (g.spInc < 0): offset = -1 * g.spInc padding_length = 0 else: padding_length = g.spInc if (g.retType == RetType.JMP): offset = 0 else: offset = Arch.octets() adjust_gadgets = search(QueryType.MEMtoREG, Arch.ipNum(), \ (Arch.spNum(),offset), constraint.add(RegsNotModified([arg1])), assertion, n=1, record=record) if (not adjust_gadgets): continue else: adjust_addr = int(validAddrStr(adjust_gadgets[0].chain[0],\ constraint.getBadBytes(), Arch.bits()), 16) # Put the gadget address in the register adjust = search(QueryType.CSTtoREG, ret_reg, adjust_addr, \ constraint.add(RegsNotModified([arg2[0]])), assertion, n=1, clmax=clmax-len(chain),record=record,\ comment="Address of "+string_bold(str(adjust_gadgets[0].chain[0]))) if (adjust): res.append(adjust[0].addGadget(g).addPadding(padding, n=padding_length)) if (len(res) >= n): return res else: # Update the search record to say that reg_ret cannot be adjusted record.impossible_AdjustRet.add(ret_reg) return res
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)