This time around, I’d written a challenge for CSAW CTF. There were 32 challenges, in all sorts of topics such as Web, Reversing, Exploitation, etc. The challenge I wrote was the 300 point Web challenge, HorseForce.
From the start, you’re given an account to this website and a task: obtain administrative access. After logging in, you are presented with a few pages: news, users, horses. Finding the vulnerability was the easiest task. With mysql errors being printed to screen, all you had to do was throw a quote in every parameter.
However, proceeding with the typical “-1 union select 1,2,3–” reveals the next hurdle: the Web Application Firewall. From a bit of fuzzing by hand, it’s trivial to figure out that the firewall only detects attacks when using words like “union” and “select”. But how do you perform a SQL injection without these basic keywords? Some ideas are (double)url-encoding the query, using comments, or swapping case. The filter was a little smarter than that (just a little) so these tricks wouldn’t work. The real goal of this challenge is to realize that there are two “separate” processes: a firewall, and a vulnerable application. Would it be possible to make the firewall see one thing, and the application another? The answer is yes.
There are many techniques to bypass a Web Application Firewall. The specific flaw in this firewall was one of Impedance Mismatch (ie: firewall and application see data differently), and more specifically, HTTP Parameter Pollution. I have links to a relevant presentation at the end of the post. Big words aside, the trick here is that the firewall scans only the first occurrence of a parameter with the same name, whereas the application only reads the last occurrence. This allows us to finally run a successful query by making a request to the following:
/horse.php?id=1&id=-1 union select 1,2,3,4–
This request ends up evading the firewall. The firewall sees “id=1” while the application sees “id=-1 union select 1,2,3,4–”. Everyone is happy! Now let’s dump the database.
And it’s bcrypt! Before I go on, let me tell you that any CTF challenge that requires you to crack a hash is a really bad one. Okay, so cracking the password is out of the picture. And we can’t use the hash to log in. Now what? After running through the database, you should have come across a database called “sessions”. From this point on, all you needed to do was steal an active session belonging to the administrator from the database, and modify your cookie to reflect that session. And you’re in!
My biggest mistake was the logic in the firewall. Because I had to write the firewall myself, I had to parse the querystring on my own. And everyone knows parsing things on your own leads to bugs. This challenge was no exception. My firewall detection function ran through every key value pair in the querystring. The bug was that any pair containing more than one “=” was automatically ignored. This led to a lot of unintentional evasion through queries such as
/horse.php?id=-1 or 1=1 union select 1,2,3,4–
The “1=1” is something I hadn’t accounted for, but is a very common part of many SQL injection payloads. Because it was so common, many people ended up bypassing the firewall unintentionally. Grepping through the access logs shows around 150~ish people having solved it this way. I did end up patching this bug, however. I felt that although the people who solved it before the patch didn’t do it the way I would have liked, they still achieved the same goal of defeating the firewall.
I kind of liked this challenge. And I’m not saying that because I wrote it. I tried my best to make this pretty realistic; everything used in this challenge (firewall evasion, strong hashing functions, session stealing) is something I’ve come across at one point or another in my experiences.
Too many people took the brute forcing approach. Some bruteforced the login, while some used tools such as DirBuster to enumerate files. I ended up blocking all traffic from these IP addresses as not only did they approach this challenge completely incorrectly, they made monitoring the challenge difficult. There was also a ton of traffic from tools such as SQLMap and Havij but I let these go as they were more relevant to the challenge.