viernes, 14 de enero de 2011

SQL Injection Web Attacks [Parte I]

Hola a todos, en esta nueva serie de posts vamos a abordar un tema nada nuevo pero sí muy interesante. Se trata de las vulnerabilidades SQL Injection. Ya antes en el blog hemos explicado vagamente lo que es un SQL injection y hemos visto como explotarlos usando diferentes técnicas para casos específicos. Ver:
Sin embargo creo que ha llegado el momento de profundizar un poco para entender bien como funciona este tipo de vulnerabilidad y podamos explotarla a nuestro gusto.

1 Preparando el laboratorio

Para poder seguir los ejemplos que pondremos va a ser necesario contar con un servidor web Apache con el modulo de php instalado y un servidor de base de datos MySQL. Podemos conseguir todo eso (y más) instalando el paquete XAMPP para nuestro sistema. Explicaré como hacerlo en linux, si usas windows es como siempre: clic en next hasta que la ventana desaparezca.

Descarga el paquete de XAMPP para linux desde aquí:

http://www.apachefriends.org/en/xampp-linux.html#374

Es un comprimido, extraelo en el directorio /opt (necesitaras privilegios de root)

sudo tar xvfz xampp-linux-1.7.3a.tar.gz -C /opt

¡Listo, ya esta instalado! xD Para arrancar XAMPP ejecuta esto:

sudo /opt/lampp/lampp start

Y para detenerlo esto:

sudo /opt/lampp/lampp stop

XAMPP trae un asistente para cambiar los password por defecto, lo llamamos así:

sudo /opt/lampp/lampp security

El directorio publico del servidor web está en "/opt/lampp/htdocs". Crearemos un subdirectorio para poner ahí el código de los ejemplos.

sudo mkdir /opt/lampp/htdocs/test

Ahora crearemos una base de datos para nuestros experimentos. Deberás tener el XAMPP corriendo.

Primero abrimos la consola de MySQL. Para ello nos ubicamos en el directorio "/opt/lampp/bin" con el comando cd.

cd /opt/lampp/bin

Y ejecutamos mysql. Si le pusiste password al usuario root deberá ir el parámetro "p".

./mysql -u root -p

Una vez dentro de la consola, creamos la base de datos con esta orden:

mysql>create database db_test;

Y la seleccionaremos con:

mysql>use db_test;

Llegado a este punto ya solo es copiar y pegar las ordenes SQL de los ejemplos.

Bueno, creo que ya tenemos todo lo necesario para empezar.

2 SQL Injection Web Attacks

Una vulnerabilidad SQL Injection (SQLi) es un error de implementación, generalmente en aplicaciones web aunque no es exclusivo de estas, que puede permitirle a un atacante modificar las consultas que hace la aplicación a su base de datos para, de esta manera, alterar el comportamiento normal de la aplicación y obtener ventajas de ello.

Esta vulnerabilidad es calificada como crítica pues en el peor (o mejor, dependiendo de nuestra perspectiva) de los casos se puede conseguir ejecutar ordenes en el servidor. Sin embargo, por distintos factores de un escenario particular, la gravedad del impacto puede reducirse.

Si bien los SQLi no son exclusivos de aplicaciones web, como ya se mencionó, en esta serie nos enfocaremos en los ataques a este tipo de aplicaciones por ser los más frecuentes. Será preciso, entonces, conocer primero como funciona una aplicacion web.

3 Funcionamiento de una aplicación web

Todo empieza cuando un usuario solicita una pagina al servidor web. Este evento puede desencadenarse al hacer clic en un link, al darle enviar a un formulario, al presionar enter sobre la barra de direcciones o de muchas otras maneras. La solicitud que se envía puede contener parámetros que especifiquen de forma más detallada lo que se está pidiendo. Estos parámetros son pares de la forma "variable=valor" e irán en la solicitud de dos posibles formas: ocultos (método POST) o visibles (método GET)

