Blind SQL injection

So far, we have identified and exploited a common SQL injection vulnerability, where the requested information is displayed in the server's response. There is a different type of SQL injection, however, where the server responses don't reveal the actual detailed information, irrespective of whether or not it exists. This is called blind SQL injection.

To detect a blind SQL injection, you need to form queries that get yes or no responses. This means that a query responds in a consistent way when the result is either positive or negative so that you can distinguish one from the other. This can be based on the response's contents, the response code, or the execution of certain injected commands. Within this last category, the most common method is to inject pause commands and detect true or false based on the response time (time-based injection). To clarify this, let's do a quick exercise with DVWA. You will also use Burp Suite to facilitate the resubmission of requests.

In a time-based injection, a query is formed aiming to pause the processing N seconds if the result is true, and executing the query without pause if the result is false. To do this, use the SLEEP(N) function in MySQL and the WAITFOR DELAY '0:0:N' function in MS SQL Server. If the server takes this time to respond, the result is true.

First, go to SQL Injection (Blind). You'll see the same User ID textbox from the other SQL injection exercise. If you submit a number, it shows the first and last name for the corresponding user. This time, however, instead of showing an error, if you submit an apostrophe or single quote, it shows an empty response. But what happens if you submit 1''? It shows the information of user 1; so it is injectable:


Let's review the information you now have. There is a valid user with ID=1. If you submit an incorrect query or a user that doesn't exist, the result is just an empty information space. Then there are true and false states. You can test these by submitting 1' and '1'='1 and 1' and '1'='2:

The false response is shown in the following screenshot. Notice how some characters are encoded in the address bar of the browser (for example, '=' is encoded to '%3D'):

To ask yes/no questions, you must replace '1'='1' with a query that should return true or false. You already know that the application's database name is 'dvwa'. Now submit the following:

1' and database()='dvwa  

You get a positive response here. Remember that you don't include the first and last quotes because they are already in the application's code. How do you know that? You need to iterate character by character to find each letter, asking questions such as, "Does the current database name starts with a ?." This can be done one character at a time through the form or Burp's Repeater, or it can be automated with Burp's Intruder.

Send a valid request from the proxy history to Intruder, and set the inputs as shown in the following screenshot:

Notice how after a is set as input, there is %25. This is the URL encoded % (percentcharacter. URL encoding is done automatically by the browser, and it is sometimes necessary for the server to interpret the characters sent right way. Encoding can also be used to bypass certain basic validation filters. The percent character, as mentioned before, is a wildcard that matches any string. Here we are saying if the user ID is 1, the current database's name starts with a, and it's followed by anything; the payload list will be all of the letters in the alphabet and the numbers from 0 to 9. SQL string comparison is case insensitive, unless specifically done otherwise. This means A is the same as a:

You now have the input position and the payloads, but how will you separate the true responses from the false ones? You will need to match some string in either the true or the false result. You know that the true response always contains the First name text, as it shows the user's information. We can make a Grep - Match rule for that:

Now start the attack, and see that d matches with a true response:

To find the second character, just prepend d (the result) to the input position:

Start the attack again, and you'll see that v is the next character:

Continue this process until none of the possible inputs return a positive response. You can also construct the first round of queries to obtain the length of the name using the following injection and iterate the last number until the correct length value is found:

1'+and+char_length(database())=1+--+'  

Remember, as Intruder doesn't add encoding as the browser does, you may need to add it yourself or configure it in the payload configuration. Here we replaced all spaces with the + symbols. Also, notice that as the char_length() return value is an integer, you need to add the comments and close the quotes after that.

An excellent reference on useful SQL commands for SQL injection in the most common DBMS can be found on PentestMonkey's SQL injection cheat sheet at http://pentestmonkey.net/category/cheat-sheet/sql-injection.