For the most part, I rarely indulge in CTF exercises due to a combination of lacking free-time and the fact that many of the solutions are often annoyingly convoluted. The other day, someone on the reverse engineering subreddit was kind enough to post links to their web challenges and after taking a look, one of them caught my eye, mostly because it involved a Java client component which is something I always have time for hacking! So, without further ado, here’s a walkthrough on the solution for solving challenge 8: Government File Store .04. You can view the full challenge description here.
Getting Started
After downloading the client component and following the instructions, we have our client up and running.
The first part of this challenge is to find the username and password required to log in. To accomplish this, we’re going to use JD-GUI as our Java decompiler of choice. After opening the .jar file in the decompiler and expanding challenge 8, we see the class “Login Panel,” which seems like a good starting point. After looking through this class, it’s pretty easy to spot the two string constants, “username,” and “password,” and sure enough, by using these values we can successfully log into the application.
And after logging in…
Next, we select a .txt file to upload and after doing so, the UI gives us some interesting information…
Compressing & Encrypting …done!
Uploading …done!
Members with proper clearance can now access your file via the government repositories.
Neat. So the next task is to find where the file went. After using my method (which we’ll see shortly), I realized that you could probably get access to this information using Wireshark, but because I was feeling sassy, I decided to create a new project in Eclipse, import the .jar to my libraries, and extract information using methods already provided to me in the code. For example, in the decompiled .jar file, we see the parameters uploadurl, username, and password which are stored as encrypted byte arrays.
private static final byte[] uploadurl = { -117, 46, 126, 45, 117, 100, -114, 47, 86, -115, -108, 52, -74, 118, 19, -1, 1, 35, -115, 92, 28, -121, 82, 43, -83, 104, -4, -72, -128, 34, -115, 37, -108, -14, -86, 63, 18, 20, 68, -87, 6, -9, 97, 44, -109, 27, 101 }; private static final byte[] username = { -126, 62, 103, 52, 33, 34, -46, 63, 69, -127, -113, 117, -89 }; private static final byte[] password = { -126, 62, 108, 109, 119, 114, -109, 120, 83, -120, -97, 124, -90, 126, 20, -10, 66, 114, -39, 90, 94, -100 };
Within this class, we also have a cipher variable and an unCipher() method:
private static String unCipher(byte[] ciphertext) { byte[] deciphered = new byte[ciphertext.length]; for (int i = 0; i < ciphertext.length; i++) { deciphered[i] = ((byte)(cipher[i] ^ ciphertext[i])); } return new String(deciphered); }
Even more interesting and useful to us is the three public methods, getURL(), getUsername(), and getPassword() which return the decrypted variables. So, we can use some simple Java like the below to retrieve this data:
package ghettoHax; import com.security.challenge.eight.*; public class Main { public static void main(String[] args) { System.out.println("URL: " + com.security.challenge.eight.Config.getURL()); System.out.println("Username: " + com.security.challenge.eight.Config.getUsername()); System.out.println("Password: " + com.security.challenge.eight.Config.getPassword()); } }
And after running our code, we receive the below output:
URL: http://damo.clanteam.com/sch8/file_upload/u.php
Username: administrator
Password: adf08923dhdfsdfg745klx
Cool, now we’re making progress. After going to http://damo.clanteam.com/sch8/file_upload/u.php and providing our credentials, we are presented with a page that simply displays “gfs:1.” If we strip off u.php and try to view the document root, we see what we’re looking for.
Oh boy, this secure government file store sure doesn’t seem very secure. Anyways, for this post, the file we uploaded is mytest.txt_1367182640000.zip and after downloading the file, when we try to extract it, we’re required to enter a password and none of the credentials we have so far seem to do the trick. I initially thought that the secret was in the cipher variable and/or the uncipher method, but after looking at these things more closely, this was clearly not the solution. Back to JD-GUI, we have the SubmitFile class which contains the method, CompressAndEncrypt which has exactly what we’re looking for.
String password = getSHA1Hash(inputfile.getName()); parameters.setPassword(password);
These two lines tell us everything we need to know which is that the password for the file is the resultant SHA1 hash of the file name itself, and for mytest.txt, this is 7106a9c7891aadea01397158206793afe19ec369. Using this as the password allows us to open our file, but the file hasn’t been changed and doesn’t give us any new information.
At this point, even though we were able to open our file, we still can’t get to the leaderboard to add our name, but we do know one very important thing – the password for all other “securely” uploaded files. The file, “memo_to_hof_admin.txt_1337017286000.zip” sounds pretty interesting and based on the file naming convention, the original file name was memo_to_hof_admin.txt and the password for this zip file will be the SHA1 hash of the filename. After downloading the zip file and providing the password “293b663b729409237c28c2ff5659b0ba22caf50b,” we have arrived at the end our or challenge.
And let’s not forget to add ourselves to the hall of fame.
Overall, the exercise wasn’t that complicated, but it was pretty fun and a nice example of poor programming practices that you often see in the wild, such as flawed crypto, hardcoded credentials, insecure transmission of sensitive date, etc.
Hats off to Damien Reilly for putting this challenge together and I hope to see similar ones in the future!