Here we are with a remake/improvement of the HTI challenges from Hack Dat Kiwi 2015! I particularly enjoyed this challenge, since I solved the previous one as well. The goal is to bypass a filter for SQL injections in a web application. Last time I got away with a trick: comments weren’t detected as dangerous, but you can actually put code in comments in MySQL! It probably wasn’t the intended solution, but a single query yielded all 3 flags of the challenge with minimum effort.
Now, this edition’s challenge was harder. Most special characters (including
*) were just banned from the input, making it hard to find tricks.
The system was made of two pages: a login page simply requesting a password, and a ticketing system where one could send tickets with two separate fields (title and message), as well as read them again after submitting.
I fiddled with the login page, without any luck: the filtering seemed to be working pretty well. The useful information I could find by reading the error message whenever an attack was detected is that the password was in a table called
secrets, field name was
password and there was a single row.
OK, so the job is to read from this table.
By playing with the ticketing system, it’s easy to guess that it just performs an
INSERT query in the tickets table (let’s call it
tickets), something like this:
INSERT INTO tickets(title, message) VALUES('$1','$2')
where of course
$2 are the inputs we can provide. Remembering that we can always display the contents of the table
tickets via the ticketing system, we just have to perform our injection here. Problem is, most special characters remain forbidden, and we have to inject both fields in order for the query to be valid.
I will save you all the errors that I’ve made (a lot, trust me), and I will jump straight to my solution.
Value of the title field:
[NUM]' DIV (SELECT CONV(HEX(SUBSTRING(password,[CHAR],1)),16,10) FROM secrets),
Value of the message field: a space followed by
[NUM] I put each time a different number in the ASCII range 0-127 (well, I actually restricted to printable characters), and made multiple requests. Instead of
[CHAR] I put the position of the character I wanted to read in the password. This query would put 0 in the message field for all characters before the one actually in the password, and 1 for it and all the ones after.
Example: if the first characted is an
f (ASCII 102), then my injection with 102 would yield 0, my injection with 103 would yield 1.
Complete injected query with 102 becomes:
INSERT INTO tickets(title, message) VALUES('102' DIV (SELECT CONV(HEX(SUBSTRING(password,1,1)),16,10) FROM secrets),',' '');
Let’s script this in bash with curl and make a ton of requests for each character to find the first number that yields 1!
The password was
Not an efficient solution, I think there might be something better. Thanks to ax for teaching me that ASCII can replace that ugly CONV/HEX combo.