Exploiting blind SQL injections in 'UPDATE' and 'INSERT' statements without stacked queries by Sina Yazdanmehr - Pentestmag

Exploiting blind SQL injections in 'UPDATE' and 'INSERT' statements without stacked queries by Sina Yazdanmehr



The SQL injection attack was introduced around 1998 for the first time. This high-level risk vulnerability can be found in any database oriented application, and is able to cause critical attacks by attackers, such as retrieving or storing arbitrary data in the database or, in some cases, even enabling remote code execution. It has some rewound types, like in-band, inferential and out-of-band SQL injection, and each of these types has subcategories.

The In-band (also known as Classic SQL injection) is the most common and the easiest for exploitation. In this type of SQL injection, the attacker is able to see the injected payload, or a database error message. The Union-based and Error-based SQL injection attacks are sub categories of this kind.

The inferential (also known as Blind SQL injection) stands for another sort of this attack, when the attacker is unable to see the result of a payload or a database error message. In this case, the attacker must use conditional statements and extract data from the target database by deducing the result of injected condition. This sort has two subsets, Boolean-based and Time-based attacks.

The last type is Out-of-band SQL injection, which is not very common. It depends on the target database features, such as making DNS or HTTP requests. By abusing those abilities, the attacker can extract data and send it back as part of an HTTP or DNS request to his server. This allows him to read the data that otherwise he cannot see since he is completely unable to see any results of his queries directly.

Since inefficient user input validation causes this fault, it might happen in each type of database statements. However, the most common SQL statement in which this vulnerability usually happens is the ‘SELECT’ expression. Lots of ways for exploiting, as well as mitigating, this type can be found on the Internet for any kind of database, like MySQL, MSSQL, Oracle and even MS Access.

Due to the variety of database types, WAFs and the fact that each programmer or developer team can have a new and innovative way for passing the user input to the database, as well as protecting against SQL injection, exploiting this flaw can be different in each case, based on the potential security systems, database type, and the vulnerable SQL statement.

In this article, I am going to approach exploiting SQL injection flaws in ‘UPDATE’ and ‘INSERT’ expressions with a novel approach when you are unable to use stacked queries and when the result of executing the statements is practically invisible, making such attacks completely blind. Of course, this method might be useful to bypass security filtrations and WAFs in any sort of this vulnerability.

Stacked queries limitation

Each programming language has a DBMS connector in order to communicate with a specific database. According to the database type and the programming language, they have some limitations: one that can impact SQL injection attacks is stacked queries limitation.

The term ‘Stacked Queries’ simply means when two or more queries are queued to be executed by a database one after another in the same session. The following MySQL queries demonstrate a simple stacked query:

SELECT ID, name, lastname FROM users; UPDATE session SET lastseen = NOW();
(Code snippet 1)

As can be seen, ‘SELECT’ and ‘UPDATE’ queries have been queued. The ‘SELECT’ statement will be executed first and after that, the database engine would interpret and execute the ‘UPDATE’ query.

MySQL has more limitations in case of stacked queries. The table below is a brief comparison of these restrictions for MySQL, MSSQL and PostgreSQL with three different programming languages:


(Table 1)

In SQL injection attacks, stacked queries are typically used in order to update data in the database in case there is a SQL injection defect in a ‘SELECT’ statement. This is important since the ‘SELECT’ statement cannot have an ‘INSERT’ or ‘UPDATE’ statement as a sub-select query, so in this case it must be done through stacked SQL queries. On the other hand, if there is a SQL injection in ‘INSERT’ or ‘UPDATE’, an attacker would need to take advantage of stacked queries in order to fetch data.(Table 1)

If the attacker finds an Injection vulnerability in an ‘INSERT’ or ‘UPDATE’ statement where the stack queries are unsupported (like PHP coupled with MySQL), he would need to find a manner to be able to fetch data from the target database. Two conditions have impact on this exploitation, these are:

  • If the application throws the database’s error messages or not.
  • If the result of a vulnerable statement is visible or not.

If the application shows the error messages or the query’s result, the exploitation would be easier because some MySQL functions return the result of subqueries in their error messages, so by abusing those functions, the attacker can execute a subquery and see its result through error messages.

Suppose the vulnerable expression is the following code:

INSERT INTO user_agent values(NULL, '{$_SERVER['HTTP_USER_AGENT']}');

(Code snippet 2)

If the vulnerable application shows database error messages, the attacker could use the ‘UpdateXML()’, ‘Extractvalue()’ or ‘Name_const()’ functions. For example, by using the ‘UpdateXML()’ function like below:

INSERT INTO user_agent values(NULL, '1' and updatexml(1, concat(0x7e, (user())), 0)

and '1');

(Code snippet 3)


Since the concat(0x7e, (user())) does not return a valid Xpath, the ‘UpdateXML()’ function will throw the following error message:

#1105 - XPATH syntax error: '[email protected]'

In further steps, he can simply substitute arbitrary queries with ‘User()’ function in this statement and extract other data from the target database. In addition to these functions, an attacker might abuse ‘BIGINT’ overflow defect in MySQL. The attacker would use a subquery like this:

INSERT INTO user_agent values(NULL, '1' and (select ~0+!(select*from(select user

from mysql.user limit 1)x)) and '1');

(Code snippet 4)

Then he will receive the following error message:

#1690 - BIGINT UNSIGNED value is out of range in '(~(0) + (not((select 'root' from


(Code snippet 5)

As can be seen, the database engine reveals the subquery’s result in the error message.

These two exploitation solutions, as well as others, work when the vulnerable application throws the database error messages. They would not be useful in a case where the application has appropriate error handling.

