Though in contrary to babystack, we’re provided the used libc this time, but that doesn’t makes it easier to pwn it…
So, we have an obvious buffer overflow here, but there are some seccomp rules in place, which only allow very few syscalls
read
open
close
mprotect
exit
exit_group
limiting us pretty much in what we can do to exfiltrate the flag from the server. It seems, we won’t get the binary to print out anything to us, so no leaks and if we can read the flag, no way to send it back to us.
Well, let’s start with gaining rip access and executing functions. In order to use read for anything useful, we need to control rdi, rsi and rdx. While there are simple pop gadgets for rsi and rdi, there isn’t one for rdx.
But we can get around this, by using the following two gadgets in combination
With this, we’ll control rbx, rbp, r12, r13, r14 and r15.
Combined with this one:
we thus control rdx, rsi and edi. The following call, will call any function, whose address is stored at r12 + rbx*8, and since we also control r12 and rbx, we can define, which function should be called.
We’ll be using this to read another ropchain, with a bit more space and also to a known address:
This reads another ropchain to 0x601150, puts 0x601150 - 8 into rbp and the following leave; ret will pivot the stack to this new ropchain.
Still, we only have calls to read and alarm available, but since we have the provided libc, we can abuse read to change this.
Since the last 3 nibbles of an address won’t be randomized by ASLR, we can overwrite the LSB of alarm got with 0x85 to change it into a neat syscall gadget.
So, now we can trigger a syscall by calling alarm@plt. We now just need a proper way to set rax to the desired syscall (we’ll be using mprotect (10)).
Again, we can use read for this, because it will store the count of read bytes in rax, so we’ll just be doing a read(0, 0x601500, 10), resulting in rax to be set to 0xa (10):
Now we can call alarm@plt to trigger mprotect, which we use to set protection of bss to rwx:
Having an rwx bss, enables us to put some shellcode there and jump to it (with p64(0x6012e0)).
But still, seccomp will hurt us, if we try to print out the flag. So what else can we do.
Remember blind sql injection? Well, we can do something similar here:
Open the flag file
Read the string to a known address
Get a character at a specific offset
Compare it to a value
If the character is bigger, jump to a good branch (go into infinite loop for example), else to a bad branch (exit the program)
Check if the service is still available (condition met) or if it exited directly (condition not met)
We put the filename flag already into bss at 0x6012c0, so this shellcode will open it, read the content to 0x601600, get the byte at offset from 0x601600 and compare it with compval. If it’s greater or equal it will jump to good, which basically just puts the binary into an infinite loop. Otherwise it will exit directly.
Armed with this, we can write a testchar method, which will compare a value at a given offset with a specific compare value (It will need to open a connection for every test, though).
This method will return True or False, depending if our compare value is greater/equal to the current character in the file.
With this we can do a binary search for every character:
This will need 8 connections per character to find the correct character (and it has to solve the proof of work every time, so it will take even more sigh). So, grab a coffee, since this will take quite some time until the flag is finally found, but it will work out in the end :)
That took some time, so I’m quite curious, if someone comes up with a more elegant and quicker method of getting the flag out of this, but well, a flag is a flag :)