Por ejemplo, seguro habrás visto alguna vez en la URL de una pagina algo así:

http://ejemplo.com/index.php?page=news&id=12

Los parámetros van después del signo de interrogación "?" y separados entre sí por el signo et "&". Quizá, en el ejemplo, estos parámetros sirven para especificar que estamos pidiendo la pagina de noticias, exactamente la noticia número 12. Este es un ejemplo de método GET donde claramente vemos las variables y sus valores en la URL.

Cuando la solicitud llega al servidor web, este identifica el recurso pedido, en el ejemplo es index.php, e intenta entregárselo al usuario. Sin embargo, como en nuestro caso se trata de una página dinámica, necesita ser interpretada por el motor de PHP antes de enviarla al usuario.

Entonces PHP toma el código de la pagina y empieza a ejecutarlo. Dentro de ese código se recupera el valor de las variables enviadas por el usuario y en base a ello se hará una u otra operación. Por ejemplo, conectarse a la base de datos y solicitar de la tabla noticias la que tenga el identificador número 12.

Para que la aplicación pueda pedirle información a la base de datos debe formar una consulta SQL. SQL (Structured Query Language) es un lenguaje de acceso a bases de datos relacionales que permite realizar diversos tipos de operaciones sobre éstas y la información que contienen.

Finalmente la salida que resultó de la ejecución del script php, es tomada por el servidor web y enviada al navegador del usuario como respuesta a su solicitud.

Fig. 1: Funcionamiento de una web.

Veamos como sería el código de la aplicacion del ejemplo.

<?php
 //Obtiene los parametros enviados por el usuario
 $page = $_GET['page'];
 $id = $_GET['id'];

 //Verifica la pagina que se esta pidiendo
 if($page == "news") {
  //Conecta a la base de datos (reemplazar user y pass)
  $link = mysql_connect ("localhost", "user", "pass");
  
  //Selecciona la base de datos (reemplazar database)
  mysql_select_db("database", $link);
  
  //Forma la consulta SQL
  $query = "SELECT * FROM noticias WHERE id=$id";
 
  //Envia la consulta a la base de datos
  $result = mysql_query ($query, $link);
 
  //Muestra el resultado de la consulta
  $row = mysql_fetch_array ($result);
  print("<h1>$row[titulo]</h1>\n");
  print("<p>$row[detalle]</p>\n");
  print("<i>Por: $row[autor]</i>");
 }
 else {
  print("Pagina no encontrada.");
 }
?>

Debes guardarlo dentro del directorio de pruebas, que creamos al inicio, con el nombre index.php. No olvides cambiar el usuario, la contraseña y el nombre de la base de datos, por los valores que estés usando.

Además, es necesario también crear la tabla noticias en nuestra base de datos y llenarla de información para tener algo así:

noticias:
+----+----------+-------------------------+---------+
| id | titulo   | detalle                 | autor   |
+----+----------+-------------------------+---------+
|  1 | TITULO 1 | Detalle de noticia 1... | Autor 1 |
|  2 | TITULO 2 | Detalle de noticia 2... | Autor 2 |
|  3 | TITULO 3 | Detalle de noticia 3... | Autor 3 |
+----+----------+-------------------------+---------+

Las ordenes SQL para construir esa tabla serían:

CREATE TABLE IF NOT EXISTS `noticias` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `titulo` varchar(50) NOT NULL,
  `detalle` varchar(50) NOT NULL,
  `autor` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `noticias` (`id`, `titulo`, `detalle`, `autor`) VALUES
(1, 'TITULO 1', 'Detalle de noticia 1...', 'Autor 1'),
(2, 'TITULO 2', 'Detalle de noticia 2...', 'Autor 2'),
(3, 'TITULO 3', 'Detalle de noticia 3...', 'Autor 3');

Podemos copiar y pegar directamente en la consola de mysql o guardarlas en un archivo, por ejemplo ordenes.sql, y luego llamar al comando mysql de esta manera:

