Introduction to SQL Injection and Exploitation (MySQL 5 error based)

Introduction to SQL Injection and Exploitation (MySQL 5 error based)
Photo by Rubaitul Azad / Unsplash

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 !