def show_shellcode(): print(string_bold('\n\t-------------------------------')) print(string_bold("\tSelected shellcode - arch " + string_special(arch))) print(string_bold('\t-------------------------------')) (shellcode, info) = selected(arch) print("\n\t{}\n\n{} - {} bytes".format( info, \ string_special(pack_shellcode(shellcode)), str(len(shellcode))))
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 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 print_chains(chainList, msg, badBytes=[]): global OUTPUT sep = "------------------" if( chainList): print(string_bold('\n\t'+msg)) if( OUTPUT == OUTPUT_CONSOLE ): print("\n"+chainList[0].strConsole(Arch.currentArch.bits, badBytes)) elif( OUTPUT == OUTPUT_PYTHON ): print('\n' + chainList[0].strPython(Arch.currentArch.bits, badBytes)) for chain in chainList[1:]: if( OUTPUT == OUTPUT_CONSOLE ): print('\t'+sep + "\n"+ chain.strConsole(Arch.currentArch.bits, badBytes)) elif( OUTPUT == OUTPUT_PYTHON ): print('\t'+sep + '\n' + chain.strPython(Arch.currentArch.bits, badBytes)) else: print(string_bold("\n\tNo matching ROPChain found"))
def add(arch): print(banner([string_bold('Adding a new shellcode')])) shellcode = '' ok = False while (not ok): sys.stdout.write('\t' + string_ropg('> ') + 'Enter your shellcode as a string in hex format:\n') shellcode_input = prompt(u" > ") try: shellcode = shellcode_input.replace('\\x', '').decode('hex') ok = True except: ok = False if (not ok): print( string_special( "\tError. Your input is in wrong format or invalid")) sys.stdout.write('\t' + string_ropg('> ') + 'Enter short shellcode name/description:\n') info = "" while (not info): info = prompt(u" > ") info = filter(lambda x: x in set(string.printable), info) add_shellcode(arch, shellcode, info)
def __str__(self): res = self.ret + " " + string_bold(self.name) res += "(" res += ', '.join( [a[0] + " " + string_special(a[1]) for a in self.args]) res += ")" return res
def strPython(self, bits, badBytes=[], init=True, noTab=False): if (noTab): tab = '' else: tab = '\t' # Getting endianness to pack values if (bits == 32): endianness_str = "'<I'" elif (bits == 64): endianness_str = "'<Q'" else: raise Exception("{}-bits architecture not supported".format(bits)) pack_str = "p += pack(" + endianness_str + "," res = '' if (init): res += tab + "from struct import pack\n" res += tab + "p = ''" for element in self.chain: if (not isinstance(element, Gadget)): padding_str = pack_str padding_str += string_special( '0x' + format(self.paddings[element][0], '0' + str(bits / 4) + 'x')) + ")" padding_str += " # " + self.paddings[element][1] res += "\n" + tab + padding_str else: res += "\n"+tab+pack_str+string_special(validAddrStr(element, badBytes, bits)) +\ ") # " + string_bold(element.asmStr) return res
def _CSTtoREG_pop(reg, cst, constraint, assertion, n=1, clmax=LMAX, comment=None): """ Returns a payload that puts cst into register reg by poping it from the stack """ # Test clmax if (clmax <= 0): return [] # Test n if (n < 1): return [] # Check if the cst is incompatible with the constraint if (not constraint.badBytes.verifyAddress(cst)): return [] if (not comment): comment = "Constant: " + string_bold("0x{:x}".format(cst)) # Direct pop from the stack res = [] if (reg == Arch.ipNum()): constraint2 = constraint.remove([CstrTypeID.CHAINABLE]) else: constraint2 = constraint.add(Chainable(ret=True)) possible = DBPossiblePopOffsets(reg, constraint2, assertion) for offset in sorted(filter(lambda x: x >= 0, possible.keys())): # If offsets are too big to fit in the lmax just break if (offset > clmax * Arch.octets()): break # Get possible gadgets possible_gadgets = [g for g in possible[offset]\ if g.spInc >= Arch.octets() \ and g.spInc - Arch.octets() > offset \ and (g.spInc/Arch.octets()-1) <= clmax] # Test if padding is too much for clmax # Pad the gadgets padding = constraint.getValidPadding(Arch.octets()) for gadget in possible_gadgets: chain = ROPChain([gadget]) for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()): if (i == offset): chain.addPadding(cst, comment) else: chain.addPadding(padding) if (len(chain) <= clmax): res.append(chain) if (len(res) >= n): return res return res
def list_regs(): """ List available registers """ print(banner([string_bold("Available registers")])) for reg in sorted(Arch.regNameToNum.keys()): if (reg == Arch.currentArch.ip): print("\t" + string_special(reg) + " - instruction pointer") elif (reg == Arch.currentArch.sp): print("\t" + string_special(reg) + " - stack pointer") else: print("\t" + string_special(reg))
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 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 print_functions(func_list): """ func_list - list of pairs (str, int) = (funcName, funcAddress) """ space = 28 print(banner([string_bold("Available functions")])) print("\tFunction" + " " * (space - 8) + "Address") print("\t------------------------------------") for (funcName, funcAddr) in sorted(func_list, key=lambda x: x[0]): space2 = space - len(funcName) if (space2 < 0): space2 = 2 print("\t" + string_special(funcName) + " " * space2 + hex(funcAddr)) print("")
def initEngine(): global INIT_LMAX, INIT_MAXDEPTH global global_impossible_REGtoREG global baseAssertion # Init global variables baseAssertion = Assertion().add(\ RegsValidPtrRead([(Arch.spNum(),-5000, 10000)]), copy=False).add(\ RegsValidPtrWrite([(Arch.spNum(), -5000, 0)]), copy=False) info(string_bold("Initializing Semantic Engine\n")) # Init helper for REGtoREG global_impossible_REGtoREG = SearchEnvironment(INIT_LMAX, Constraint(), baseAssertion, INIT_MAXDEPTH) init_impossible_REGtoREG(global_impossible_REGtoREG)
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 STRtoMEM_write(string, addr, constraint, assertion, limit=None, lmax=STR_TO_MEM_LMAX, addr_str=None, hex_info=False): """ WRITE STRATEGY Copy the string using mem(XXX) <- YYY gadgets """ if (not addr_str): addr_str = hex(addr) # We decompose the string in substrings to be copied substrings_addr = find_best_valid_writes(addr, string, constraint, limit) if (substrings_addr is None): return (None, None) # Build chain res = ROPChain() offset = 0 for (substring_addr, substring_val) in substrings_addr: substring_info = "(" + string_bold("Substring in int") + hex( substring_val) + ")" write_chain = store_constant_address(QueryType.CSTtoMEM, substring_addr, substring_val, constraint, assertion, clmax=lmax - len(res), optimizeLen=True) if (write_chain): res.addChain(write_chain) else: verbose("Coudln't find suitable memory write ropchain") return (None, None) return (substrings_addr[0][0], res)
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ════════════════════ v1.2 """ # Definitions of commands CMD_HELP = "help" CMD_LOAD = "load" CMD_CONFIG = "config" CMD_EXIT = "exit" CMD_SEARCH = "semantic" CMD_EXPLOIT = "exploit" helpStr = banner([ string_bold('Main Commands'), string_special('(For more info about a command type <cmd -h>)') ]) helpStr += '\n\t' + string_bold( CMD_LOAD) + ': \t\tload gadgets from a binary file' helpStr += '\n\n\t' + string_semantic(string_bold(CMD_SEARCH)) + \ ': \tEnter semantic-mode (Search for'+'\n\t\t\tgadgets and ROPChains)' helpStr += '\n\n\t' + string_exploit(string_bold(CMD_EXPLOIT)) + \ ': \tEnter exploit-mode (Automated exploit'+'\n\t\t\tgeneration features)' helpStr += '\n\n\t' + string_bold(CMD_HELP) + ': \t\tprint available commands' helpStr += '\n\t' + string_bold(CMD_EXIT) + ': \t\texit ROPGenerator' def main(): print(string_ropg(string_bold(ASCII_art))) initLogs()
from ropgenerator.exploit.Pwn import pwn from ropgenerator.IO import string_exploit, string_bold, string_special, banner, error import sys # Definitions of commands CMD_HELP = "help" CMD_SHELLCODE = "shellcode" CMD_SYSCALL = "syscall" CMD_PWN = "pwn" CMD_CALL = "call" CMD_MAIN = "main" CMD_EXIT = "exit" helpStr = banner([ string_bold('Exploit-Mode Commands'), string_special('(For more info about a command type <cmd -h>)') ]) helpStr += '\n\t' + string_bold(CMD_PWN) + ': \t\tbuild an exploit' helpStr += '\n\t' + string_bold(CMD_SYSCALL) + ': \tcall a system function' helpStr += '\n\t' + string_bold(CMD_CALL) + ':\t\tcall a regular function' helpStr += '\n\t' + string_bold(CMD_SHELLCODE) + ': \tmanage shellcodes' helpStr += '\n\n\t' + string_bold(CMD_HELP) + ': \t\tshow this help' helpStr += '\n\t' + string_bold(CMD_MAIN) + ': \t\treturn to the main menu' helpStr += '\n\t' + string_bold(CMD_EXIT) + ': \t\texit ROPGenerator' promptSession = PromptSession(ANSI(u"(" + string_exploit(u'exploit') + u")> ")) def exploit_mode():
################################ # DELIVER-SHELLCODE COMMAND # ################################ # Options OPTION_ADDRESS = '--address' OPTION_ADDRESS_SHORT = "-a" OPTION_RANGE = "--address-range" OPTION_RANGE_SHORT = "-r" OPTION_HELP = '--help' OPTION_HELP_SHORT = '-h' CMD_DSHELL_HELP = banner([string_bold("'deliver-shellcode' command"),\ string_special("(Deliver a shellcode & Execute it)")]) CMD_DSHELL_HELP += "\n\n\t"+string_bold("Description:")+\ "\n\t\tThis method tries to create an executable memory area"+\ "\n\t\t, then copy a given shellcode into this area, and then"+\ "\n\t\t jump to execute this shellcode" CMD_DSHELL_HELP += "\n\n\t" + string_bold("Options") + ":" CMD_DSHELL_HELP += "\n\n\t\t" + string_special( OPTION_ADDRESS_SHORT) + "," + string_special( OPTION_ADDRESS) + " <int>\t Address where to deliver shellcode" CMD_DSHELL_HELP += "\n\n\t\t" + string_special( OPTION_RANGE_SHORT ) + "," + string_special( OPTION_RANGE ) + " \t Memory space that can be used to\n\t\t\t<addr>,<addr>\t deliver the shellcode"
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 _CSTtoREG_pop(reg, cst, env, n=1): """ Returns a payload that puts cst into register reg by poping it from the stack """ ID = StrategyType.CSTtoREG_POP ## Test for special cases # Test lmax if (env.getLmax() <= 0): return FailRecord(lmax=True) # Limit number of calls to ... elif (env.nbCalls(ID) >= 99): return FailRecord() # Check if the cst is in badBytes elif (not env.getConstraint().badBytes.verifyAddress(cst)): return FailRecord() # Set env env.addCall(ID) # Get comment if (env.hasComment(ID)): envHadComment = True comment = env.popComment(ID) else: envHadComment = False comment = "Constant: " + string_bold("0x{:x}".format(cst)) ######################## # Direct pop from the stack res = [] res_fail = FailRecord() # Adapt constraint if ip <- cst if (reg != Arch.ipNum()): constraint2 = env.getConstraint().add(Chainable(ret=True)) else: constraint2 = env.getConstraint() possible = DBPossiblePopOffsets(reg, constraint2, env.getAssertion()) for offset in sorted(filter(lambda x: x >= 0, possible.keys())): # If offsets are too big to fit in the lmax just break if (offset > env.getLmax() * Arch.octets()): break # Get possible gadgets possible_gadgets = [g for g in possible[offset]\ if g.spInc >= Arch.octets() \ and g.spInc - Arch.octets() > offset \ and (g.spInc/Arch.octets()-1) <= env.getLmax()] # Test if padding is too much for clmax # Pad the gadgets padding = env.getConstraint().getValidPadding(Arch.octets()) for gadget in possible_gadgets: chain = ROPChain([gadget]) for i in range(0, gadget.spInc - Arch.octets(), Arch.octets()): if (i == offset): chain.addPadding(cst, comment) else: chain.addPadding(padding) if (len(chain) <= env.getLmax()): res.append(chain) if (len(res) >= n): break if (len(res) >= n): break ######################### # Restore env env.removeCall(ID) if (envHadComment): env.pushComment(ID, comment) return res if res else res_fail
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
from ropgenerator.exploit.Scanner import initScanner import ropgenerator.Architecture as Arch from ropgenerator.IO import string_bold, info, string_special, banner, notify, error from magic import from_file from base64 import b16decode from random import shuffle, random, randrange, Random # Command options OPTION_ARCH = '--arch' OPTION_ARCH_SHORT = '-a' OPTION_HELP = '--help' OPTION_HELP_SHORT = '-h' # Help for the load command helpStr = banner([ string_bold("'load' command"), string_special("(Load gadgets from a binary file)") ]) helpStr += "\n\n\t" + string_bold("Usage") + ":\tload [OPTIONS] <filename>" helpStr += "\n\n\t" + string_bold("Options") + ":" helpStr += "\n\t\t"+string_special(OPTION_ARCH_SHORT)+","+string_special(OPTION_ARCH)+\ " <arch>"+"\tmanualy specify architecture.\n\t\t\t\t\tAvailable: 'X86', 'X64'" helpStr += "\n\n\t" + string_bold( "Examples" ) + ":\n\t\tload /bin/ls\t\t(load gadgets from /bin/ls program)\n\t\tload ../test/vuln_prog\t(load gadgets from own binary)" def print_help(): print(helpStr)
def print_available(): global available print(string_bold("\n\n\tSupported Linux 32-bits syscalls")) for name,syscall in available.iteritems(): print("\n\t"+string_payload(name)+": "+str(syscall))
OPTION_LMAX = '--max-length' OPTION_LMAX_SHORT = '-m' OPTION_VERBOSE = "--verbose" OPTION_VERBOSE_SHORT = "-v" OPTION_OUTFILE = '--output-file' OPTION_OUTFILE_SHORT = '-o' OPTION_PADDING_BYTE = '--padding-byte' OPTION_PADDING_BYTE_SHORT = '-pb' OPTION_PADDING_LEN = '--padding-len' OPTION_PADDING_LEN_SHORT = '-pl' CMD_PWN_HELP = banner([string_bold("'pwn' command"),\ string_special("(Generate full exploits)")]) CMD_PWN_HELP += "\n\n\t"+string_bold("Usage:")+\ "\n\t\tpwn [OPTIONS] <subcommand> [SUBCOMMAND_OPTIONS]" CMD_PWN_HELP += "\n\n\t"+string_bold("Subcommands")+":" CMD_PWN_HELP += "\n\t(For more info use 'pwn <subcommand> -h')" CMD_PWN_HELP += "\n\n\t\t"+string_special(CMD_DELIVER_SHELLCODE)+"\t Inject a shellcode an execute it" CMD_PWN_HELP += "\n\n\t"+string_bold("Options")+":" CMD_PWN_HELP += "\n\n\t\t"+string_special(OPTION_BAD_BYTES_SHORT)+","+string_special(OPTION_BAD_BYTES)+" <bytes>\t Bad bytes for payload.\n\t\t\t\t\t Expected format is a list of bytes \n\t\t\t\t\t separated by comas (e.g '-b 0A,0B,2F')" CMD_PWN_HELP += "\n\n\t\t"+string_special(OPTION_LMAX_SHORT)+","+string_special(OPTION_LMAX)+" <int>\t Max length of the ROPChain in bytes" CMD_PWN_HELP += "\n\n\t\t"+string_special(OPTION_PADDING_BYTE_SHORT)+","+string_special(OPTION_PADDING_BYTE)+" <byte> Byte for payload padding" CMD_PWN_HELP += "\n\n\t\t"+string_special(OPTION_PADDING_LEN_SHORT)+","+string_special(OPTION_PADDING_LEN)+" <int>\t Length of payload padding" CMD_PWN_HELP += "\n\n\t\t"+string_special(OPTION_OUTPUT_SHORT)+","+\ string_special(OPTION_OUTPUT)+\
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 build(pair_list): """ Takes a list of pairs (addr, raw_asm) corresponding to gagets (their address and their intructions as a byte string) Fills the 'gadgets' and 'db' global structures ;) """ def sigint_handler(signal, frame): global sigint sigint = True def timeout_handler(signum, frame): global sigalarm sigalarm = True signal.alarm(0) raise TimeOutException('Timeout') global gadgets, db, sigint gadgets = [] raw_to_gadget = dict() sigint = False original_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, sigint_handler) signal.signal(signal.SIGALRM, timeout_handler) info(string_bold("Creating gadget database\n")) startTime = datetime.now() success = i = 0 # Create the gadgets list for (addr, raw) in pair_list: charging_bar(len(pair_list) - 1, i, 30) if (sigint): break if (raw in raw_to_gadget): gadgets[raw_to_gadget[raw]].addrList.append(addr) success += 1 else: try: signal.alarm(1) gadget = Gadget([addr], raw) signal.alarm(0) success += 1 gadgets.append(gadget) raw_to_gadget[raw] = len(gadgets) - 1 except (GadgetException, TimeOutException) as e: signal.alarm(0) if (isinstance(e, GadgetException)): log("Gadget ({}) : ".format('\\x'+'\\x'\ .join("{:02x}".format(ord(c)) for c in raw)) + str(e)) i += 1 # Find syscalls # TODO # Getting time cTime = datetime.now() - startTime signal.signal(signal.SIGINT, original_sigint_handler) if (sigint): print("\n") fatal( "SIGINT ended the analysis prematurely, gadget database might be incomplete\n" ) sigint = False notify("Gadgets analyzed : " + str(len(pair_list))) notify("Successfully translated : " + str(success)) notify("Computation time : " + str(cTime)) # Create the database db = Database(gadgets)
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
OPTION_KEEP_REGS = '--keep-regs' OPTION_KEEP_REGS_SHORT = '-k' OPTION_LIST = "--list" OPTION_LIST_SHORT = "-l" OPTION_FUNCTION = "--call" OPTION_FUNCTION_SHORT = "-c" OPTION_OFFSET="--offset" OPTION_OFFSET_SHORT = "-off" OPTION_HELP = "--help" OPTION_HELP_SHORT = "-h" CMD_SYSCALL_HELP = banner([string_bold("'syscall' command"),\ string_special("(Call system functions with ROPChains)")]) CMD_SYSCALL_HELP += "\n\n\t"+string_bold("Usage:")+\ "\n\t\tsyscall [OPTIONS]" CMD_SYSCALL_HELP += "\n\n\t"+string_bold("Options")+":" CMD_SYSCALL_HELP += "\n\t\t"+string_special(OPTION_FUNCTION_SHORT)+","+\ string_special(OPTION_FUNCTION)+" <function>\t Call a system function" CMD_SYSCALL_HELP += "\n\n\t\t"+string_special(OPTION_BAD_BYTES_SHORT)+","+string_special(OPTION_BAD_BYTES)+" <bytes>\t Bad bytes for payload.\n\t\t\t\t\t Expected format is a list of bytes \n\t\t\t\t\t separated by comas (e.g '-b 0A,0B,2F')" CMD_SYSCALL_HELP += "\n\n\t\t"+string_special(OPTION_KEEP_REGS_SHORT)+","+string_special(OPTION_KEEP_REGS)+" <regs>\t Registers that shouldn't be modified.\n\t\t\t\t\t Expected format is a list of registers \n\t\t\t\t\t separated by comas (e.g '-k edi,eax')" CMD_SYSCALL_HELP += "\n\n\t\t"+string_special(OPTION_OFFSET_SHORT)+","+\ string_special(OPTION_OFFSET)+" <int>\t Offset to add to gadget addresses" CMD_SYSCALL_HELP += "\n\n\t\t"+string_special(OPTION_LIST_SHORT)+","+\ string_special(OPTION_LIST)+" [<system>]\t List supported functions" CMD_SYSCALL_HELP += "\n\n\t\t"+string_special(OPTION_OUTPUT_SHORT)+","+\ string_special(OPTION_OUTPUT)+\