Wednesday, December 11, 2013

IRISSCON 2012 CTF: Security Challenge VIII

Now we're going to work on Security Challenge VIII. I've actually been sitting on this solution for a while - Java reversing is pretty easy so it was one of the first ones I did. Challenges 2 and 7 are still evading me, but I'll get there eventually.

In this challenge, you're presented with a "Government File Store" system. When you first run the provided .jar file, you get prompted for a login. 

Some real quick cheesey brute force attempts didn't work, so I'll move on to a Java decompiler. I used DJ Java Decompiler which got the job done, but I'd prefer to find a free option if anyone wants to chime in with one. Inside the classes of this program, there's one called That's probably what we're interested in, and it's a pretty simple SwingUI with two buttons and a couple of text fields. So going straight to the only ActionListener.actionPerformed() implementation I can find, there's a branch of the method which handles the login button.
Well that pretty clearly gives us the login credentials - we actually probably could have found this using strings against the unzipped jar file. Whatever though, we got the answer so we'll move on. After logging in, we're presented with this screen, which is an instance of the SecureFileStore class, which we know from above.
In this, we can browse to a file, tell it to be uploaded, and get some status updates out of it. Looking at the source, there's a function  upload() in the class which references a SubmitFile class in the same package.
So going to look at the function we can see some interesting things. First off, we notice that it's using a ZipOutputStream, indicating that it's created an encrypted .zip file. Next, there's another line near the beginning of this function which sets the password of this zip to the SHA1 hash of the file's name: String password = getSHA1Hash(inputfile.getName());. Then it uses some standard Java classes to create an HTTP connection, but starts referencing a class Config to retrieve a URL, username, and password. So we'll go to the Config class and see what's up with getURL(), getUsername(), and getPassword(). All of these reference a function called uncipher() which they pass some constant byte arrays to.
Examining the unCipher() function, it's clearly a really simple XOR function. I wrote a quick Python script to run the uncipher for myself which yielded a url, a username, and a password.
So navigating to that URL and hoping that it implements a GET so we don't have to try to figure out where the admin panel is is the next step. Luckily we're met with the familiar HTTP based login prompt, into which we'll input those credentials we just found.
Damn. All we get out of that is a nice little string that says "gfs:1". Maybe there's an index.php there though. So we'll try to navigate to that and we're met with the list of uploaded files and some instructions.
As they say, it's pretty obvious which file we want as there's only one that seems to be interesting whatsoever. So we'll download the memo to the admin and try to open it up with something like 7zip (since they claim it won't work with Explorer's unzip). If you notice the filenames, all have a timestamp appended after the original extension of the file and before the .zip extension. It's probably a good bet that we don't want the hash of the whole .zip filename, because who names their files like that? So we'll hash just "memo_to_hof_admin.txt" and that gives us "293b663b729409237c28c2ff5659b0ba22caf50b". Type that into 7-zip when we go to extract this and we get a text file with the link to a hall of fame and credentials to edit it...why would we want that?
But whatever we'll go there and login. Oh, it gives us the key lolololol.

IRISSCON 2012 CTF: Security Challenge VI

So after a long hiatus I'm back to working on the IRISSCON 2012 Security Challenges. This one is going to be for Security Challenge VI which presents us with a website where we can register an account, go to a members only page, and then try to access an admin page.
So first I tried some SQL injection on the login page and registration page and didn't get much. So we'll register an account and poke around. Going to the members area and click the admin only link gives us a little error message.
There's not much else in the UI that seems exploitable, so let's fire up Burp to see what's going on in the communication end. I logged out, and then started with the login page. During the login process, I noted that some cookies are being set for deletion by marking their values as "deleted" and also setting their expiry date a year in the past.

But I don't really care what the server has to say about that so I'll change it and see what happens.

