Hack.lu CTF 2014 – Killy The Bit (web200)

October 27, 2014

After reading other write-ups for this task (https://github.com/ctfs/write-ups/tree/master/hack-lu-ctf-2014/killy-the-bit), I thought I should write one. Not that it is better (I really prefer the other solutions) but I find it interesting because it involves luck and brute force (that you actually don’t see often in a CTF :))

I looked at the challenge after the three hints were published: I know there is a passwd column, blind is a bad idea and it is possible to solve the challenge with one request

So here the relevant part of the source code (https://github.com/ctfs/write-ups/blob/master/hack-lu-ctf-2014/killy-the-bit/index.phps):

There are 3 queries:

1. $res = mysql_query("SELECT name,email FROM user where name='".$_GET['name']."'");

if result then email is sent 
else continue

2. $res = mysql_query("SELECT name,email FROM user where name sounds like '".$_GET['name']."'");

if no result then “user not found, are you sure”
else continue

3. $res = mysql_query("SELECT name,email FROM user where name sounds like '".$_GET['name']."'");


if result then display the name

The output of the last query is then displayed so the idea would be to do a union with the passwd column to display it:

admi' UNION SELECT passwd,2 FROM USER WHERE NAME = 'admin' -- 



This would then display the admin password.
The problem is that this injection returns a result in the first query and therefore an email is sent and the program returns.

The expected solution was to simply add

LIMIT 1,2


In the first query, it doesn’t return anything (as the resultset only contains one entry but due to the offset 2 it is empty) but it returns an entry containing the password on the following queries.


I didn’t think about that…
Instead that’s what I wanted to do:
– I want query 1 to return an empty resultset (i.e. no password sent)
– I want query 2 to return a result (i.e. no “we couldn’t find a user, are you sure it is…”
– I want query 3 to return a result (the injection)


My idea was to use some kind of randomness in the query to make it works in 2 and 3 and fails in 1: for example WHERE RAND() > 0.5. By submitting the query multiple times, it should work at some stage 🙂 (probability of 1/8)
Unfortunately RAND() was filtered (because of the AND filter). Why isn’t it called RND()?


So let’s use SYSDATE() instead that returns the time in second when the query is executed. Idea is then to check the parity of the second: if uneven then true else false. It was not possible to work with millseconds with this mysql version.
Now is time to build the query. We need to restrict the password to the one from the admin and add the randomness. As AND and && were filtered, let’s use CASE:

' UNION SELECT passwd,1 FROM user WHERE CASE WHEN name = 'admin' THEN MOD(sysdate(),2) ELSE 0 END-- 



And now send the request until the first request is executed at an even second and the second and third at an uneven second.

Running the script:


-->A new password was generated and sent to your email address!
-->We couldn't find your username!<br>Are you sure it is ' union select passwd,passwd from user where case when name = 'admin' then MOD(sysdate(),2) else 0 end-- ?
-->A new password was generated and sent to your email address!
-->We couldn't find your username!<br>Are you sure it is ' union select passwd,passwd from user where case when name = 'admin' then MOD(sysdate(),2) else 0 end-- ?
-->We couldn't find your username!<br>Are you sure it is ' union select passwd,passwd from user where case when name = 'admin' then MOD(sysdate(),2) else 0 end-- ?
-->We couldn't find your username, but it sounds like this user:<br>flag{Killy_The_Bit_Is_Wanted_for_9000_$$_FoR_FlipPing_Bits}<br>

 



After maybe 40 tries I finally caught the right moment 😉

 

Leave a Reply