DATABASE

Blind SQL Injection Exploitation : Using Response-Based Techniques

10/13/2010 9:43:48 AM
Just as we used request timing to infer information about a particular byte, we can also infer state by carefully examining all data in the response, including content and headers. You can infer state either by the text contained in the response or by forcing errors when particular values are under examination. For example, the inference exploit could contain logic that alters the query such that query results are returned when the examined bit is 1 and no results if the bit is 0, or again, an error could be forced if a bit is 1 and no error generated when the bit is 0.

Although we will delve into error-generating techniques shortly, it is worth mentioning here that the types of errors we strive to generate are runtime errors rather than query compilation errors. If the syntax in the query is wrong, it will always produce an error, regardless of the inference question; the error should be generated only when the inference question is either TRUE or FALSE, but never both.

Most blind SQL injection tools use response-based techniques for inferring information, as the results are not influenced by uncontrolled variables such as load and line congestion; however, this approach does rely on the injection point returning some modifiable response to the attacker. You can use either the binary search approach or the bit-by-bit approach when inferring information by poring over the response.

MySQL Response Techniques

Consider the case where the following SQL query is executed through a Web application with input data MadBob, and returns one row from the reviews table that is contained in the page response. The query is:

SELECT COUNT(*) FROM reviews WHERE review_author='MadBob'

The result of execution is a single row containing the number of reviews written by MadBob, and this is displayed on the Web page in Figure 1.

Figure 1. Query for “MadBob” Returning a Count of Two Reviews, Used As TRUE Inference


By inserting a second predicate into the WHERE clause, it is possible to alter whether the query returns any results. You can then infer one bit of information by asking whether the query returned a row, with the following statement:

SELECT COUNT(*) FROM reviews WHERE review_author='MadBob' AND
ASCII(SUBSTRING(user(),i,1))>k#

If no results are returned, you can infer that bit k of byte i is 0; otherwise, the bit is 1. This is visible in Figure 2, where a search with the string MadBob’ and if(ASCII(SUBSTRING(user( ), 1,1))>127,1,0)# produced a 0 review count. This is a FALSE state and so the 1 character has an ASCII value less than 127.

Figure 2. Query That Returns a Count of 0 Reviews and Is a FALSE Inference


Where numeric parameters are used, it is possible to split and balance input. If the original query is:

SELECT COUNT(*) FROM reviews WHERE id=1

a split and balanced injection string that implements the bit-by-bit approach is:

SELECT COUNT(*) FROM reviews WHERE id=1+
if(ASCII(SUBSTRING(CURRENT_USER(),i,1))&2j=2j,1,0)

Where it is not possible to alter content, an alternative method of inferring state is to force database errors when a 1-bit is seen and no errors when a 0-bit is seen. Using MySQL subqueries in combination with a conditional statement, you can selectively generate an error with this SQL query that implements the bit-by-bit inference method:

SELECT COUNT(*) FROM reviews WHERE
id=IF(ASCII(SUBSTRING(CURRENT_USER(),i,1))&2j=2j,(SELECT table_name
FROM information_schema.columns WHERE table_name = (SELECT table_name
FROM information_schema.columns)),1);

This is fairly dense, so it helps to break the query into pieces. The IF( ) statement handles the conditional branching, and the condition we are testing is one we have seen regularly throughout this chapter: ASCII(SUBSTRING(CURRENT_USER( ),i,1))&2j=2j, which implements the bit-by-bit inference method. If the condition is true (i.e., bit j is a 1-bit), the query SELECT table_name FROM information_schema.columns WHERE table_name = (SELECT table_name FROM information_schema.columns) is run, and this query has a subquery that returns multiple rows in a comparison. Because this is forbidden, execution halts with an error. On the other hand, if bit j was a 0-bit, the IF( ) statement returns the value 1. The true branch on the IF( ) statement uses the built-in information_schema.columns table, as this exists in all MySQL Version 5.0 and later databases.

I should point out that when using an application written in PHP with MySQL as the data store, errors arising from the execution of database queries do not generate exceptions that cause generic error pages. The calling page must check whether mysql_query( ) returns FALSE, or whether mysql_error( ) returns a non-empty string; if either condition exists, the page prints an application-specific error message. The result of this is that MySQL errors do not produce HTTP 500 response codes, but rather the regular 200 response code.

SQL Server Response Techniques

Consider the following T-SQL that can infer one bit of information by asking whether a vulnerable query returned rows with the statement:

SELECT COUNT(*) FROM reviews WHERE review_author='MadBob' and
SYSTEM_USER='sa'

If the query returned results the login in use was sa, and if no rows came back the login was something else. You can integrate this quite easily with the binary search and bit-by-bit inference methods to extract the actual login:

SELECT COUNT(*) FROM reviews WHERE review_author='MadBob' AND
ASCII(SUBSTRING(SYSTEM_USER,i,1))>k--

and

SELECT COUNT(*) FROM reviews WHERE review_author='MadBob' AND
ASCII(SUBSTRING(SYSTEM_USER,i,1))&2j=2j

The split and balance trick works nicely with response-based inference on SQL Server. Combined with a conditional subquery that uses CASE, you can include a string as part of the search depending on the state of a bit or value. Consider first a binary search example:

SELECT COUNT(*) FROM reviews WHERE review_author='Mad'+(SELECT CASE WHEN
ASCII(SUBSTRING(SYSTEM_USER,i,1))>k THEN 'Bob' END) + ''

Here is the matching bit-by-bit example:

SELECT COUNT(*) FROM reviews WHERE review_author='Mad'+(SELECT CASE WHEN
ASCII(SUBSTRING(SYSTEM_USER,i,1))&2j=2j THEN 'Bob' END) + ''

If either of these two queries returned results only seen for the search input ‘MadBob’, then in the binary search exploit the i-th byte had an ASCII value greater than k or in the bit-by-bit exploit the i-th byte had the j-th bit set to 1.

You could also force a database error in cases where the page does not return content but does trap database errors and displays either a default error page or an HTTP 500 page. One common example of this is ASP.NET Web sites running on Internet Information Server (IIS) 6 and 7 that do not have the <customError> tag set in the web.config configuration file, and where the vulnerable page does not trap exceptions. If a broken SQL query is submitted to the database, a page similar to that shown in Figure 3 is displayed, and digging deeper into the returned HTTP headers reveals that the HTTP status was 500 (Figure 4). The error page does not lend itself to the regular error-based extraction methods because database error messages are not included.

Figure 3. Default Exception Page in ASP.NET


Figure 4. Response Headers Showing 500 Status


Introducing errors can be tricky. The error cannot exist in the syntax because this would cause the query to always fail before execution; rather, you want the query to fail only when some condition exists. This is often accomplished with a divide-by-zero clause combined with a conditional CASE:

select * FROM reviews WHERE review_author='MadBob'+(CASE
WHENASCII(SUBSTRING(SYSTEM_USER,i,1))>k THEN CAST(1/0 AS CHAR) END)

The underlined division will be attempted only if the k-th bit of byte i is 1, allowing you to infer state.

Oracle Response Techniques

The Oracle response-based exploits are similar in structure to both MySQL and SQL Server, but obviously they rely on different functions for the key bits. For example, to determine whether the database user is a DBA, the following SQL query will return rows when this is true and no rows otherwise:

SELECT * FROM reviews WHERE review_author='MadBob' AND
SYS_CONTEXT('USERENV','ISDBA')='TRUE';

Likewise, you can write a bit-by-bit inference exploit that measures state based on whether results are returned with a second injected predicate:

SELECT * FROM reviews WHERE review_author='MadBob'
ANDBITAND(ASCII(SUBSTR((…),i,1)),2j)=2j

The binary search form is:

SELET * FROM reviews WHERE review_author='MadBob' and
ASCII(SUBSTR((…),i,1)) > k

Using Oracle's string concatenation, it is also possible to make the exploit safe to use in a function or procedure argument list by rewriting it as a split and balanced string with concatenation and a CASE statement:

Mad'||(SELECT CASE WHEN (ASCII(SUBSTR((…),i,1)) > k THEN 'Bob' ELSE '' END
FROM DUAL)||';

With the preceding snippet, the full ‘MadBob’ string is generated only when the inference test returns true.

Finally, we can also generate runtime errors with a divide-by-zero clause, similar to what we did with SQL Server. Here is a sample snippet that contains a zero divisor in a split and balanced bit-by-bit approach:

MadBob'||(SELECT CASE WHEN BITAND((ASCII(SUBSTR((…),i,1))2j)=2j THEN
CAST(1/0 AS CHAR) ELSE '' END FROM DUAL)||';

Observe how the division had to be wrapped in a CAST( ); otherwise, the query would always fail with a syntax error. When the inference question returned TRUE in a vulnerable page running on Apache Tomcat, an uncaught exception was thrown, resulting in the HTTP 500 server error shown in Figure 5.

Figure 5. Uncaught Oracle Exception Caused by a 0 Divisor


Returning More Than One Bit of Information

So far, each inference technique we've covered focused on deriving the status of a single bit or byte based on whether the inference question returned TRUE or FALSE, and the fact that only two states were possible permitted the extraction of exactly one bit of information per request. If more states are possible, more bits can be extracted per request, which would improve the channel bandwidth. The number of bits that can be extracted per request is log 2n, where n is the number of possible states a request could have. To quantify this with actual figures, each request would need four states to return two bits, eight states to return three bits, 16 states to return four bits, and so on. But how can more states be introduced into a request? In some cases, it is not possible to introduce more states just as blind SQL injection is not possible in all vulnerable injection points, but it often is possible to extract more than one bit. In cases where the inference question is answered with timing methods or content methods, it is possible to introduce more than two states.

Up until now, the bit-by-bit approach has asked whether bit j of byte i is 1. If four states are possible, the inference question could be a series of questions that ask whether the two bits starting at bit j of byte i are 00, 01, 10, or 11. Where timing is used as the inference method, this could be phrased as the following CASE statement:

CASE

WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1) = 0
THEN WAITFOR DELAY '00:00:00'
WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1) = 1
THEN WAITFOR DELAY '00:00:05'
WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1) = 2
THEN WAITFOR DELAY '00:00:10'
ELSE
THEN WAITFOR DELAY '00:00:15'
END

This does not seem particularly remarkable; in the worst case (where the bitstring is 11) this CASE statement yields a 15-second delay, which is longer than if these two bits were extracted one at a time with a five-second delay, but on uniformly distributed data the average delay is less than 10 seconds. This approach also requires fewer requests, so the total time spent on request submission and response transmission is lowered.

Another option to increase the number of states is to alter the search term in a WHERE clause so that, for instance, one of four possible results is displayed, allowing you to infer the bitstring:

SELECT * FROM reviews WHERE review_author='' + (SELECT
CASE

WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1)= 0
'MadBob'
WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1)= 1
'Hogarth'
WHEN ASCII(SUBSTRING((…),i,1))&(2j+2j=1) = 2
'Jag'
ELSE
'Eliot'
END)

When the search results match ‘MadBob’ the inference is ‘00’; when they match ‘Hogarth’ it's ‘01’, when they match ‘Jag’ it's ‘10’, and when they match ‘Eliot’ it's ‘11’.

The two CASE statements in the preceding code demonstrate how to improve the bit-by-bit approach. However, it is also possible to improve the binary search approach. One of the major drawbacks to the binary search is that only a single relation is tested—namely, “greater than.” Say the ASCII value of the byte under examination is 127. The first inference questions asks “Is 127 > 127?” The answer is FALSE and so seven further questions must be asked to refine the question, until you ask “Is 127 > 126?” after which the value is inferred. Instead, you would like to insert a second, shortcut question after the first inference question—“Is 127 = 127?”—but include both questions in a single request. You can do this through a CASE statement implementing a binary search method combined with an error-generating divide-by-zero clause:

CASE
WHEN ASCII(SUBSTRING((…),i,1)) > k
THEN WAITFOR DELAY '00:00:05'
WHEN ASCII(SUBSTRING((…),i,1)) = k
THEN 1/0
ELSE
THEN WAITFOR DELAY '00:00:10'
END

Thus, if an error is observed, i = k. If the request is delayed by five seconds, i is greater than k; otherwise, i is less than k.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8