This was great little challenge, all it did was read 0x400 bytes into a buffer and returned 0.
So we have a buffer overflow, NX and ASLR enable, and only only a single libc function read. It’s ropping time!
First of all we need a way to get a leak to defeat ASLR. write in libc is located close enough to read that we could overwrite just the last byte of the GOT entry and turn it into a call to write. This would allow us to leak anything, but afterward we would have to way to call read again to do anything useful.
Another option is to overwrite read to point to a syscall, that would give us greater control over what we could do:
Overwriting one byte also has the nice side effect of setting rax to 1 which is what we need for a write syscall.
Now we have a leak and can get the libc base address, then we need to get rax to 0 to perform another read.
As the main function in binary returns 0, we can use that:
The only downside is that it will do a leave, so the stack will be pivoted to whatever we overflowed the saved rbp to be. That’s no problem, we just need to set rbp to point to some fixed writeable location and make sure we have setup our rop there. We can use somewhere in the bss and set it up before we overwrite the read got.
So we setup the rop in bss to call read and overwrite the new stack with system, and set rdi to /bin/sh and we have a shell.