Chasing another SQL Injection

Firstly, what is SQL injection?

SQL injection occurs where user controlled data is converted into an SQL statement and is executed by the web application’s database server.

What is the threat?

A successful SQL injection attack can lead to the insertion, modification or deletion of database data. SQL injection based attacks can also aid in bypassing authentication and authorisation mechanisms and in certain circumstances result in the ability to review the contents of the entire database server’s databases or gain access to the underlying operating system.

Note – This is solely dependent on the permissions that the SQL statement is executed with. If the execution permissions are limited to read access ONLY, then the impact of such attack would also be limited.

What is Blind SQL injection?

In typical SQL injection, error messages are used to help security testers discover and in some cases retrieve data from a database. In contrast, Blind SQL injection does not rely on error messages and instead must rely on the behaviour of the application, often through subtle control over application time-based responses in order to retrieve data. In essence, Blind SQL injection is the same as Standard SQL injection, except that more thought has to go into identifying a vulnerable instance and understanding the realm of control over the database server.

In Practice

During a recent web application security engagement, 7 Elements found a partial Blind SQL injection vulnerability. We say ‘partial’ as the application returned an error indicating that the SQL statement was invalid and that we were able to interact with the construction of the actual SQL statement executed by the back end database server. However, due to complex parsing and validation routines (both SQL and JSON), queries had to follow a specific set of rules:

  1. The SQL statement had to be valid and could not be terminated or commented.
  2. Bypass JSON input validation routines.
  3. The response had to result in an integer only otherwise parsing the results failed.

This made things much more difficult to exploit, as returning data was going to be difficult. Therefore, the partial Blind references the Blind techniques used to extract data from the database. We would like to point out that automated vulnerability scanning indicated that the server was vulnerable to SQL injection but it also stated it was vulnerable to OS Command Execution, XML Injection and Ruby code execution… it was, sadly, only vulnerable to SQL Injection.

The environment under test was further restrictive as interactions with the application resulted in sporadic response times, meaning that time based responses were not a viable way to infer any true or false statements. Luckily the SQL injection vulnerability was the result of a query that did return data, if data was in the database to be returned (By this we mean an integer of the number of times in the table the data was found).

It was possible to confirm that SQL injection was possible by first utilising two main statements, one being true and the other being false. Therefore by using the following methods we could confirm that we are able to interact with the SQL query using the following syntax:

True Statement

(Username = '7Elements AND 1=1’)

False Statement

(Username = '7Elements AND 1=2’)

The True statement returns data while the False statement returned no data. The response below details two instances of the 7Elements user in the table.

Request:

POST /testapp HTTP/1.1Host: 7elements.testing.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-GB,en;q=0.5
--CUT--

{"list":"_view","sort":"(Username = ‘7Elements’)"}

Response:

{"d":"{\"page\":1,\"total\":1,\"records\":2}”}

When exploiting this particular type of vulnerability there are a few methods to return data. For the most part it results in a large number of requests being sent.

Automation? Sounds good to me

Most security testers will be familiar with SQLMAP, a fantastic tool that automates the exploitation of SQL vulnerabilities. Unfortunately, in this case it was not possible to detect the SQL vulnerability. Even after some fine-tuning, it was still not possible to detect the SQL injection vulnerability. On review of the response to each query, it was obvious that this was due to the parsing rules used within the application. Another few attempts were made to force the correct syntax but unfortunately this still did not work.

We then moved to another common tool, SQLNinja. This time after tweaking the configuration, SQLNinja identified the injection point.

SQLNinja could then be used to identify the database user. It stated the ‘sa’ account as running queries. For anyone who knows databases this is a *high five moment*. SQLNinja then detailed the version as MS-SQL 2005. Thinking we were onto a winner, we tracked the HTTP requests and responses only to see that the tool was using time based responses. The tool sent one query, when the application did not respond in a timely manner, it “inferred” this to be the correct answer. This didn’t look good… some manual verification is definitely required.

Manual Verification

Useful statements to start with when enumerating data in using blind techniques is to understand the length of a parameter that you want to enumerate. This helps understand how many quires should be made to fully enumerate the contents.

To calculate the length of a variable such as @@version we can use the following syntax:

Username = '7Elements' AND (select len(@@version)) > 24)

If this returns True, we can continue to up the number until its not true and then replace the value with a ‘=’ to validate the length.

For anyone that has done this type of injection on MS-SQL should realise that characters 21-24 are the version number. Rather than brute forcing the whole of the @@version variable we can send a minimal number of requests to validate the SQLNinja findings. The following example code details how each character of a variable, table, column or database can be returned. Red highlights the position of the character in the variable, while yellow indicates the value being assessed.

Payload:

(Username = '7Elements' AND (substring((select @@version),24,1) = '5'))

Unfortunately, this was equal to a false statement, showing that the version is not 2005 as SQLNinja had earlier identified.

Now we can work through common version numbers: 2000, 2005, 2008, 2012, 2014 and 2016. It turns out after further probing the 24th character is a 4. By decrementing the red marker, we can confirm the actual version to be 2014.

This is becoming pretty cool pretty quickly, but it requires a large amount of requests. So some quick thinking… We can now evaluate the length of variables and then systematically work our way through each character to identify its true value.

Possible characters in use, a-zA-Z0-9 and then some punctuation (-,._:;) Again these settings must be chosen based on the parsing rules as discussed earlier.

Some basic math says that the total number of options are (26 + 26 + 10 + 6) = 68. The math for working out the keyspace in this instance is equal to [the total number of possible options] times [the length]. This is because we can query each character in isolation, unlike brute forcing a password – [total number of options] to the power [the length].

It seems to me that without even completing the math we need to get back to some automation.

Burp is a very flexible proxy application with a number of additional security testing modules. The module we will use to automate this is Intruder.

Intruder has a number of options to help with the use of multiple payloads. In this scenario we can make use of Cluster bomb.

Cluster bomb – This uses multiple payload sets. There is a different payload set for each defined position (up to a maximum of 20). The attack iterates through each payload set in turn, so that all permutations of payload combinations are tested. I.e., if there are two payload positions, the attack will place the first payload from payload set 2 into position 2, and iterate through all the payloads in payload set 1 in position 1; it will then place the second payload from payload set 2 into position 2, and iterate through all the payloads in payload set 1 in position 1.”

Using Burp we will complete some basic enumeration of MS-SQL built in variables: BD_name(), @@version and user_name(). The first thing we want to do is to work out the length of each variable as described earlier. This can be automated but its easier to do manually.

Once we have the length we can set up the Cluster bomb payloads. To do this we will set:

  • Payload 1 (Red Marker) – with the payload set to numbers configured as 1-22 (length of string).
  • Payload 2 (Yellow Marker) – with the payload set of a-Z0-9 and any punctuation selected.

The actual payload line will look like this:

(Username = '7Elements' AND (substring((select @@DB_name()),§Payload-1§,1) = '§Payload-2§'))

As the response contains different content it is possible to filter the response by an expression, that matches a true statement, leaving only good information as per the following screenshot:

Partially Blind SQL

Partially Blind SQL

Using Burp it was very easy and fast to enumerate the three variable below:

DB_name() = sevenelements_dnn_only
@@version = microsoft sqlserver 2014
user_name() = sevenelements_test_user

In Conclusion

It goes without saying that this is just the starting point, but we think it’s a great place to leave this example for now. In a time when SQL injection has again hit the headlines, we can see that automated tools do not exceed the assurance provided by a proper in-depth security engagement. The skills of penetration testers with the capability to validate and manually exploit vulnerable configurations vastly outweighs the cheap and cheerful approach of automated vulnerability scanning.