$mysql -u root -p -D [base_de_datos] < ordenes.sql

Si todo ha ido bien, entonces ahora podremos ver como funciona la aplicacion conectándonos a:

http://localhost/test/index.php?page=news&id=1

y podemos ir variando el "id" para mostrar las otras noticias.

http://localhost/test/index.php?page=news&id=2
http://localhost/test/index.php?page=news&id=3
...

Fig. 2: Ejemplo de aplicación web.

4 Entendiendo la vulnerabilidad

Hasta este punto ya debemos haber comprendido como es que funciona una aplicación web. Ahora analizaremos ligeramente el código de la aplicación para ver cual es el error que nos permite modificar las consultas SQL.

En las lineas 3 y 4 podemos observar como se obtienen los valores enviados por el usuario:

$page = $_GET['page'];
$id = $_GET['id'];

Observe que no se hace ninguna validación. Luego en la linea 15 se concatena el valor de la variable $id para formar la consulta SQL.

$query = "SELECT * FROM noticias WHERE id=$id";

Por ejemplo si $id fuera 1 la orden quedaría así:

SELECT * FROM noticias WHERE id=1

La orden SELECT sirve para pedir información. En el ejemplo se está pidiendo todos los registros de la tabla noticias cuyo valor de la columna id sea igual a 1.

Pero que pasaría si el valor de $id no fuese un inocente número entero sino algo como esto: "1 AND 1=0". Es decir la llamada a la aplicación la hacemos así:

http://localhost/test/index.php?page=news&id=1 AND 1=0

Luego de concatenar el id, la consulta SQL quedará de esta forma:

SELECT * FROM noticias WHERE id=1 AND 1=0

Ahora la orden significa que se deben seleccionar todos los registros donde el id sea igual a 1 y 1 sea igual a 0. Como la ultima condición siempre es falsa, no se seleccionará ningún registro. La respuesta que recibiremos será algo así:

Fig. 3: Ejemplo de inyección SQL.

Como ven hemos conseguido modificar el significado de la consulta. Esto fue posible gracias a que la aplicación no valida adecuadamente los parámetros que recibe y los concatena tal cual.

Este es el principio de todo SQL Injection. Espero que este ejemplo tan sencillo haya servido para comprender como funciona la vulnerabilidad. Más adelante veremos como explotar un SQLi, las herramientas que podemos utilizar, técnicas de explotación para escenarios más complicados, etc.

Hasta entonces... Saludos.

Otros capítulos de la serie:

9 comentarios:

  1. Muy bueno! Voe que me hicieste caso.
    Aver si saco tiempo y me pongo a ello ;)

    Un saludo

    ResponderEliminar
  2. Gracias xD

    Aunque va a ir algo lento... no tengo mucho tiempo libre estos días :S

    Un saludo...

    ResponderEliminar
  3. Bien amigo ya comenze la parte 1 (Y) Thanks

    ResponderEliminar
  4. Gracias por tu tiempo y dedicacion

    ResponderEliminar
  5. sta en algo too sto :)

    ResponderEliminar
  6. Oye disculpa mi ignorancia, pero en que SO hacemos el laboratorio??

    ResponderEliminar
  7. Recomendaría alguna distro de Linux (Ubuntu por ejemplo)

    Un saludo.

    ResponderEliminar
  8. Si al poner la comilla en un formulario no sale ningun tipo de error, quiere decir que no se le puede aplicar SQL Injection?

    ResponderEliminar
  9. Hola Guillermo

    No necesariamente... las aplicaciones pueden estar configuradas para no mostrar los errores asi que podria suceder que en lugar de mostrar un error suceda otra cosa... algun comportamiento extraño como redirigir a otra página, mostrar un espacio vacío donde antes había contenido, mostrar una página en blanco, mostrar un código de respuesta HTTP 500 Internal Error, etc...

    En la siguiente parte de esta serie se explican algunos tests para determinar si la App es vulnerable o no.

    Un saludo.

    ResponderEliminar