Inyecciones SQL basada en Uniones
Última actualización
Última actualización
¡Hola Hacker! Bienvenid@ a un nuevo artículo.
El primero tipo de inyecciones SQL que veremos en esta serie de artículos, son las llamadas inyecciones basadas en uniones.
Un ataque de inyecciones sql basadas en uniones es un tipo de vulnerabilidad en la que un atacante explota una debilidad en una aplicación para inyectar una consulta SQL maliciosa que utiliza la cláusula UNION
. El objetivo de este ataque es combinar los resultados de la consulta original de la aplicación con los resultados de una consulta adicional, controlada por el atacante, lo que le permite acceder a información no autorizada.
En otras palabras, este tipo de ataque permite al atacante extraer datos de una base de datos a la que normalmente no tendría acceso, aprovechando la estructura de la base de datos para fusionar los resultados de su propia consulta con los datos que la aplicación originalmente devuelve.
Para realizar la prácita de esta inyección utilizaremos nuestro Laboratorio.
Antes de comenzar, deberemos crear el siguiente script php en la carpeta app, el cual nos ayudara a realizar la demostración.
Una vez creado el script, si ingreamos a nuestra aplicación y le pasamos 1 como valor del parámetro id
, vemos que obtenemos el siguiente resultado:
Efectivamente, el usuario con el id
1 es el Admin.
Si le pasamos como valor del id 4, vemos que no que no existe un usuario con este id, lo cual es correcto.
En primer lugar, para comprobar si es estamos frente a un código vulnerable a inyecciones SQL, podemos realizar la siguiente petición:
Agregamos una comilla al final de nuestro parámetro id
, también podríamos obviar el valor de nuestro id
y enviar simplemente id='
.
Vemos que el script nos devuleve un warning el cual indica que la función mysqli_num_rows
esperaba recibir un parámetro mysqli_result
y recibio un booleano. Lo cual indica que la consulta ha fallado.
Linea 18 donde se produce el error.
Al ver que es vulnerable, seguimos explotando la vulnerabilidad para obtener más información, en este caso para conocer la cantidad de campos que se estan listando.
Vemos que no devuelve ningun error.
La consulta SQL quedaría de la siguiente forma:
Los "--" (guiones) o "#" se utilizan en MySQL para realizar comentarios, es decir, que todo lo que venga luego de estos no será ejecutado.
La clausula ORDER BY, permite realizar un ordenamiento de los resultados devueltos por la columna. Recibe dos parámetros, el número de columna por el cual aplicar el ordenamiento y el orden ASC (Ascendente por defecto) y DESC (Descendente).
Pero si ingresamos un valor de 2, vemos que devuelve nuevamente un error.
Lo cual es correcto, ya que nuestra consulta devuelve un solo campo (username
).
La consulta SQL quedaría de la siguiente forma:
Antes de seguir adelante, expliquemos brevemente en que consiste la clausula UNION SELECT
.
La palabra clave UNION
en SQL se utiliza para combinar los resultados de varias consultas SELECT
. Cuando usas UNION
, los resultados de una consulta adicional se agregan al conjunto de resultados de la consulta principal, formando un único conjunto de datos.
Por ejemplo, si tienes la siguiente consulta:
Esto devolverá un único conjunto de resultados con dos columnas: una que contiene los valores de column_a
y column_b
de table_1
, y otra con los valores de column_c
y column_d
de table_2
.
Para que una consulta UNION
funcione correctamente, hay dos reglas clave que deben cumplirse:
El número de columnas debe ser el mismo en ambas consultas.
Los tipos de datos de las columnas correspondientes deben ser compatibles entre sí.
En un ataque de inyección SQL utilizando UNION
, es importante asegurarse de que estas dos reglas se cumplan. Esto generalmente requiere que el atacante descubra:
Cuántas columnas devuelve la consulta original.
Qué tipo de datos tienen las columnas de la consulta original, para poder inyectar datos que se ajusten a esas columnas.
Ahora que tenemos claro en que consisite la clausula UNION SELECT
sigamos con nuestro laboratorio.
Hasta el momento, logramos determinar que el número de columnas devueltas por la consulta es uno y el tipo de dato que tiene la columna es una cadena (string).
Una vez conocemos la cantidad de campos y el tipo de dato, podemos jugar con la clausula UNION SELECT
.
Podemos obtener el nombre de la base de datos actualmente en uso usando lo siguiente:
Indicamos un valor de id
igual a 4 (un id de usuario que no existe) y luego la clausula UNION SELECT
indicando como valor a listar el resultado de la función DATABASE()
el cual devuelve el nombre de la base de datos actualmente en uso.
La consulta SQL queda de la siguiente manera:
Para poder conocer todas las base de datos existentes, podemos utilizar la base de datos information_schema
y la tabla schemata
.
information_schema Base de datos que almacena información de la esctructura de todas las base de datos
schemata Tabla que almacena las base de datos existentes
schema_name Campo que almacena el nombre de la base de datos
Usamos el comando LIMIT offset, length
para poder listar las base de datos ya que en este caso se muestre un registro unicamente.
La consulta generada es la siguiente:
Otra alternativa es usar la función GROUP_CONCAT
La cláusula
GROUP_CONCAT
de MySQL se usa para concatenar valores de varias filas en un solo valor, dentro de un grupo definido por la cláusulaGROUP BY
. Es útil cuando quieres combinar valores relacionados en una sola cadena.
Listamos las tablas de la base de datos app_db
.
Vemos que nos devuelve el nombre de la tabla.
Listamos las columnas de la tabla users
de la base de datos app_db
.
Una vez conocemos el nombre de los campos, podemos obtener los valores:
La consulta a ejecutar sería la siguiente:
0x3A es ":" en hexadecimal
La consulta a ejecutar sería la siguiente:
De esta forma, logramos obtener los usuarios.
Las inyecciones SQL basadas en uniones son una amenaza común debido a su efectividad en aplicaciones web mal configuradas. Para prevenir este tipo de ataques, es esencial implementar medidas como el uso de consultas parametrizadas (prepared statements), la validación estricta de las entradas de los usuarios y el principio de privilegios mínimos en las bases de datos. Además, es crucial realizar auditorías y pruebas regulares de seguridad, como pruebas de penetración, para identificar y mitigar posibles vulnerabilidades antes de que sean explotadas. La protección activa y constante es la clave para mantener los sistemas seguros frente a este tipo de ataques.
Espero que estés disfrutando de esta serie de artículos y que la información te sea útil. Si te ha gustado, te animo a compartirla con otros para que más personas puedan beneficiarse.
Si encuentras algún error o tienes sugerencias para mejorar, no dudes en hacérmelo saber. Estoy siempre abierto a tus comentarios.
¡Gracias por leer!
¡Happy Hacking!