Inyeccion mortal: SQL Injeciton
Probablemente el ataque más temido por todo dueño de una web sea el que les robe/destruya/modifique información de la misma. SQL Injection es uno de los ataques más graves que puede sufrir un site (o aplicación en general), debido a las implicaciones del mismo.
Un ataque llevado a cabo con paciencia puede permitir al invasor obtener lo que desee de una base de datos. Debido a que la mayoría de los sites actuales son dinámicos o tienen algún sistema de login, el rango de ataques es muy grande.

Si bien es un ataque muy difundido, es increíble la cantidad de sites vulnerables. Realmente se muestra que existe muy poco conocimiento en la comunidad de programadores, porque existen muchas soluciones para este problema.

Un uso que se le está dando últimamente al resultado de inyecciones es en las botnets. Es gracioso (y triste a la vez) como mediante una demostración de SQL Injection en una empresa, el analista descubrió que el site ya había sido atacado y que este formaba parte de una botnet... pueden leer el artículo aquí.

Quizás muchos de los lectores del blog conozcan al menos el concepto más básico de SQL Injection, pero debido a que últimamente me ha tocado tratar con varios programadores que no tenían conocimiento del tema, creo que es un buen momento para hacer una serie de informes sobre este ataque.

El problema con el SQL Injection es que es un ataque bastante elaborado, no es tan simple como realizar un XSS. Si bien el concepto básico se aplica a todas las bases de datos, cada DBMS aporta diferentes herramientas para obtener/manipular los datos. Así que, comencemos por el principio...


Erase una vez SQL Injection

SQL Injection es un ataque donde el perpetrador inyecta sentencias SQL en un formulario para poder acceder/manipular los datos en la base de datos. Dependiendo la gravedad de la vulnerabilidad, el atacante podrá ejecutar diferentes sentencias SQL, incluyendo insert, update, delete, select, etc.

La falla, como de costumbre, es una mala (o inexistente) validación de los datos ingresados por el usuario. Una vez que el usuario ingresa valores, el programa los toma y los envía tal cual los recibe al DBMS, abriendo la posibilidad para que el usuario acceda directamente a la base de datos.

Hay que aclarar que SQL Injection no es sólo un ataque web, también es posible realizarlo en aplicaciones ejecutables. Toda aplicación donde se acceda a una base de datos y no se valide la entrada será vulnerable a este ataque.


' or '1'='1

Para demostrar cómo funciona una inyección, nunca mejor ejemplo que el ultra clásico ' or '1'='1. Casi todos los libros/artículos utilizan este ejemplo para explicar el ataque, y como muestra bien el funcionamiento, haré uso del mismo una vez más.

Supongan que tenemos un sistema de autenticación con usuario y password. Si la autenticación se realiza contra datos almacenados en una base de datos, la consulta puede ser algo así:
$query = "SELECT * FROM users WHERE name='".$_POST['name']"' AND password='".$_POST['password']."'";

$result = consutar_DBMS($query);

if($result)
login();
else
reject();
Por si no conocen PHP, la variable $_POST nos devuelve variables ingresadas por el usuario usando el método POST (algo clásico en formularios web). Si la base de datos nos devuelve una fila, quiere decir que el usuario existe y lo podemos dejar entrar.
Una consulta sin inyección, donde el usuario es demasiadovivo y el password es 123456, podría ser:
SELECT * FROM users WHERE name='demasiadovivo' AND password='123456'
Ahora viene la parte buena. Supongan que en lugar de ingresar como password 123456, ingresamos ' or '1'='1. La cosulta quedaría:
SELECT * FROM users WHERE name='demasiadovivo' AND password='' or '1'='1'
Esta consulta siempre retorna un resultado (or 1=1 siempre es true), sin importar si el usuario existe o no. Como resultado, el usuario logra ingresar al site bypasseando el control de login... gracias al SQL Injection.


Ataques?

Como se imaginarán, si tenemos acceso a una variable no validada, podremos ejecutar en la base de datos todo lo que el DBMS nos permita.
Para el ejemplo anterior, podríamos tal vez inyectar cosas como:
- '; drop table users -- borramos la tabla users
- '; insert into users values('hacker', 'mipass'); -- insertamos el usuario hacker con password mipass
- '; insert into contenido values('te hackie la página'); -- insertamos el contenido "te hackie la página" (site defacement)
Si accedemos a una variable utilizada para mostrar contenido, como suele ser el caso de páginas dinámicas, podemos robar información. Por ejemplo, supongamos que tenemos un código como el siguiente:
$query = "SELECT * FROM contenido WHERE id = '".$_GET['id']."'";
$result = consutar_DBMS($query);
print($result)
es decir, utilizamos la variable id, pasada por GET para obtener el contenido (recurso muy utilizado en las páginas). En este caso, podríamos hacer cosas como:
- -1' union select usuario, password from users -- nos robamos la tabla de usuarios
- -1' union select datos from informacion_financiera -- nos robamos la info financiera
solo por citar algunos ejemplos.

El operador UNION nos permite concatenar resultados de varias consultas. Lo que hago en el ejemplo es concatenar el resultado de la consulta original con el resultado de una consulta armada por mi. La vida no es tan fácil, y para poder utilizar UNION debemos saber cuántos campos se están obteniendo en la consulta original. La unión sólo tendrá éxito si la cantidad de campos seleccionados por nuestra consulta coincide con la cantidad de campos seleccionados en la consulta original.

Dependiendo el DBMS, se puede lograr incluso obtener una shell remota en el servidor! Por ejemplo, MS SQL Server provee el store procedure xp_cmdshell, con el cual podemos ejecutar comandos en el sistema operativo:
- '; exec master..xp_cmdshell 'dir' -- nos lista el contenido del directorio


Blind

Existen dos tipos de ataque SQL Injection. En el más simple, el atacante obtiene respuesta del DBMS en caso de error, orientándolo sobre cómo armar una consulta válida, pero en otros casos (cuando el programador se acuerda de capturar las excepciones), el site no nos revela ninguna información sobre lo que sucedió en el DBMS. En el último caso, el atacante trabaja "a ciegas" y al tipo de ataque se lo llama Blind SQL Injection.

Es claro que es mucho más difícil atacar a ciegas porque el atacante debe descubrir cómo se están ejecutando las transacciones sin mucha ayuda. Por ejemplo, no es lo mismo ingresar una comilla en un campo y obtener un error como:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1
que no ver ningún error y que la página retorna como si nada.

Seguramente se preguntarán, si no me entero que sucede detrás, cómo puedo saber si una página es vulnerable a SQL Injection? La técnica utilizada para encontrar la vulnerabilidad en estos casos es observar la respuesta del servidor. En muchos casos el site responderá de forma sutilmente distinta cuando una consulta es inválida. Además el atacante puede hacer pruebas true/false con consultas conocidas por retornar siempre true o siempre false.

Por ejemplo, tomemos el código anterior, donde se obtiene el contenido de la sección usando la variable id. Ahora supongamos que el programador captura las excepciones y no vemos respuesta en caso de consultas inválidas. El atacante puede saber que el site es vulnerable utilizando las consultas:
- 1' and '1'='0
- 1' and '1'='1
asumo en este caso que existe el contenido con id 1 (lo obtenemos de la misma página). Como pueden ver, la primer consulta no retorna contenido, mientras que la segunda si lo hace. Esta diferencia entre respuestas hace que el atacante sepa que el site es vulnerable.

Los ataques blind son muy complejos porque cuesta mucho obtener datos como nombres de tablas, cantidad de columnas, nombre de las columnas, etc, aunque no son imposibles. Un atacante decidido, con el tiempo necesario, los puede realizar.


Continuará...

Este artículo es una breve introducción al SQL Injection y los tipos de inyección, e intenta demostrar lo peligroso que es el ataque. Necesitaba esta introducción para poder explicar ataques grandes y más complejos. Espero dejarlos lo suficientemente interesados para lo que vendrá.

SQL Injection es un ataque con muchas posibilidades. Si bien todo lo que podamos hacer por SQL es estándar para todos los DBMSs, existen ataques dependientes del DBMS subyacente. Obtener nombres de tablas en SQL Server no se realiza da la misma forma que en MySQL, y así para todos los DBMS. El tema da para mucho y espero contar con el tiempo suficiente para contarles todo =)

La idea es hacer con SQL Injection una serie de artículos como el de XSS, contando con un artículo destinado a cómo protegernos (si, recuerden que trabajamos en seguridad, no todo es hacking...).


Referencias

- SQL Injection (wiki)
- SQL Injection (OWASP)

6 comentarios:

MagnoBalt dijo...

Buenas buen Post, tenia ganas tambien de hacer algo similar sobre SQLi.
Tengo un arreglo, Si el post esta basado en MySQL sobre php no se puede hacer stacked query como esto

- '; drop table users -- borramos la tabla users

Ahora si no lo esta, esta bien.!! Lo digo por que veo que mensionas $_GET $_POST etc..

Se espera los otros articulos de SQLi

Un abrazo

d3m4s1@d0v1v0 dijo...

Hola Magno,
es verdad lo que decís, en MySQL con PHP no se puede hacer más de una consulta en la misma sentencia al DBMS, olvidé aclararlo. Pasa que traté de dejar este post bien general, sin entrar en detalles del DBMS subyacente.
Espero tener tiempo para armar el resto de los artículos de SQLi, aunque por ahora estoy con otras cosas.
Saludos

nacho dijo...

El tema es super interesante, voy a seguir revisando el blog! saludos!

Juan Manuel dijo...

Desde ya esta muy bueno el post me gustaria ver mas sobre el tema como lo que es administracion en redes con linux como un manual de Ntop o cosas asi

Desde ya saludos.

Juan Manuel de tobauntu.com.ar

Anónimo dijo...

buen blog loco me gusta esto es muy interesante soy estudiante de sistemas que recien he empezado gracias porla informacion

Anónimo dijo...

thank you very much

Publicar un comentario