6 minuto(s) de lectura

Después del post de las LFI creo que la siguiente vulnerabilidad básica que hay que conocer en el mundo del pentesting web es la inyeccion de SQL (SQL Injection para los angloparlantes), esta es una vulnerabilidad web que permite a un atacante modificar las consultas que la aplicación web realiza a la base de datos para obtener información no autorizada o saltarse ciertos controles de acceso. En algunos casos este tipo de vulnerabilidades puede comprometer la infraestructura y el funcionamiento de este servicio web.

A la hora de probar en pentesting los cheatsheets pueden sernos de gran ayuda por el distintio tipo de inyecciones que existen aunque en este artículo solo se tocarán ejemplos básicos además de posibles defensas y consecuencias.

Dónde se encuentran

Se pueden buscar usando cierta semántica especial en las distintas entradas de datos que intuyamos que hacen consultas a una base de datos, como podría ser un inicio de sesión o alguna consulta de que impliqué datos guardados en masa como un stock de artículos. Esta se puede comprobar generalmente añadiendo:

  • Introduciendo ' esperando algún error o salida anómala.
  • Introduciendo alguna condición lógica como OR 1=1 y mirando la salida de la aplicación.
  • Probando payloads específicamente diseñados para este tipo de ataques.

Inyección tipo WHERE clause

Existen múltiples tipos de inyecciones, en este artículo se expondrá la más simple, la basada en una cláusula WHERE. Estas suelen tener la siguiente forma:

"SELECT * FROM table WHERE column1='" + parametro1 + " AND column2='" + parametro2 + "'..."

En esta se podría omitir el número de comprobaciones que queramos ya que podemos añadir distintos tipos de comentarios (en línea o en bloque) evitando las comprobaciones que no nos convengan como atacantes y generando una consulta con nuevas condiciones que validasen cualquier campo con la exporesión ' OR 1=1 -- - que es la más típica usada en estos casos.

Ejemplo vulnerable básico inicio de sesión

Si nos imaginamos como con una consulta SQL de la manera más básica podría autenticar un usuario, podríamos imaginarnoslo de esta forma:

SELECT *
  FROM users
 WHERE email = '$mail'
   AND password  = '$pass' LIMIT 1

Esto funcionaría principalmente que busca en la tabla de usuarios un usuario donde coincidan un mail con su contraseña pero si en este caso existe un usuario mal intencionado y en los campos de inicio de sesión introdujese lo siguiente:

  • $mail: admin@mail.com
  • $pass: ‘ or 1=1–

Este ejemplo de entrada nos dejaría la comprobación de la contraseña como pass = '' OR 1=1 --' LIMIT 1, esto haría que nos registraramos con ese usuario admin ya que la condición de 1=1 es correcta por lo que <condición> or <condición> da como verdadera la parte de la comprobación de la contraseña y el resto se ignora porque se queda como comentario, por lo que la consulta finalmente está formada correctamente.

Riesgos

  • Acceso no autorizado a datos sensibles: Un atacante puede obtener acceso a datos confidenciales almacenados en la base de datos, como contraseñas, información personal, o datos financieros.

  • Modificación de datos: Los atacantes pueden modificar, eliminar o insertar datos maliciosos en la base de datos, lo que puede llevar a la corrupción de datos o cambios no autorizados en la información.

  • Ataques de fuerza bruta: Los atacantes pueden utilizar la inyección SQL para obtener información que les ayude a llevar a cabo ataques de fuerza bruta en otros sistemas, como contraseñas de usuario o credenciales.

  • Ejecución de comandos del sistema: Una vulnerabilidad SQLi puede permitir a los atacantes ejecutar comandos del sistema en el servidor de la aplicación, lo que puede llevar a la toma de control completa del sistema.

  • Ataques de denegación de servicio (DoS): Los atacantes pueden aprovechar las vulnerabilidades SQLi para realizar ataques de denegación de servicio, abrumando la base de datos y la aplicación con solicitudes maliciosas.

  • Divulgación de información interna: Los atacantes pueden utilizar la inyección SQL para obtener información interna de la aplicación, como la estructura de la base de datos o detalles técnicos, que podrían facilitar futuros ataques.

  • Riesgo de infección por malware: Los atacantes pueden inyectar código malicioso en la base de datos a través de SQLi, lo que puede propagar malware a través de la aplicación o a los usuarios finales.

  • Vulnerabilidades persistentes: Si una vulnerabilidad SQLi no se corrige, los riesgos asociados pueden persistir durante un largo período, permitiendo que los atacantes continúen explotando la vulnerabilidad.

