Defcon Quals 2018 - stumbler
Team: Samurai
Attachment: stumbler app_init app_fn_0 app_fn_1 app_fn_2 xpl.py stumblerpow.py
Joined this challenge pretty late. avery3r
had already done the first part of the challenge, reversing it completely and found a way to bypass the pow
on this challenge.
When the service then asks you, if you want to play a game and you answer with n
, it will print out some portion of the stack (from which you can leak addresses from stack and app_init
).
He also figured out a way to do an arbitray read/write
- When the service asks you, if you want to play a game, answer with
y
- It will then ask you for a number, which it will then convert to an address
- It then prints out 8 bytes of data from this address
- And then reads 8 bytes of data, which will get stored at this address
The catch here seemed to be, that the binary added new randomized memory regions on every round. Though the new addresses, which got created, could be guessed (avery3r
also provided calculations for that), those additional regions can be ignored.
The stack won’t move, as well as the app_init
section stays in place, and that’s all we needed in the end to finalize this challenge.
To start with this, we’ll first leak the values from the stack, we’re given, when we decline to play:
For writing data to the stack, we can just play the game, pass the address, we want to write to as our guess. We’ll then receive the data, that’s currently stored there and can write 8 bytes of data to it:
Since we know the stack address, we could use this to overwrite the return address of the guessing function giving us a free call. Fiddled around with the different stumbler app functions to find out, if there would be some kind of win function, which might give us a proper read or even a shell, but didn’t find a way to exploit this one with one single call.
So I opted for doing a ropchain instead. But we only have a limited amount of guesses. After writing 4
addresses, the binary closed, so it would be hard to do a proper ropchain with that.
Thus, I used 3 writes to prepare a stager ropchain on the stack, which would read my final ropchain. And with the 4th write, I put a stack pivot gadget into the return address of the guessing function, so it would pivot to my stager ropchain, waiting on the stack to get executed.
app_init
has a function recv_all
which will read x bytes from the given socket descriptor:
We can use this function to receive additional data and abuse the fact that rdi
will already contain our socket descriptor from the previous reads and rsi
will also already point to a buffer on the stack.
Only problem that arises here, is that rdx
will still contain 8, since the service always only reads 8 bytes from us. Not enough for a proper ropchain, but with 3 writes, we can create a small ropchain, that will fix that for us.
So, this prepares our ropchain on the stack, which will set rdx
to 0x1000
and then stack pivot into it, resulting in another read of 0x1000
bytes onto the stack. Neat, this should make things much easier, not having to fiddle around with the guessing game anymore.
From here, it’s just a matter of open("flag")
, read
from it and send_all
it back to us :)
Since stumbler
already has 10 open file descriptors, we know that the flag fd will be 11
after the open
. So we just read 100 bytes (more than enough for a flag) from it and use the send_all
method from app_init
to send it back to us.