It's been a long break between the last CTF I wrote up and now. To be honest there has been a bit of a lull in the number and quality of CTFs since earlier in the year. It seems they are starting up again though and that's great news.
This weekend our team took part in CSAW 2015 Qualifiers round and finished a respectable 124th or so out of 1000 or so teams by scoring 2,210 points. I'm happy with that but more happy with the way the team worked together. Great job guys.
Here is the clue for this writeup:
So a remote server listening on port 12012 and a file called "ftp", we perform the usual first steps. Download the file, use "file" command, use "strings" to check for simple flags.
We found it was a Linux ELF binary for 64bit architectures. Given this is a reverse engineering challenge I placed the file directly into IDA Pro to browse the disassembly while starting the process on a local system. It's written quite well (for a CTF program!) so it starts up first try and listens on port 12012 on localhost.
Upon connecting we see a welcome banner, I try the usual FTP commands such as USER and PASS and these work correctly. I don't have a valid username yet so let's check out those functions in IDA Pro to see how authentication works.
In IDA Pro I first open the Strings sub-view and find a nice easy string related to authentication. I found one called "Please send password for user". I double click this string and then follow the Data Xref to a function "sub_40159B".
This function seems to be taking the username and password, then validating them against stored constants. In the IDA Pro decompilation, the authentication mechanism looks like this:
So I've already renamed some of those functions in the snippet above, what it's doing is validating that your username is "blankwall" and your password hashed is equivalent to 0xD386D209.
Next I built a quick and dirty "FTP Client" in Python to send username "blankwall" with a password of "ABCDE" to see how the hash function deals with that. I located the hash function at sub_401540 and set a breakpoint there in GDB w/PEDA.
Note: You have to be careful when debugging this program as it fork's a new process ID for each connection, so I only attach the debugger after sending my username (but before sending my password).
Here's the code of my testing client:
I found that this function performs what I later found to be called "multiplicative hash" on the user input. It also uses a modulus to prevent overflowing the integer. Since a modulus is used, reversing the algorithm to "decrypt" the stored password value of 0xD386D209 is not possible as information is destroyed during the hashing process.
So the only way to recover the password I thought was to recreate the algorithm exactly and then brute force inputs until my hashed output was 0xD386D209! In order to do that I found the decompiled version of the function to be a good starting point:
However for me stepping through the binary in PEDA instruction by instruction gave an even simpler explanation of what the function was doing to the input as we can see the hash function building the output in the RAX/RCX registers. Here is the code I worked on. It prints the hash value after every calculation. This enables me to follow along with GDB to ensure my implementation of the multiplicative hash algorithm is correct.
Which when run showed me the output step by step:
This final value agreed with GDB so I knew it was correct.
Once I had this function implemented in Python, I used the magic itertools Python module to calculate every combination of alphabetic characters. I tried 4,5 and finally 6 character passwords before finally striking gold and finding that the password "cookie" hashes successfully to 0xD386D209. I used this code for that:
Now we had the password I was able to login. After login though I was again stuck. We could use the LIST command to view the files stored on the FTP server but the usual FTP command "RETR" to retrieve files seemed to be bugged and kept throwing up "Invalid characters" error message.
Back to IDA Pro. At this point I spent an hour or so reversing the RETR command to find out what I needed to fix to get it to send me a file. To no avail really. Then I regrouped my thoughts and approached the problem from another angle. What if the solution was built into the FTP server itself.
A quick few minutes later and I identified the function "sub_4025F8" which seemed to only exist to give a flag called "re_solution.txt" to connected clients! Great! This function is only called from one place too. In fact it's only ever called in response to entering a simple command in the FTP server:
So back to the server we went, logged in with our found username, cracked password, and knowledge of the command, and we were rewarded with the flag!
Final exploit for this challenge follows: