When Is Blind SQL Injection Used?
In typical SQL injection scenarios, the targeted application uses unsafe methods of inserting unvalidated user input into database queries. This lets attackers enter SQL code instead of the expected inputs, causing the database to execute injected SQL statements. An example of PHP code vulnerable to this type of attack would be:
$sqlQuery = "SELECT * FROM Products WHERE ID = " . $_GET["id"];
By supplying suitably crafted SQL for the ID query string parameter (which can be as simple as modifying a URL), the attacker can execute arbitrary queries, and potentially (if process privileges allow it) even administrative operations, including dropping tables. The usual approach to extracting data would be to append
UNION SELECT to a valid ID value, followed by the query body. Apart from directly reading data using injected queries, information about the database name, version, and structure can also be revealed by internal server error messages triggered by deliberately malformed SQL code. Our blog post on SQL injection provides a detailed description of normal SQL injection attacks – but what exactly makes blind SQL injection different?
Depending on the SQL query’s syntax, server configuration, and the application itself, many vulnerable web applications might not return any useful information following an injection. They might also just show a generic error page when confronted with unexpected SQL code. However, as long as the underlying SQL injection vulnerability exists, attackers still have several ways of exploiting it to extract information from the database. These attacks are called blind injections because the attack results are not directly visible, and rely on analyzing server reactions to various injected statements. There are two main ways to approach this: through a series of yes/no tests or by checking differences in response times.
Boolean-Based Blind Injection
Presented with an injected query that is valid SQL but was not expected, an application with a blind SQL injection vulnerability may behave differently depending on the result. For example, the application may not expect NULL values in its query results. By using carefully crafted queries and comparing the server response with its reaction to a known false question, attackers can trick the server into giving yes/no answers about database structure and content. While this is much slower than simply selecting data, a series of such questions can be sent using automated SQL injection tools to extract useful information.
Using such true and false questions, attackers can extract small amounts of data, such as table names, column names, the number of columns in a specific table, and so on. This is done by guessing characters one by one and checking server reactions to each guess.
Let’s say we have an online store that is vulnerable to SQL injection into query parameters, runs on SQL Server, and uses the following URL format to show product details:
We start by injecting something that is definitely false and making a note of the server’s reaction (injected SQL in red):
http://store.example.com/storefront?prodid=7 and 1=42
Now let’s inject the following code to check if the name of the first table in the database starts with “a”:
7 and ( SELECT TOP 1 substring(name, 1, 1) FROM sysobjects WHERE id=( SELECT TOP 1 id FROM ( SELECT TOP 1 id FROM sysobjects ORDER BY id) AS subq ORDER BY id DESC) ) = 'a'
The URL supplied to the server would be:
http://store.example.com/storefront?prodid=7 and (select top 1 substring(name, 1, 1) from sysobjects where id=(select top 1 id from (select top 1 id from sysobjects order by id) as subq order by id desc))='a'
If the server responds differently than for the false question, we know the first character is “a”. Then we can change to
substring(name, 2, 1) to check the second character, and so on.
There are several ways to optimize this brute-force approach, for example converting everything to lowercase and running a binary search by comparing character ASCII values rather than actual characters. Assuming that only the 26 basic Latin characters are used, this lets the attacker guess each character in no more than 5 attempts (log226). Adapting the previous example, this would start in the middle of the alphabet, at the letter “m” (ASCII 109):
7 and ( SELECT TOP 1 ascii(lower(substring(name, 1, 1))) FROM sysobjects WHERE id=( SELECT TOP 1 id FROM ( SELECT TOP 1 id FROM sysobjects ORDER BY id) AS subq ORDER BY id DESC) ) > 109
A true answer means that the first letter is in the half above “m”. The next question would ask about the middle value between “n” and “z”, which is 115, and so on until the letter is found.
Time-Based Blind Injection
If the vulnerable application returns absolutely no clues about the injection result, attackers can resort to time-based attacks. Again, these use a series of questions to extract information from the database server, but this time the injected code causes the server to delay its reaction if the answer is “yes”. That way, a quick reaction is interpreted as a “no”, and a delayed reaction means “yes”. The easiest way to cause a delay is to use the
WAITFOR DELAY clause, if the RDBMS supports it. If that’s not possible, the attacker can use a time-intensive operation to obtain a delay for the “yes” answer.
The injection code below is very similar to boolean-based attacks but with a delaying statement instead of a true/false reaction check. Adapting the example above, we might inject a semicolon to end the first query and then add a whole new second query to ask if the first character of the first table is in the second half of the alphabet:
7; IF( EXISTS( SELECT TOP 1 * FROM sysobjects WHERE id=( SELECT TOP 1 id FROM ( SELECT TOP 1 id FROM sysobjects ORDER BY id) AS subq ORDER BY id DESC) AND ascii(lower(substring(name, 1, 1))) > 109) ) WAITFOR DELAY '0:0:5'
If the answer is true, the server will execute
WAITFOR DELAY '0:0:5', causing it to wait for 5 seconds. By monitoring web server response times for different URLs, the attacker can indirectly extract information from the underlying database. Because of the delays, this type of attack is even slower than boolean-based attacks, but with automation and optimization, it can still reveal a lot of information in a realistic timeframe.
Preventing Blind SQL Injection Attacks
Blind SQL injection attacks are just a specialized subset of SQL injection in general, so the risks and remedies are exactly the same as for regular SQL injection vulnerabilities. The number one rule is to avoid building dynamically generated database queries using string concatenation. By using parameterized statements exclusively, you can greatly reduce the threat of SQL injection. Escaping SQL special characters in user inputs is also effective if applied consistently, and most languages and frameworks provide ready tools for this. For a detailed discussion of SQL injection, see our SQL Injection Cheat Sheet, which also includes a section on blind injections with examples for MySQL, Microsoft SQL Server, and Oracle databases.
Alongside XSS attacks, SQL injection remains a top threat to web applications, as seen on by the OWASP Top 10 and SANS/CWE Top 25 lists. Regular application scanning using a proven vulnerability scanning solution is vital to maintain security, and Invicti can detect many kinds of SQL injection vulnerabilities, including blind injection.