Introduction to SQL Injection and Exploitation (MySQL 5 error based)
In this blog post, I will explain about SQL Injection attack and a basic exploitation by browser-based payload injection.But basic concepts of SQL is needed before you read next.
Let's begin.
What is SQL Injection ?
SQL Injection is a web application security vulnerability, which allows an attacker to "Inject" and execute his/her SQL queries in the context of the web application/website running. This is generally done by injecting SQL queries in GET
or POST
HTTP Requests which are then passed to the DBMS system without proper sanitisation.
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 - OWASP Project
Exploitation (MySQL error based)
Before proceeding to exploitation, I would like to give some details about the vulnerable webpage and environment used in SQL Injection exploitation demo.
Webserver: Apache 2.2 for Windows
Server Side Script Processor : PHP 5.3
DBMS Server : MySQL 5.5.13 for Windows
Operating System: Windows XP
Our end goal will be to exploit the SQL injection vulnerability, enumerate all the databases, tables, columns and to grab some data stored in a table found earlier.
So let the hacking begin. Please follow the URLs in browser carefully to understand everything.
The page looks like this:
So, from above it looks like "support" variable in URL containing value "yes" is used in the internal SQL Query to display the table since the value in "SUPPORT" column is always"YES". Now let's play with the URL for SQL Injection:
After adding a single quote '
character in the URL the page throws an MySQL error which says syntax error near yes. So it seems the page vulnerable to SQL Injection.
The back-end SQL statement assumed here is
SELECT * FROM SOME_TABLE WHERE SUPPORT ='yes'
Now let's try to harvest some data using UNION operator.
The UNION operator
The UNION
operator is used to join the result of two or more SELECT statements. But UNION operator comes with a twist. It requires two criteria to be fulfilled.
I. The two queries should return the same number of columns.
II. The data-types in the corresponding columns of the SELECT
statements must be of
the same orcompatible type.
So first thing here is to find the number of columns.
How can we find the number columns in the table in which the SQL commands are running. First approach is that you can guess number of columns from the data displayed in the browser or you can test by trial-and-error method using the ORDER BY
keyword.
The ORDER BY keyword
The ORDER BY
keyword displays the result sorted by specified column name or number.
So let's play with ORDER BY keyword to find the number of column(s) in the affected table.
ORDER BY 20
fails here, because column number 20 does not exist therefore columns are less than 20. And %23
is the URL encoded form of #
which will comment out further portion of the statement which can fail the injection.
Assumed internal SQL Statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' order by 20 #'
Let's decrease the column count in ORDER BY
clause
ORDER BY 10
also fails, which clearly hints that column is less than 10.
Assumed internal SQL Statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' order by 10 #'
Let's further decrease the column count.
Hey! ORDER BY 6
seems to work, we see the table being displayed instead of the "unknown clause" error , that means columns are >=
(greater than or equal to) 6
Assumed internal SQL Statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' order by 6 #'
Let's increase the ORDER BY
value to 7 and see if it messes up the result or not.
ORDER BY 7
again throws an error which finally means the internal column count is 6.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' order by 7 #'
The table displayed in the browser shows six columns, but in many instances the output columns and internal SQL columns will not be same because the webpage might not want to display the result from all the columns in DBMS table . But that case is not here, it looks like the page displays all the columns.
Now, we have successfully found the number of columns, which we will use for UNION
based attack, now the problem again arises that UNION
needs exact number of columns and same data-types (or convertible data-types ). How will we do it ?. We can just substitute NULLs in place of columns of the second table and try to gather data by injecting commands into columns of 6 columns available and injectable.
Let's try to find the MySQL version in use by using @@version
super variable.
The above picture clearly shows the version displayed is 5.5.13. So we have found some very basic data using SQL Injection.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' UNION SELECT @@version,NULL, NULL, NULL, NULL, NULL#'
The above SQL statement uses column 1 to fetch the MySQL version and rest columns are filled with NULLs.
Now there is a problem the injected query also returns the data from the first table of UNION
keyword, So how can we fix it. We can force the WHERE
clause to fail by issuing an AND 1=0
or similar constraints which are always false so the first portion of UNION
will fail and only second part will be shown.
Let's try to enumerate all the available databases, See below
You can see clearly the we have got two databases available for us.
Here we have enumerated the MySQL's information_schema.columns
table and table_schema
column to find the databases available to us, because information_schema.columns
contains information regarding available databases to the user, table names, column names and etc . We have assumed that information_schema
database to exist, because by default it is available in all versions of MySQL 5.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT table_schema,NULL,NULL, NULL, NULL, NULL FROM information_schema.columns#'
Now let's try to enumerate all the tables available in dvwa database:
So, you can see columns have been successfully enumerated by using column_name column of information_schema.columns
table.Since we have used two columns for our injection, i.e., table_name and column_name we have padded further values by using four NULLs to fill 6 columns for UNION
to work.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT table_name,column_name,NULL, NULL, NULL, NULL FROM information_schema.columns where table_schema = 'dvwa' #'
Let's try to enumerate first_name,last_name,user,password from dvwa database's users table.
You can see clearly all the data in first_name,last_name,user,password have been dumped successfully !
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT first_name,last_name,user,password,NULL,NULL FROM dvwa.users #'
So, this was all about data grabbing by SQL Injection.
Wait! I've some bonus stuff to share.
Privilege Enumeration
We just saw how to fetch data using a SQL Injection attack. Now let's check for the user privileges to further extend the attack if we are on a higher privilege account.
So to do this we have to first check for the current database user.
We used user()
function to see the current database user running the vulnerable webpage and luckily we found that it's the root database user, which is the most powerful user with all high level privileges like FILE
, SHUTDOWN
etc.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT user(),NULL,NULL,NULL,NULL,NULL #'
Let's try enumerate the privileges available to the current user
We just listed the privileges available to root@localhost
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT PRIVILEGE_TYPE,NULL,NULL,NULL,NULL,NULL FROM information_schema.user_privileges WHERE GRANTEE = "'root'@'localhost'" #'
Now from the above snap, you can see a privilege know as FILE
which can be used to read and write files with MySQL.
Let's try to read C:\boot.ini
using SQL injection:
The above picture shows the content of C:\boot.ini , LOAD_FILE()
function was used to read the file.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT LOAD_FILE('C:\\boot.ini'),NULL,NULL,NULL,NULL,NULL #'
Now lets try to write a file using SQL Injection
We will write "You have been hacked" to the file C:\hacked.txt
You can see above, we have used INTO OUTFILE
keyword to write the text into respective file. You may see \N displayed which is due to the NULL columns used with the UNION operator.
Assumed internal SQL statement after Injection:
SELECT * from SOME_TABLE where support = 'yes' AND 1=0 UNION SELECT 'You have been hacked',NULL,NULL,NULL,NULL,NULL INTO OUTFILE 'C:\\hacked.txt' #'
That's all folks, Thanks !