def select_shellcode(arch): """ Returns shellcode pair (shellcode, description) """ if (not shellcodes[arch]): error("No shellcodes available for architecture " + arch) return (None, None) list_shellcodes(arch) print("") choice = None ok = False sys.stdout.write('\t' + string_ropg('> ') + 'Select a shellcode:\n') while (not ok): choice_input = prompt(u" > ") try: choice = int(choice_input) if (choice >= 1 and choice <= len(shellcodes[arch])): ok = True else: print(string_special("\tError. Invalid choice")) except: ok = False return shellcodes[arch][choice - 1]
def getGadgets(filename): """ Returns a list of gadgets extracted from a file Precondition: the file exists Returns ------- list of pairs (addr, asm) if succesful None if failure """ ropgadget = "ROPgadget" notify("Executing ROPgadget as: " + ropgadget ) try: p = subprocess.Popen([ropgadget,"--binary",filename,"--dump", "--all"],stdout=subprocess.PIPE) except Exception as e: error("Could not execute '" +ropgadget+ " --binary " + filename + " --dump --all'") print("\tError message is: " + str(e)) print("\n\t(Maybe check/update your config with the 'config' command,\n\t or make sure you have the last ROPgadget version installed)") return None # Get the gadget list # Pairs (address, raw_asm) first = True count = 0 res = [] for l in p.stdout.readlines(): if("0x" in l): arr = l.split(' ') addr = arr[0] raw = b16decode(arr[-1].upper().strip()) res.append((int(addr,16), raw)) count += 1 notify("Finished : %d gadgets generated" % (count)) return res
def write_shellcodes(filename, data): try: f = open(filename, 'w') except: error("Couldn't save shellcodes: file {} can't be opened".format( filename)) return for (shellcode, info) in data: f.write(shellcode.encode('hex') + '\n' + info + '\n') f.close()
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 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 listSyscalls(args): if( not args ): systems = availableSystems else: systems = [] for arg in args: if( arg in availableSystems ): systems.append(arg) else: error("Unknown system: '{}'".format(arg)) return for system in list(set(systems)): if( system == sysLinux32 ): Linux32.print_available() elif( system == sysLinux64 ): Linux64.print_available()
def _build_syscall(funcPointer, args, constraint, assertion, optimizeLen=False): if (len(args) == 0): return funcPointer(constraint, assertion, optimizeLen=optimizeLen) elif (len(args) == 1): return funcPointer(args[0], constraint, assertion, optimizeLen=optimizeLen) elif (len(args) == 2): return funcPointer(args[0], args[1], constraint, assertion, optimizeLen=optimizeLen) elif (len(args) == 3): return funcPointer(args[0], args[1], args[2], constraint, assertion, optimizeLen=optimizeLen) elif (len(args) == 4): return funcPointer(args[0], args[1], args[2], args[3], constraint, assertion, optimizeLen=optimizeLen) elif (len(args) == 5): return funcPointer(args[0], args[1], args[2], args[3], args[4], constraint, assertion, optimizeLen=optimizeLen) else: error("{}-arguments calls not supported".format(len(args))) return []
def exploit_mode(): """ Returns ------- True if ROPGenerator must continue False if ROPGenerator must be closed """ finish = False while (not finish): try: # Get command user_input = promptSession.prompt() args = user_input.split() argslen = len(args) if (argslen > 0): command = args[0] else: command = None continue # Execute command if (command == CMD_PWN): pwn(args[1:]) elif (command == CMD_SHELLCODE): shellcode(args[1:]) elif (command == CMD_SYSCALL): syscall(args[1:]) elif (command == CMD_CALL): call(args[1:]) elif (command == CMD_EXIT): return False elif (command == CMD_HELP): print(helpStr) elif (command == CMD_MAIN): finish = True else: error("Unknown command '{}'".format(command)) if (command != None): print('') except KeyboardInterrupt: pass return True
def main(): print(string_ropg(string_bold(ASCII_art))) initLogs() finish = False promptSession = PromptSession(ANSI(u"(" + string_ropg(u'main') + u")> ")) while (not finish): try: user_input = promptSession.prompt() args = user_input.split() argslen = len(args) if (argslen > 0): command = args[0] else: command = None continue if (command == CMD_LOAD): load(args[1:]) elif (command == CMD_EXIT): finish = True elif (command == CMD_HELP): print(helpStr) elif (command == CMD_SEARCH): if (not Database.gadgets): error( "You have to load gadgets before entering semantic-mode" ) elif (not semantic_mode()): finish = True elif (command == CMD_EXPLOIT): if (not exploit_mode()): finish = True else: error("Unknown command '{}'".format(command)) if (command != None): print('') except KeyboardInterrupt: pass closeLogs() save_shellcodes() exit(0)
def strConsole(self, bits, badBytes=[]): res = '' for element in self.chain: if (not isinstance(element, Gadget)): element_str = string_special('0x' + format( self.paddings[element][0], '0' + str(bits / 4) + 'x')) element_str += ' (' + self.paddings[element][1] + ')' else: valid_addr_str = validAddrStr(element, badBytes, bits) if (not valid_addr_str): error( "Error! ROP-Chain gadget with no valid address. THIS SHOULD NOT HAPPEND (please report the bug for fixes)" ) return '' element_str = string_special(valid_addr_str) +\ " (" + string_bold(element.asmStr) + ")" if (res != ''): res += "\n\t" + element_str else: res += "\t" + element_str return res
def list_shellcodes(arch): global shellcodes if (arch not in Arch.available): error("Error. Architecture {} is not supported".format(arch)) return if (not shellcodes[arch]): error("No shellcodes available for architecture " + arch) return print( banner([ string_bold("Available shellcodes for arch " + string_special(arch)) ])) i = 0 for shellcode in shellcodes[arch]: i = i + 1 number = "({})".format(string_bold(str(i))) print("\n\t{} {}\n\t{} - {} bytes".format(number, shellcode[1], \ string_special(short_shellcode(shellcode[0])), str(len(shellcode[0]))))
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 shellcode(args): # Parsing arguments if (not args): print_help() return if (args[0] in [OPTION_LIST, OPTION_LIST_SHORT]): func = list_shellcodes elif (args[0] in [OPTION_ADD, OPTION_ADD_SHORT]): func = add elif (args[0] in [OPTION_REMOVE, OPTION_REMOVE_SHORT]): func = remove elif (args[0] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return else: error("Error. Unknown option '{}'".format(args[0])) return if (len(args) == 1): error("Architecture missing") print(string_bold("\tSupported architectures")+": "+\ ','.join([string_special(arch) for arch in Arch.available])) elif (not args[1] in Arch.available): error("Architecture {} not supported".format(string_special(args[1]))) print(string_bold("\tSupported architectures")+": "+\ ','.join([string_special(arch) for arch in Arch.available])) else: func(args[1]) return
def semantic_mode(): """ Returns ------- True if ROPGenerator must continue False if ROPGenerator must be closed """ finish = False while (not finish): try: user_input = promptSession.prompt() args = user_input.split() argslen = len(args) if (argslen > 0): command = args[0] else: command = None continue if (command == CMD_FIND): find(args[1:]) elif (command == CMD_EXIT): return False elif (command == CMD_HELP): print(helpStr) elif (command == CMD_MAIN): finish = True elif (command == CMD_REGS): list_regs() else: error("Unknown command '{}'".format(command)) if (command != None): print('') except KeyboardInterrupt: pass return True
def remove(arch): if (not shellcodes[arch]): error("No shellcodes to remove for architecture " + arch) return list_shellcodes(arch) print("") choice = '' ok = False sys.stdout.write('\t' + string_ropg('> ') + 'Select a shellcode to remove:\n') while (not ok): choice_input = prompt(u" > ") try: choice = int(choice_input) ok = remove_shellcode(arch, choice) if (not ok): print(string_special("\tError. Invalid choice")) except: ok = False print("") notify('Shellcode removed')
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_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 call(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON # Parsing arguments if( not args): print_help() return OUTPUT = OUTPUT_CONSOLE funcName = None i = 0 seenOutput = False seenFunction = False seenBadBytes = False seenKeepRegs = False seenShortest = False seenLmax = False seenOffset = False clmax = None offset = 0 constraint = Constraint() assertion = getBaseAssertion() while i < len(args): if( args[i] in [OPTION_LIST, OPTION_LIST_SHORT]): func_list = getAllFunctions() print_functions(func_list) return 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] in [OPTION_KEEP_REGS, OPTION_KEEP_REGS_SHORT]): if( seenKeepRegs ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing register after option '"+args[i]+"'") return seenKeepRegs = True (success, res) = parse_keep_regs(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(RegsNotModified(res)) elif( args[i] in [OPTION_CALL, OPTION_CALL_SHORT] ): if( not loadedBinary() ): error("Error. You should load a binary before building ROPChains") return elif( seenFunction ): error("Option '{}' should be used only once".format(args[i])) return userInput = '' i +=1 while( i < len(args) and args[i][0] != "-"): userInput += args[i] i += 1 (funcName, funcArgs ) = parseFunction(userInput) if( not funcName): return seenFunction = 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_SHORTEST, OPTION_SHORTEST_SHORT]): if( seenShortest ): error("Option '{}' should be used only once".format(args[i])) return seenShortest = True i += 1 elif( args[i] == OPTION_LMAX or args[i] == OPTION_LMAX_SHORT ): if( seenLmax ): 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 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 += 2 seenLmax = True elif( args[i] == OPTION_OFFSET or args[i] == OPTION_OFFSET_SHORT ): if( seenOffset ): error("Option '{}' should be used only once".format(args[i])) return if( i+1 >= len(args)): error("Error. Missing offset after option '"+args[i]+"'") return try: offset = int(args[i+1]) if( offset < 0 ): raise Exception() except: try: offset = int(args[i+1], 16) if( offset < 0 ): raise Exception() except: error("Error. '" + args[i+1] +"' is not a valid offset") return i += 2 seenOffset = True elif( args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return else: error("Error. Unknown option '{}'".format(args[i])) return if( not funcName ): error("Missing function to call") else: # Set offset if( not set_offset(offset) ): error("Error. Your offset is too big :'(") return try: res = build_call(funcName, funcArgs, constraint, assertion, clmax=clmax, optimizeLen=seenShortest) if( isinstance(res, str) ): error(res) else: print_chains([res], "Built matching ROPChain", constraint.getBadBytes()) except Exception, e: reset_offset() raise sys.exc_info()[1], None, sys.exc_info()[2] # Reset offset reset_offset()
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") except Exception, e: reset_offset() raise sys.exc_info()[1], None, sys.exc_info()[2] ### reset offset reset_offset() return # Test parsing result if( subcommand is None ): error("Missing subcommand") return
def dshell(args, constraint, assertion, lmax): address = None limit = None seenAddress = False seenRange = False # Parse options i = 0 while i < len(args): if (args[i][0] == '-'): if (args[i] in [OPTION_ADDRESS_SHORT, OPTION_ADDRESS]): if (seenAddress): error("Error. '" + args[i] + "' option should be used only once") return None elif (seenRange): error( "Error. You can't specify a delivery address and range at the same time" ) return None if (i + 1 >= len(args)): error("Error. Missing address after option '" + args[i] + "'") return seenAddress = True try: address = int(args[i + 1]) except: try: address = int(args[i + 1], 16) except: error("Error. '" + args[i + 1] + "' bytes is not valid") return None i = i + 2 seenAddress = True elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print_help() return "help" elif (args[i] in [OPTION_RANGE, OPTION_RANGE_SHORT]): if (seenRange): error("Error. '" + args[i] + "' option should be used only once") return None elif (seenAddress): error( "Error. You can't specify a delivery address and range at the same time" ) return None if (i + 1 >= len(args)): error("Error. Missing address range after option '" + args[i] + "'") return seenRange = True values = args[i + 1].split(',') if (len(values) < 2): error("Error. Invalid address range") return elif (len(values) > 2): error("Error. Too many values after '{}' option".format( args[i])) return int_values = [] # Convert addresses into int for value in values: try: address = int(value) except: try: address = int(value, 16) except: error("Error. '" + value + "' isn't a valid address") return None if (address < 0): error("Error. Addresses can't be negative") return None int_values.append(address) # Check consistency if (int_values[0] > int_values[1]): error( "Error. Invalid address range: lower address should be inferior to upper address" ) return None # Ok (address, limit) = int_values i += 2 else: error("Error. Unknown option '{}'".format(args[i])) return None else: error("Error. Invalid option '{}'".format(args[i])) return None # Select shellcode to deliver (shellcode, shellcode_info) = select_shellcode(Arch.currentArch.name) if (not shellcode): return None else: shellcode = str(shellcode) # Build the exploit print("") info("Building exploit: deliver-shellcode strategy\n\n") res = build_dshell(shellcode, constraint, assertion, address, limit, lmax) return res
def parseFunction(string): def seek(char, string): for i in range(0, len(string)): if string[i] == char: return (string[:i], i) return ([], -1) if (not string): error("Missing fuction to call") return (None, None) # COmpress the string string = "".join(string.split()) # Get the function name (funcName, index) = seek("(", string) if (not funcName): error("Invalid function call") return (None, None) rest = string[index + 1:] args = [] arg = '' i = 0 end = False while (i < len(rest)): c = rest[i] # No args if (c == ")" and not args): end = True i += 1 # String elif (c == '"' or c == "'"): (s, index) = seek(c, rest[i + 1:]) if (s == 0): error("Error. Empty string argument ?") return (None, None) elif (not s): error("Missing closing {} for string".format(c)) return (None, None) # Parse the string j = 0 s = str(s) parsed_string = "" while (j < len(s)): if (s[j:j + 2] == "\\x"): if (j + 3 < len(s)): try: char = int(s[j + 2:j + 4], 16) except: error("Invalid byte: '{}'".format(s[j:j + 4])) return (None, None) else: error("Invalid byte: '{}'".format(s[j:j + 4])) return (None, None) parsed_string += chr(char) j += 4 else: parsed_string += s[j] j += 1 args.append(str(parsed_string)) i += index + 2 if (i >= len(rest)): error("Error. Missing ')'") return (None, None) elif (rest[i] == ')'): end = True i += 1 elif (rest[i] == ","): i += 1 else: error("Error. Missing ',' or ')' after string") return (None, None) # Constant else: # Get the constant arg = '' ok = False for j in range(i, len(rest)): if (rest[j] == ")"): end = True ok = True break elif (rest[j] == ','): ok = True break else: arg += rest[j] if (not ok): error("Missing ')' after argument") return (None, None) if ((not arg) and args): error("Missing argument") return (None, None) # Convert to int try: value = int(arg) except: try: value = int(arg, 16) except: try: value = int(arg, 2) except: error("Invalid operand: " + arg) return (None, None) args.append(value) i = j + 1 if (end): break if (not end): error("Error. Missing ')'") return (None, None) if (i < len(rest)): error("Error. Extra argument: {}".format(rest[i:])) return (None, None) # str() to set its type to str ;) return (str(funcName), args)
def load(args): global helpStr global loaded # Parse arguments and filename filename = None user_arch = None i = 0 seenArch = False if (not args): print(helpStr) return while i < len(args): if (args[i] in [OPTION_ARCH, OPTION_ARCH_SHORT]): if (seenArch): error("Option {} can be used only one time"\ .format(args[i])) return seenArch = True if (i + 1 == len(args)): error("Missing argument after {}.\n\tType 'load -h' for help"\ .format(args[i])) return elif (args[i + 1] == Arch.ArchX86.name): user_arch = Arch.ArchX86 elif (args[i + 1] == Arch.ArchX64.name): user_arch = Arch.ArchX64 else: error("Unknown architecture: {}".format(args[i + 1])) return i += 2 elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print(helpStr) return else: filename = args[i] break if (not filename): error("Missing filename.\n\tType 'load help' for help") return # Test if the file exists if (not os.path.isfile(filename)): error("Error. Could not find file '{}'".format(filename)) return print('') info(string_bold("Extracting gadgets from file") + " '" + filename + "'\n") # Cleaning the data structures initDB() Arch.reinit() # Get architecture and OS info arch = getPlatformInfo(filename) if (arch == user_arch == None): error("Error. Could not determine architecture") return elif (arch and user_arch and (arch != user_arch)): error("Error. Conflicting architectures") print("\tUser supplied: " + user_arch.name) print("\tFound: " + arch.name) return elif (arch): Arch.currentArch = arch else: Arch.currentArch = user_arch # Init the binary scanner initScanner(filename) # Extract the gadget list gadgetList = getGadgets(filename) if (not gadgetList): return # Build the gadget database # (we mix the list so that charging bar # appears to grow steadily ) r = random() shuffle(gadgetList, lambda: r) build(gadgetList) # Init engine initEngine() loaded = True
def syscall(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON # Parsing arguments if( not args): print_help() return OUTPUT = OUTPUT_CONSOLE funcName = None offset = 0 i = 0 seenOutput = False seenFunction = False seenBadBytes = False seenKeepRegs = False seenOffset = False constraint = Constraint() assertion = getBaseAssertion() while i < len(args): if( args[i] in [OPTION_LIST, OPTION_LIST_SHORT]): listSyscalls(args[1:]) return 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] in [OPTION_KEEP_REGS, OPTION_KEEP_REGS_SHORT]): if( seenKeepRegs ): error("Error. '" + args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing register after option '"+args[i]+"'") return seenKeepRegs = True (success, res) = parse_keep_regs(args[i+1]) if( not success ): error(res) return i = i+2 constraint = constraint.add(RegsNotModified(res)) elif( args[i] == OPTION_OFFSET or args[i] == OPTION_OFFSET_SHORT ): if( seenOffset ): error("Error. '"+ args[i] + "' option should be used only once") return if( i+1 >= len(args)): error("Error. Missing offset after option '"+args[i]+"'") return try: offset = int(args[i+1]) if( offset < 0 ): raise Exception() except: try: offset = int(args[i+1], 16) if( offset < 0 ): raise Exception() except: error("Error. '" + args[i+1] +"' is not a valid offset") return i += 2 seenOffset = True elif( args[i] in [OPTION_FUNCTION, OPTION_FUNCTION_SHORT] ): if( not loadedBinary() ): error("Error. You should load a binary before building ROPChains") return elif( seenFunction ): error("Option '{}' should be used only once".format(args[i])) return userInput = '' i +=1 while( i < len(args) and args[i][0] != "-"): userInput += args[i] i += 1 (funcName, funcArgs ) = parseFunction(userInput) if( not funcName): return seenFunction = 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 else: error("Error. Unknown option '{}'".format(args[i])) return if( not funcName ): error("Missing function to call") else: # Set offset if ( not set_offset(offset) ): error("Error. Your offset is too big :'(") return try: call(funcName, funcArgs, constraint, assertion) except Exception, e: reset_offset() import sys raise sys.exc_info()[1], None, sys.exc_info()[2] # Reset offset reset_offset()
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 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 syscall(args): global OUTPUT, OUTPUT_CONSOLE, OUTPUT_PYTHON # Parsing arguments if (not args): print_help() return OUTPUT = OUTPUT_CONSOLE funcName = None i = 0 seenOutput = False seenFunction = False seenBadBytes = False seenKeepRegs = False constraint = Constraint() assertion = Assertion() while i < len(args): if (args[i] in [OPTION_LIST, OPTION_LIST_SHORT]): listSyscalls(args[1:]) return 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] in [OPTION_KEEP_REGS, OPTION_KEEP_REGS_SHORT]): if (seenKeepRegs): error("Error. '" + args[i] + "' option should be used only once") return if (i + 1 >= len(args)): error("Error. Missing register after option '" + args[i] + "'") return seenKeepRegs = True (success, res) = parse_keep_regs(args[i + 1]) if (not success): error(res) return i = i + 2 constraint = constraint.add(RegsNotModified(res)) elif (args[i] in [OPTION_FUNCTION, OPTION_FUNCTION_SHORT]): if (not loadedBinary()): error( "Error. You should load a binary before building ROPChains" ) return elif (seenFunction): error("Option '{}' should be used only once".format(args[i])) return userInput = '' i += 1 while (i < len(args) and args[i][0] != "-"): userInput += args[i] i += 1 (funcName, funcArgs) = parseFunction(userInput) if (not funcName): return seenFunction = 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 else: error("Error. Unknown option '{}'".format(args[i])) return if (not funcName): error("Missing function to call") else: call(funcName, funcArgs, constraint, assertion)
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 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 load(args): global helpStr global loaded # Parse arguments and filename filename = None user_arch = None i = 0 seenArch = False seenRopgadget = False ropgadget_options = '' if (not args): print(helpStr) return while i < len(args): if (args[i] in [OPTION_ARCH, OPTION_ARCH_SHORT]): if (seenArch): error("Option {} can be used only one time"\ .format(args[i])) return seenArch = True if (i + 1 == len(args)): error("Missing argument after {}.\n\tType 'load -h' for help"\ .format(args[i])) return elif (args[i + 1] == Arch.ArchX86.name): user_arch = Arch.ArchX86 elif (args[i + 1] == Arch.ArchX64.name): user_arch = Arch.ArchX64 else: error("Unknown architecture: {}".format(args[i + 1])) return i += 2 elif (args[i] in [OPTION_ROPGADGET_OPTIONS, OPTION_ROPGADGET_OPTIONS_SHORT]): if (seenRopgadget): error("Option {} can be used only one time"\ .format(args[i])) return seenRopgadget = True ropgadget_options = '' if (i + 1 == len(args)): error("Missing argument after {}.\n\tType 'load -h' for help"\ .format(args[i])) return j = i + 1 # Read the argments if (args[j][0] != "'"): error("ROPgadget options must be given between '' ") return if (args[j][-1] == "'" and len(args[j]) != 1): ropgadget_options += args[j][1:-1] else: ropgadget_options += args[j][1:] j += 1 closed_ok = False while (j < len(args)): if (args[j][0] != "'"): if (args[j][-1] == "'"): ropgadget_options += " " + args[j][0:-1] closed_ok = True break elif ("'" in args[j]): error( "ROPgadget options: You must leave a space after the closing '" ) return else: ropgadget_options += " " + args[j] else: if (len(args[j]) > 1): error( "ROPgadget options: You must leave a space after the closing \'" ) return else: closed_ok = True break j += 1 if (not closed_ok): error("ROPgadget options: missing closing \'") return i = j + 1 elif (args[i] in [OPTION_HELP, OPTION_HELP_SHORT]): print(helpStr) return else: filename = args[i] break if (not filename): error("Missing filename.\n\tType 'load help' for help") return # Test if the file exists if (not os.path.isfile(filename)): error("Error. Could not find file '{}'".format(filename)) return print('') info(string_bold("Extracting gadgets from file") + " '" + filename + "'\n") # Cleaning the data structures initDB() Arch.reinit() # Get architecture and OS info arch = getPlatformInfo(filename) if (arch == user_arch == None): error("Error. Could not determine architecture") return elif (arch and user_arch and (arch != user_arch)): error("Error. Conflicting architectures") print("\tUser supplied: " + user_arch.name) print("\tFound: " + arch.name) return elif (arch): Arch.setArch(arch) else: Arch.setArch(user_arch) # Init the binary scanner initScanner(filename) # Extract the gadget list gadgetList = getGadgets(filename, ropgadget_options) if (not gadgetList): return # Build the gadget database # (we mix the list so that charging bar # appears to grow steadily ) r = random() shuffle(gadgetList, lambda: r) build(gadgetList) # Init engine initEngine() loaded = True