Continuing from Stage4, now onto the final stage, Stage5!
Right away Stage5 had me a bit baffled. Up until this point I had been relying entirely on comparisons, tests, and jumps to determine the steps necessary for completing the previous stages. Without any of these I was lost as to where I should begin.
There is not much going on in this function. There’s a malloc
with some hex bytes(which will appear later) but these are unimportant. There are some basic prints and then the function takes a user input and passes that to vscanf
with the format string "%s"
. So I know the input is expecting a single string; but then what?
Those hex bytes from the malloc
are appended to the name entered, as observed above. This seems to be for visual purposes only and doesn’t actually change anything. We need to dig deeper. Up until this point every successful stage completion is met with "Defused Stage X"
, so I know that is ultimately the goal. When searching for "Defused Stage 5"
I found this additional function.
Well this seems to be where I want to end up. It has the correct defused print out and returns a value of true. But, how am I supposed to call this function while still abiding by all the rules laid out in the CTF? I didn’t know exactly what I was doing at this point, but I had a rough idea. My first thought was to input the name of this function when the CTF prompted for a name.
When I tried this, I was met with a ton of EXCEPTION_ACCESS_VIOLATION
‘s. It wasn’t good that I was crashing, but it was a clue that I was sort of on to something. Knowing Colin and his abilities this was not unintentional. It wasn’t the solution to Stage5, but it showed that I was on the right path, sort of.
The next thing I did was to try and flesh out a better understanding of the function AlwaysReturnTrue
.
So where is this function trying to jump to?
It was trying to jump back to main, where it would test our return value, and if it was false it was going to print that the bomb exploded. Otherwise, if we returned true, it would take us to the final bomb defused print. This definitely helped solidify my hunch that this was exactly the function I needed to call to pass Stage5. How that was going to be possible I still had no idea…
I went back to Stage5 and mulled over everything what was happening. There were no comparisons, tests, or jumps, but it was clear that I needed to call this function and it’s undeniably the starting point to solving this.
I went back to my most valuable tool of all: Google. Finally after studying the MSDN document for fscanf
this popped out at me in a true “aha moment”.
At this point I truly felt like I had discovered the last bit of information that I needed. Now it was time to really observe the call to vscanf
so I could find the vulnerability I assumed was present and exploit that to call the function AlwaysReturnTrue
that I so desperately needed.
First I observed what the stack looked like before the call to vscanf
.
Then I decided to give it the input of "AlwaysReturnTrue"
since I knew it was going to give me some undefined behavior, and that’s all I really had to go off of at the time.
Alright, now I was finally seeing some light at the end of the tunnel. This is the buffer overflow in action. Above, before the call to vscanf
returns, we’re at the point where it will store a string to format. At the offset +C
from 0x19FF2C
is the return address. I knew I needed to change this address to the address of AlwaysReturnTrue
. My first attempt was to input any 12 characters followed by the address of AlwaysReturnTrue
.
That was a dumb mistake. Obviously, the numbers I entered for the address are still treated as characters of a string, so their hex representation is incorrect for my needs.
After some more Googling I found some helpful websites listing all the alt codes and their values. I knew the address of AlwaysReturnTrue
was 0x401550
, so I had to find corresponding alt-codes that would give me characters that represented the exact hex values I needed. I also needed to flip the endianness around.
The characters I needed were P
for 0x50
, §(NAK)
for 0x15
, and @
for 0x40
.
Looks good.
Success! Stage5 is defused and I’ve completed Colin’s CTF Challenge #1.
Overall I had a lot of fun reversing this CTF. I really enjoyed the added challenging of solving it without any binary editing or flag switching. I definitely learned a lot and am feeling even more confidant about my RE knowledge and understanding. Once again, a big shout-out to my friend Colin for creating this CTF and also for being steadfast in his commitment to never sharing a single hint and really allowing me to earn the satisfaction of completing these challenges.