Then we're met with a nice little error - very simply we just get a response that says "SQL Error" (I'll theorize on why later). So these fields are obviously used for something, which means we may be on the right track. I changed the usernamesch6 cookie value to "admin" to see what happens, but no dice. I actually struggled with this part for a long time and went to read about cookie based SQLi (semicolons have to be encoded) and in the example I found at The Infosec Institute I noticed something I hadn't thought of - they decode one of the values from Base64 into plaintext. Web developers love that crap, so I decided to base64 "admin" to "YWRtaW4=" to see if we got any different results. The error went away (still going to explain the SQL Error before, don't worry), but we still get no access. So maybe some SQL injection is in order! Let's try just adding a single quote as base64 ("Jw==") and see what happens.

SQL Error! That's a good sign. The same problem exists in the password cookie. I tried using my login credentials using in B64, but that didn't work. So I went on the assumption that the username is being matched to "admin" and worked from there. I tried using ' or '1'='1';-- in the password field, but didn't get anywhere. So let's see if the SQL query checks the username first and the password second, such that we can cut off the password verification. admin';-- is encoded in B64 as "YWRtaW4nOy0t" and used in the username cookie which works.
Now, all this base64 into SQL business is probably why "jpaglier" wouldn't work. It actually broke down at "jpaglie". Each letter in base64 represents an octet triple and I'm assuming that inserted a control character or something that SQL couldn't handle causing the crash. It helped us though, so cheers to that.

Tuesday, December 10, 2013

Reflected and Stored XSS on

So this little startup at recently posted a link on my university program's Facebook page asking the computer science students what they think. Of course the first thing I did was start inputting characters into the search bar which might show the presence of XSS or SQLi vulnerabilities. Unfortunately, I didn't think to take screenshots during this process, but it was all pretty simple to follow - I wasn't super thorough during this, so I let them know they're going to want to do a serious audit in the future.

First I tried using a single quote in the search bar (controlled by a URL parameter q), which would reload the page with the text \' in the search box. Looks like improper escaping to me (probably using the PHP addslashes() function). So then I tried to break out of the value attribute of the HTML element by using a double quote instead. Lo and behold, just a single \ appeared in the search box and the search button started rendering weirdly. Next I decided I wanted to include some JS in the page, so I input "> <script> alert(1); </script> into the search box. Looking in Chrome's developer console, the XSS Auditor fired a warning that it was blocking JS from running because it appeared in the URL. Cool. It's important to note that the addslashes() function was breaking the HTML if I tried to input well-formed markup like <img src="" />, but Chrome is nice to developers so  it inserted quotes for me (and rendered the page how I wanted) if I left them out a la <img src= />So for the report I filed, I put in an img tag containing a funny .gif just to show I could muck with things, but didn't stop there, because we can obviously have a little more fun at this point, but nothing too special.

Next I moved on to their posting page. I did the really blind method of tossing a <script> alert(1); </script> into every text field and into the value attribute of every select tag and submitted. It happily chugged along, and my alert boxes were showing up in a number of places across the site.

Following that, I went back and used the same parameter in the URL of the search page to embed an iframe into the page pointing at the ones which ran the stored JS which also got code executing on that page. My memory is fuzzy about whether or not I could get a script that I had hosted off-site running in that page or if Chrome was blocking that too, but I'm sure some more fiddling could have gotten stuff done. In the end, we had the url;%3C/script%3E.

I reported the issue to the administrators and was pleased to receive a call back within 15 or 20 minutes, when we had a conversation about what I had done and how to mitigate it. I fired them off some OWASP reference material and the issue seems to be fixed now (though I only did a cursory check). They expressed a keen interest in learning about the problems (graciously giving me permission to do this writeup) and mitigating them and I'm extremely happy with their response. Keep at it guys.

Saturday, November 2, 2013

Mini CTF: Guess the Key

Once again, we'll take a look at a Mini CTF challenge. This one gives you an executable that lets you input a key and then verifies it, giving you some feedback.

Pretty simple. So we'll fire up ollydbg (IDA might be in order if this was more complex, but it's not and instant feedback is nice) and check out what's going on. Looking at the strings in the executable reveals 4 strings that obviously represent win/lose codepaths.

So we'll jump to where these are referenced in the code. For kicks we'll go straight to the success and see what's going on there.

So there's a condition that is checked and based on that we jump to either success or failure. Right above it is a series of operations followed by checks and similar jumps which happen to look very much like checking a string char by char. So we'll go up to the top of that loop to see what happens.

So at the beginning, we check that the string is 16 (0x10) characters long then check each character in the string for specific properties (additions, subtractions, XORs, rotations, etc.). I'll spare you the details (the work here was more tedious than it was hard), but it's nothing too complex. The only issue I ran into was mixing up the semantics of the ROR instruction, which wrap the register values rather than pad them like a shift.

In the end we get the key "d4e364fe580d793c" and we're done.

Friday, November 1, 2013

IRISSCON 2012 CTF: Security Challenge III

So it's time for IRISSCON 2012's Security Challenge III. Poking around we see a list of members that links us to an information page about each member, a restricted area that we need to login to get to, and a login page. First I tried SQL injection on the login form, but didn't seem to get much. However, the member info page has a GET parameter in the URL (member-info.php?id=1) so let's see if that's broken maybe. Adding a ' to break the query (member-info.php?id=1') seems to work and even gives us a hint.

So lets play around and see if we can get a basic query working - sqlite_version() probably won't tell us much, but we can figure out how much information we can get once we dig deeper. This seems prime for a UNION injection and we know that there's at least 5 columns being returned from the database, so lets give member-info.php?id=1' UNION SELECT sqlite_version(),1,1,1,1;-- a try.

Fantastic, so we know we can at least bring 5 columns in. Let's see if we can find anything about the tables in the database. The SQLite FAQ points us at the sqlite_master table, which just happens to have 5 columns (easy mode). So let's change the query to member-info.php?id=1' UNION SELECT * FROM sqlite_master WHERE type='table';-- Too bad it gives us the same information about let's try it with an ID that doesn't exist so that first query returns nothing. member-info.php?id=10' UNION SELECT * FROM sqlite_master WHERE type='table';-- gives much better results.

Nice! We even got the accounts table right away, though the first time I went through I had to poke around the database by changing the rootpage in the query until I found a table with useful information. Now we can see that this table has 3 columns, so we'll need to pad its output a bit in the query. Changing it to member-info.php?id=10' UNION SELECT *,1,1 FROM accounts;-- gives us some really useful information.

We now have the username "stallone" and password hash, so drop that into CrackStation (which I like to use before I fire up hashcat) and we get the password "fire". Logging in with those credentials lets us get to the members only area and we're done.

Mini CTF: Crack the Code

Today we'll quickly go through Mini CTF's Crack the Code challenge. In this one we're given a python file that takes in a 7-digit code and does something based on that.

Alright that's cool. So let's take a look at the source to see how this works.

So, like we'd expect it takes in the code, validates it, and then uses that code to decrypt some message. The validation code is pretty simple: it hashes the code in a few different ways, then checks certain positions in those hashes to see if it matches.

This may have some collisions because it's not a very thorough, but brute forcing this won't be expensive so let's just try the easy route for now. We'll circumvent the rest of the program and write our own loop that checks all the valid codes from 0000000 to 9999999 and, if we find one that validates then print it, run the decryption and halt.

This runs pretty quickly, giving us the pin and the key.

Mini CTF: Crack Me!

Another one of Mini CTF's challenges is one called Crack Me! Starting out, just running the program shows that it wants to check for a CD to be present to even get us started.

Obviously I don't have this CD, so we have two options: create a CD that matches whatever requirements the checks verify with or go ahead and hack away with a debugger. I don't have blank CD's because its 2013 and we're going to need to crack anyway, so let's fire up ollydbg. First we'll check out the strings and see if there's any obvious check close to that window being spawned. We arrive at 0x0040136E and pretty close by we can see the function prologue at 0x00401358.

We can use ollydbg to find where this function gets called from (right click -> go to -> jump or call to selection) and we see there's only one place this function gets called.

So we jump to 0x00401503 and we'll break at its prologue (0x004014EC) to see if there's anything interesting going on there that we might be able to use to bypass the check for the CD.

From there we notice that at 0x00401501 there's a JZ op that we fall through before our call to the error function we came from. Let's reverse that logic to a JNZ and see what happens.