If the application has suitable error handling, the result of the query might be shown to the user, or maybe the user does not see any result of executed queries. In the remainder of my paper, I will focus on exploiting blind and result based SQL injection in the ‘INSERT’ and ‘UPDATE’ statements.

Exploiting result based injections

Since some databases, such as MySQL, do not join strings with plus sign ‘+’, it is impossible to join a subquery result in a vulnerable ‘UPDATE’ or ‘INSERT’ statement. Other payloads with ‘AND’ or ‘OR’ conditional expression (as shown in the previous part) can be coupled with time-based conditions so that an attacker will be able to deduce the result of the injected payload by measuring the response time.

In this case, an attacker must first escape quotation mark to be able to inject arbitrary statements, after that he will need to join the injected query with previous and next strings in quotation marks. For instance, the vulnerable SQL statement that was mentioned earlier (Code snippet 2) could be exploited by injecting a malicious payload in user-agent HTTP header, and the attacker simply can escape quotation marks with injecting this mark before and after his payload.

For joining these parts, the attacker can use a conditional expression as mentioned before, or take advantage of mathematical operators, but, for this purpose, he first needs to convert the result of the subquery to its integer equivalent. The best way to perform this would be to convert the target data to hexadecimal (base 16) first, and then taking the hex value to its decimal value (base 10), so that the attacker will have target data in decimal value. This approach is the best since all databases support these types and have casting and converting data types functions, thus the attacker could calculate the hex and decimal values of target data easily.

Although this manner is the easiest way, databases are limited in data size and this can cause problems with storing long converted strings as integers. For instance, MySQL maximum BIGINT size is ‘18446744073709551615’, so due to the fact that the length of the target data is indeterminate for the attacker, he needs to chop the data by string functions such as ‘SUBSTRING()’. It is impossible to fetch a whole subquery result in one request because the database engine cannot store whole integer equivalent of result at the same time.

The attacker might chop result of injected query and extract it part by part. For example, an attacker might use the following payload for the previous sample vulnerable code:

1' * CONV(HEX(SUBSTRING((SELECT user FROM mysql.user LIMIT 1), 1, 8)), 16, 10) * '1

(Code snippet 6)

The complete query which will be executed by database will be:


mysql.user LIMIT 1), 1, 8)), 16, 10) * '1');

(Code snippet 7)

Since the number 1 is ineffective in multiplication, the result of subquery (first database username) as numbers in base 10 will be stored instead of user-agent. The result of this payload is ‘1919905652’ that can be converted to the original result of doing this conversion vice versa. It must first be taken to base 16 and then converted to a string, it can easily be done by the following expression:

SELECT unhex(conv(1919905652, 10, 16));

(Code snippet 8)

And it equals to ‘root’. This manner can be extended to extract other data from a database by changing the subquery inside of ‘SUBSTRING()’ function.

Exploiting blind injections

In the last scenarios, the attacker was able to see the payload result, or the database error messages. However, it is possible to exploit this vulnerability when the application does not show anything about result or errors of a query.

An attacker is able to overcome this issue by utilizing flow control statements coupled with delay functions in order to infer the validity of a conditional statement result and extract arbitrary data. Databases support various sorts of flow controls, one of them is ‘CASE … WHEN’, which is used for complex condition expressions. The value of the ‘CASE’ statement is compared with ‘WHEN’ statements and the body of first ‘WHEN’ expression is executed which matches with the value. Also, it accepts ‘ELSE’ expression that is executed when none of ‘WHEN’ statements match with the condition value.

An attacker might use this statement with a delay function like ‘SLEEP()’ or ‘BENCHMARK()’ to extract data blindly. Basically, when the attacker uses flow control statements coupled with delay functions, he determines a condition for the database engine and makes it wait for a certain amount of time when the condition is valid. Otherwise, the database engine responds to the request without postponement. Therefore, the attacker can measure the response time for each request and infer the validity of the condition.

The subsequent payload demonstrates how an attacker could exploit a blind injection:


(Code snippet 9)

And the final query that will be executed by the database engine will be:

INSERT INTO user_agent values(NULL, '1' AND CASE (SUBSTRING(VERSION(), 1, 1)) WHEN


(Code snippet 10)

This payload result depends on the ‘CASE’ statement validity. Since the attacker has integrated ‘SLEEP()’ function with ‘CASE’ statement, the server response will take more than 10 seconds in case the conditional statement is true, otherwise the attacker receives the response faster (instantaneously).

If the server responds to this request in less than 10 seconds, the attacker deduces that this condition is false, so he may try another condition like the following expression:


(Code snippet 11)

In this payload, the attacker has substituted the number ‘5’ with the number ‘4’ in the ‘WHEN’ condition. It means the server will respond to this request after 10 seconds in case the target database version string starts with number ‘5’.

The attacker may also try to extract some other information, such as the database username or current database name, in the first step to verify basic assumptions and make sure injected payloads are executed by the target database. After that, he could replace the ‘VERSION()’ function with more complex queries in order to extract arbitrary data from the database.


I sincerely thank Mr. Bojan Zdrnja whose expertise, experience, generous support and guidance made it possible for me to prepare this article. It is my pleasure to work with him.



Author: Sina Yazdanmehr

He is a penetration tester and information security consultant. His expertise is web and mobile applications security. He currently works for Infigo IS, and has worked for other security firms and CERT since 2009.

Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest Most Voted
Inline Feedbacks
View all comments
3 years ago

This simply doesn’t work,no way

© HAKIN9 MEDIA SP. Z O.O. SP. K. 2013