Vulnerabilidad de Inyección SQL
Table of Contents
ToggleIntroducción
Para entender la vulnerabilidad de inyección SQL, primero debemos saber que una base de datos es un conjunto de datos organizados y estructurados según un determinado modelo de información (lógica de negocio) que refleja la relación entre ellos. Actualmente, y debido al desarrollo tecnológico de campos como la informática y la electrónica, la mayoría de las bases de datos están en formato digital, lo cual ofrece un amplio rango de soluciones al problema de almacenar información.
SQL significa Structured Query Language (lenguaje de consulta estructurada), el cual puede referirse como lenguaje de consulta o lenguaje de programación. El objetivo principal de SQL es interactuar con la base de datos relacional en la que se almacenan los datos de forma tabular (tabla formada por filas y columnas).
Hay muchos tipos de base de datos, entre ellas Mysql, Oracle, SQL server, etc.. En estos apartados estaremos viendo y haciendo ejemplos con mysql (cuyo servicio también es llamado Mariadb).
¿Qué es la vulnerabilidad de Inyección SQL?
La vulnerabilidad de inyección SQL representa un fallo, o mala configuración, en el filtrado de sentencias SQL a nivel de validación de entrada cuando realizamos operaciones hacia una base de datos. Por ejemplo, cuando queremos loguearnos en un sitio web o ver cualquier información, la cual nos pida cierta comprobación a nivel de usuario (id, user-name, password, mail, etc..).
Esta vulnerabilidad nos permite tener acceso a datos confidenciales, como pueden ser nombres de usuario, contraseñas, pagos y mucho más. Por tal motivo, este fallo es muy peligroso porque está en todas partes. Es decir, puede ocurrir en cualquier lenguaje de programación que esté dentro de otro.
A continuación, estaremos viendo los diferentes métodos de ataques llevados a cabo para poder protegernos. Como también, a otras personas o entidades de esta vulnerabilidad.
Entorno de trabajo
Para entender mejor la vulnerabilidad de inyección SQL, lo primero que haremos será crearnos una base de datos a modo de ejemplo. Para esto debemos activar el servicio de la siguiente forma systemctl start mysql. En caso de no tener instalado el servicio podemos instalarlo haciendo apt install mariadb-client. Ahora bien, nos conectaremos a través del comando mysql –u root y colocamos lo siguientes comandos:
Como vemos, hemos creado la base de datos “empresa”. Luego listamos las existentes en el sistema.
Ahora bien, las bases de datos están formadas por tablas (como las de Excel por ejemplo), por lo que procederemos a crearlas y a insertarle los campos o columnas. Por lo tanto, usaremos la recién creada colocando use database empresa; y luego lo siguiente:
Ahora bien, ya hemos generado nuestra tabla “empleados” cuyas columnas son “id,username,password y contact” con sus valores pertinentes. Recordemos que int hace referencia a un carácter de tipo númerico, el cual posee las siguientes especificaciones:
auto_increment Como su nombre lo indica se refiere a un autoincremento numérico con respecto al valor id. Es decir, a medida que se agregue un empleado (o un username), se le agregará un número único identificarlo.
PRIMARY KEY Hace referencia a un dato único e irrepetible en una tabla. Por lo tanto, cada valor que tome el campo id será único e irrepetible en dicha tabla. Por ejemplo, esto suele ponerse en el campo “documento” o “número telefónico” de una tabla, ya que dichos datos no deberían ser igual a los de otros usuarios.
Y el valor varchar, se refiere a la cantidad máxima de caracteres ocupados por dicho dato.
Por último, vamos a insertar datos en las columnas de dicha tabla de la siguiente manera:
Cabe mencionar que se les llama registros o filas a los datos pertenecientes a cada campo. Por ejemplo, la primera fila está compuesta por 1, admin, admin1234, admin@kali ; la segunda fila está compuesta por 2, Mike, Mike1234, Mike@kali y así sucesivamente.
Ahora bien, en base a las siguientes consultas podemos pedirle a dicha tabla los datos que queremos saber:
Para especificar columnas en especial como se muestra en la anterior imagen:
SELECT columna1,2..X FROM TABLA;
El * hace referencia a todas las columnas
Para especificar los datos de una columna respecto a otra:
SELECT columnaX FROM TABLA WHERE columnaY = ‘valor’ ;
Podemos observar que a los valores int (numéricos) no hace falta colocarle comillas (‘’), a diferencia de los string (caracteres alfanuméricos) es necesario que estén entre comillas (simples o dobles). Otra cosa a tener en cuenta, el ‘;’ solo es válido cuando lo colocamos en motores de base de datos o en las practicas que hemos visto anteriormente en nuestra Shell. En un sitio web NO los utilizamos.
Teniendo esto en cuenta, cuando entramos a un sitio web suele haber dos campos para ingresar: usuario y contraseña. O también, id o algún nickname. Por lo tanto, por “detrás” del sitio web la configuración puede ser la siguiente:
Por defecto, siempre habrá dos comillas para colocar el valor que nos piden, ya sea un string en el caso de username, o un int, en el caso de un id.
¿Cómo omitir el inicio de sesión?
Cuando queremos ingresar a un sitio web muchas veces nos encontramos con accesos que nos piden nombre de usuario y contraseña como el siguiente:
Aquí, la vulnerabilidad de inyección SQL radica de la siguiente forma:
Si el sitio se encuentra mal configurado, podríamos loguearnos sin colocar la contraseña, es decir, solo con el nombre de usuario. ¿Cómo funciona esto? Bueno, primero podemos intuir que la query por detrás es como la siguiente:
SELECT Username, Password FROM TABLE WHERE Username = ‘ ’ AND Password = ‘ ’ ;
Por lo tanto, si modificamos dicha query comentando el apartado de «Password» podríamos anularla. Entonces, tomando un usuario “admin” (administrador) como ejemplo, haremos lo siguiente:
SELECT Username, Password FROM TABLE WHERE Username = ‘ admin’ — –’ AND Password = ‘ ’ ;
Lo que sucede aquí es que dentro del valor “Username” estamos colocando admin’– – (en color rojo) y, con los guiones (– -) estamos comentando el resto de la query, es este caso, ‘ AND Password = ‘ ’;. Por lo tanto, estamos anulando el campo “Password” y dejando solamente la opción de “Username” para loguearnos.
Esto se vería de la siguiente forma:
A veces puede suceder que nos pida poner algo en el campo de Password. Sin embargo, poniendo cualquier caracter podemos llegar a loguearnos.
Cláusula WHERE
La cláusula WHERE nos permite la recuperación de datos ocultos. Para entenderlo mejor, veamos el siguiente ejemplo en la máquina DVWA en nivel low:
Nos pide que coloquemos un id por lo tanto, podemos pensar que dicho dato pertenece a un usuario en especial. Veamos dicho script:
Como vemos, el id hace referencia a first_name y a last_name por lo tanto cada número pertenece a uno de estos. Sin embargo, lo primero que debemos pensar es si el sitio es vulnerable a SQL Injection. Esto lo hacemos colocando una comilla (‘) y, en caso de que nos devuelva un error de sintaxis o la pantalla en blanco, podríamos ir probando distintos métodos.
Como vemos, solo hemos colocado una comilla y el sistema no lo reconoce como tal. Veamos un ejemplo en nuestra base de datos creada anteriormente. La query cuyo valor debemos colocar se vería de la siguiente manera:
Ahora bien, coloquemos una comilla dentro del valor y veamos que pasa:
Como vemos nos da un mensaje de error de sintaxis. Por lo tanto, esto es buena información para nosotros ya que, podemos suponer que dicho sitio web es vulnerable a sql injection. Esto sucede porque, en caso de estar bien configurado, no nos debería aparecer un mensaje de error por parte de la base de datos. Sino que el mismo debería ser del sitio web. Por ejemplo, un mensaje cuya frase sea “Los datos ingresados no son correctos”.
Filtrando datos
Ahora debemos saber cuántas columnas o campos posee la tabla en la que ingresamos datos. Veamos el siguiente ejemplo:
SELECT username,contact FROM table_name WHERE id =’1’ or 1=1 — – ’ ;
Observemos que, en caso de agregar los datos de color rojo, lo que estamos haciendo son dos cosas:
1) Con 1=1 hacemos referencia a un tipo de dato BOOLEAN (también llamado valor lógico), cuyos valores pueden ser “false” o “true”. Por lo tanto, le estamos diciendo al sistema que nos devuelva false o true, es decir, 1 o 1=1 (claramente 1=1 es true). Por lo que el sistema interpreta que el valor true se cumple para cada columna (en este caso usernmane y contact) y nos devuelve todos los valores pertenecientes a las mismas.
Para dejar en claro lo explicado anteriormente, veamos otros ejemplos:
Por lo tanto:
(1=1) OR (1=0) es igual a True OR False.
True or False es igual a True
Y en el caso de X AND True sería igual a X
2) El doble guion se utiliza para comentar el resto de la consulta o query, ya que las inyecciones que vamos a realizar se van a utilizar en un sitio web es importante eliminar el contenido que no nos sirve. Esto, como vimos, lo podemos hacer colocando — – o también, #. En programación los comentarios se utilizan para ir explicando las funciones que se van desarrollando o dar alguna indicación.
Ahora bien, sabiendo todo esto, procedamos a realizar nuestra inyección en dicho sitio web:
Como podemos ver nos devuelve cada fila de los campos first name y Surname de la tabla.
Cláusula ORDER BY
Ahora bien, para saber la cantidad de columnas ocultas deberíamos usar la cláusula ORDER BY. El mismo, nos permite ordenar los resultados de las consultas por columnas de la siguiente forma:
- En orden ascendente (por defecto) o descendente.
- Por una columna o un conjunto de ellas.
El siguiente ejemplo ordena el resultado por el atributo surname, por defecto ascendente:
SELECT name,surname FROM clients ORDER BY surname ;
El siguiente ejemplo ordena el resultado por el atributo surname, por defecto ascendente y name descendente:
SELECT name,surname FROM clients ORDER BY surname ASC, name DESC ;
Teniendo esto en cuenta, recordemos que los mensajes de error nos dan información. Por lo tanto, vamos a usar una query o consulta de forma tal que nos permita saber la cantidad de columnas que posee una tabla de la siguiente manera:
SELECT username,contact FROM table_name WHERE id = ’’order by X — –’ ;
Donde X = número
Por lo tanto, iremos probando uno por uno hasta que nos dé un mensaje de “columna X inexistente”, de esta forma sabremos la cantidad de columnas de dicha tabla.
Observemos el siguiente ejemplo en nuestra shell: (Recordemos que no hace falta comentar con — – en la Shell)
¡Exacto! Dicho mensaje de error nos da la información de que la tabla ‘empleados’ solo posee 4 columnas.
En nuestra máquina de DVWA, como vimos en el script anteriormente, la query es la siguiente:
SELECT first_name, last_name FROM users WHERE users_id = ‘$id’ ;
Por lo tanto, si queremos hacer uso de ORDER BY, siempre vamos a tener como resultado dos columnas como máximo: first_name y last_name
Veamos esto en la práctica:
Podemos ver que empezamos desde 1 hasta 3, y llegado a este punto nos devuelve la página vacía (prácticamente un mensaje de error). Por lo tanto, el uso de ORDER BY nos servirá como una guía más que como una solución.
¿Qué se entiende por Inyección SQL a ciegas?
Entendemos Inyección SQL a ciegas (o BLIND-SQL) a todo ataque a base de datos cuyo resultado no se muestra en pantalla o se representa de una forma muy genérica, de modo tal, que debamos incluir otro servidor o intermediario para poder visualizar dicho resultado. Cabe aclarar que las consultas utilizadas también suelen ser diferentes a las normales de SQLI.
La herramienta BurpSuit será fundamental para estos casos. En los apartados de BLIND-SQL veremos distintos métodos y aplicaciones para lograr extraer información.