Swamp CTF Return Challenge Walkthrough
SwampCTF was a recent CTF found by a few friends on CTFtime.org that was hosted by the University of Florida Student InfoSec Team. This walkthrough will discuss how to complete the Return PWN Challenge.
First we download the challenge and attempt to break it locally before attacking it remotely.
A quick file check shows its a simple 32 bit linux binary. Running the challenge it appears to be asking us for some input. We throw 186 A's at the program and notice that last 136 dont get read, at this point its clear we need to attack this binary in the first 50 bytes.
At this point I figure its time to take a look at the program in a disassembler. I opened it up in binary ninja and saw a few interesting things immediately.
We can see here there are 2 interesting functions, doBattle and slayTheBeast. Checking the slayTheBeast function we can see a cat for flag.txt, it's obvious at this point our goal is to call this function somehow.
Analyzing the doBattle function we can see where we get thrown our text that says our actions are ineffective. Since this function also appears to be the first function that gets called by main it looks like our first step is making it past this point.
It looks like when our jmp isnt taken at that "jbe 0x08048573" point we get thrown a message that says our actions are ineffective and then the program exits out. My next step was to run this through a debugger and see what was happening in that comparison before the jbe.
Jbe is a conditional jump in assembly that in this case asks if edx is less than or equal to eax before determining which way to jump. If EDX is less than EAX we proceed to jump to the left side, if EDX is greater than EAX we proceed to jump to that right side where we fail and exit zero. It's clear our goal is to somehow get EDX lower than EAX to continue to the next step.
It appears 4 out of 50 of our A's fill that EDX register! This is good news as it means we can control this jump based on our input. We simply needed to provide a value to EDX that was less than or equal to EAX. First we needed to figure out which 4 of these A's filled up EDX.
Since were only dealing with 50 bytes I decided to simply manually remove 4 A's at a time and re-run in the debugger until EDX was no longer filled with our input. When passing 42 A's we successfully saw an empty EDX. I then passed 42 A's and 4 B's and was able to confirm our 4 B's overwrote EDX. We now have EDX control and control over the doBattle function! The next step was simply figuring out what to fill EDX with when a teammate advised simply setting it equal to EAX.
I create a payload of 42 A's and the value of EAX that we need to match into a file and then pipe the payload into the binary while debugging to make sure we get a match. After it runs we step through and are able to confirm we jumped as required! Success!
At this point I was stuck for a bit hitting a segfault before I decided to utilize the full 50 bytes again and noticed something interesting. It appeared our last 4 bytes in the 50 bytes appear in the stack right after the 4 bytes we passed to equal EAX to reach this point from doBattle.
This was quite useful as it meant what we pass gets stored in the stack and returned to, this meant all we had to do was find a way to retun to the slayTheBeast function at this junction. We had an important requirenment we needed to resolve to get this working, whatever we pass as our 4 bytes that gets compared in the doBattle function needed to be less than EAX as well as assisting us in calling the slayTheBeast function. Then it hit me, I can have it return to another return and then have my last 4 bytes that are returned to be the slayTheBeast function!
I used gdb-peda and the ropgadget feature in order to find a return function and then restructured my payload to include the return and the slayTheBeast function as my last 8 bytes.
Running it through the debugger we can see where our return appears and then successfully returns to the slayTheBeast function! Running this out of the debugger successfully cats our mock flag.txt and completes this challenge.