Protecciones

Usando sentencias preparadas

Como la mayoría de las veces las sentencias SQL son escritas en otro lenguaje de programación y se conectan a la BD usando un driver. Una buena práctica es introducir dichos parámetros en la sentencia con métodos que nos proporcione el driver, así el driver le pasará estos parámetros a la BD por separado.

Ejemplo en Java

Seguro:

// Connect to the database.
Connection conn = DriverManager.getConnection(URL, USER, PASS);

// Construct the SQL statement we want to run, specifying the parameter.
String sql = "SELECT * FROM users WHERE email = ?";

// Generate a prepared statement with the placeholder parameter.
PreparedStatement stmt = conn.prepareStatement(sql);

// Bind email value into the statement at parameter index 1.
stmt.setString(1, email);

// Run the query...
ResultSet results = stmt.executeQuery(sql);

while (results.next())
{
    // ...do something with the data returned.
}

Inseguro


// The user we want to find.
String email = "user@email.com";

// Connect to the database.
Connection conn = DriverManager.getConnection(URL, USER, PASS);
Statement stmt = conn.createStatement();

// Bad, bad news! Don't construct the query with string concatenation.
String sql = "SELECT * FROM users WHERE email = '" + email + "'";

// I have a bad feeling about this...
ResultSet results = stmt.executeQuery(sql);

while (results.next()) {
  // ...oh look, we got hacked.
}

La diferencia es que el primero le pasa el parámetro con el método setString por lo que es pasado a la BD aparte y el segundo podría introducirse la sentencia SQL que se quisiera.

Saltarse caracteres

Esta es menos eficaz que la anterior. Trata de saltar caracteres como ' o ", ya que son los típicos para indicar el fin del un string. La mayoría de lenguajes tienen funciones estándar para hacer esto. Aun así esto tiene un par de incovenientes:

  • Hay que tener mucho ciudado para saltar estos caracteres en cualquier sentencia SQL de la BD.
  • No todas las inyecciones abusan de esa vulnerabilidad. Por ejemplo si espera un tipo de dato entero, las comas ya no sirven para protegernos.

Comprobando los inputs

Comprobar si el correo cuadra con nuestra expresión regular de lo que debería ser un correo, si un dato que es un número no contiene carácteres no numéricos o rechazar espacios o intros son buenas prácticas a la hora de comprobar el input que nos está dando el usuario. No nos podemos confiar con que el usuario es bueno perse, hay que comprobar que los datos que nos está pasando no son maliciosos.

Hacer esto en el lado del cliente (JavaScript) suele estar bien para darle al cliente un feedback inmediato, pero no es una buena defensa contra atacantes buenos ya que puede que no estén usando ni navegadores para hacer las peticiones, solo usando scripts y se puedan saltar esa verificación de los inputs.

Referencias

  • https://www.hacksplaining.com/exercises/sql-injection

  • https://www.youtube.com/watch?v=C-FiImhUviM

Cheatsheets

  • https://www.invicti.com/blog/web-security/sql-injection-cheat-sheet/

  • https://www.w3schools.com/sql/sql_union.asp

  • https://pentestmonkey.net/category/cheat-sheet/sql-injection

  • https://portswigger.net/web-security/sql-injection/cheat-sheet

Categorías:

Actualizado: