jueves, 3 de febrero de 2011

SQL Injection Web Attacks [Parte II]

Ahora que ya sabemos de lo que trata un SQLi podemos continuar por como detectar esta vulnerabilidad en un sitio web.

5 Buscando vulnerabilidades SQLi.

Anteriormente vimos que un SQLi se produce cuando la aplicacion web forma las consultas SQL concatenando parámetros, que nosotros le enviamos, sin validarlos adecuadamente. A esos parámetros les llamaremos entradas de la aplicacion web. El primer paso para detectar un SQLi será identificar todas las entradas que recibe la aplicación. Para ello recordemos que las entradas se envían usando los métodos GET y POST del protocolo HTTP. Veamos de forma más detallada estos métodos.

El método GET se usa normalmente para pedir cualquier recurso vía HTTP. Si queremos enviar parámetros usando este método, estos irán en la URL después del signo de interrogación "?" y por tanto son visibles y fáciles de modificar desde nuestro navegador.

GET /test/index.php?page=news&id=1 HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Ubuntu 10.10)
Accept: text/html

Este es un ejemplo de solicitud HTTP que usa el método GET para pedir el recurso /test/index.php pasándole los parámetros page=news e id=1. También se puede observar las cabeceras Host, User-Agent y Accept que aportan información adicional sobre la solicitud.

El método POST es bastante similar, pero a diferencia de GET, se usa para enviar información al servidor (comúnmente formularios). No envía los datos en la URL sino que hace uso del cuerpo del mensaje HTTP por lo que puede enviar mayor cantidad de información. Envía los datos "ocultos", es decir, un usuario normal no podrá verlos ni modificarlos tan fácilmente.

POST /test/index.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Ubuntu 10.10)
Accept: text/html
Content-Type: application/x-www-form-urlencoded
Content-Length: 14

page=news&id=1

El ejemplo muestra una solicitud HTTP POST que envía datos a /test/index.php. Los datos van al final del mensaje. En las cabeceras podemos ver el Content-Type que en este caso especifica que estamos enviando un formulario y el Content-Length que indica el número de caracteres de los datos enviados. Después de las cabeceras viene una linea en blanco a modo de separación y finalmente los datos que queremos enviar.

Además de los parámetros que enviamos por GET o POST, otras zonas que se pueden utilizar para inyectar código SQL son algunas cabeceras como: Cookie, User-Agent, Referer o Host. Las cabeceras del protocolo HTTP aportan información adicional sobre la solicitud, van a una por linea y tienen la forma "Cabecera: valor". Por ejemplo, la cabecera Cookie se usa para identificar la sesión del usuario, algunas aplicaciones web almacenan su valor en la base de datos, junto a otros datos de la sesión, y posteriormente lo utilizan para recuperar dichos datos. De ahí que pueda ser vulnerable a una inyección SQL. La cabecera User-Agent le informa al servidor nuestro navegador y sistema operativo, Referer indica la URL de la pagina que estábamos viendo al momento de hacer la solicitud y Host, el nombre de dominio del sitio web solicitado. Todos estos datos podrían utilizarse para fines estadísticos y su valor ser almacenado en una base de datos. Por ello también debemos considerarlos en nuestro análisis.

5.1 ZAP, una herramienta para detección automatizada.

Bien ahora que conocemos las zonas que podemos utilizar para testear un SQLi, el siguiente paso será modificar los valores de los datos que enviamos buscando generar algún error o deducir si podemos alterar la consulta SQL. Para ello vamos a utilizar una herramienta llamada Zed Attack Proxy (ZAP) que se puede descargar desde el siguiente enlace:

http://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project

ZAP actúa como un proxy, un intermediario entre nuestro navegador y el servidor web, de modo que todo el intercambio de datos pasa por ZAP y este puede analizarlo y descubrir muchos tipos de vulnerabilidades. Cuenta con herramientas muy útiles que nos permiten escanear puertos, escanear vulnerabilidades, encontrar recursos mediante crawling o por fuerza bruta, modificar los mensajes HTTP/HTTPS en ambos sentidos de la comunicación, entre otros. ZAP está hecho en java así que para correrlo en nuestro equipo necesitaremos tener instalado el interprete de java.

Instalar ZAP en linux es bastante sencillo. Primero creamos una carpeta y lo descomprimimos ahí

mkdir zaproxy
tar -xzvf ZAP_1.2.0_Linux.tar.gz -C zaproxy

Luego nos ubicamos dentro de la carpeta y le damos permisos de ejecución al script zap.sh

cd zaproxy
chmod +x zap.sh

Finalmente ejecutamos ZAP con el script

./zap.sh


Para configurar el puerto por donde escucha ZAP hacemos click en el menú Tools y luego en Options. Escogemos la opción Local proxy y cambiamos el puerto al que queramos.


ZAP ya esta listo. Ahora solo configura tu navegador para que use a ZAP como proxy.

Desde ahora todas las paginas que visitemos serán capturadas por ZAP para poder analizarlas más tarde.

Veamos un ejemplo de uso.

Buscando con google, no demoraras mucho en encontrar paginas que reciban parámetros en la URL. En mi caso busqué así:

inurl:"id=" "biblioteca"

Uno de los resultados fue el siguiente:

http://biblioteca.unizar.es/biblio.php?id=27

Le hacemos clic y ahora revisamos lo que ha capturado ZAP en la pestaña History del panel inferior derecho.


Como podemos ver, ZAP ha registrado la solicitud. Si damos clic sobre el registro, en las pestañas "Request" y "Response" del panel superior veremos la solicitud y respuesta HTTP respectivamente. La otra pestaña "Break" se usa para modificar los mensajes HTTP pero eso lo veremos más adelante.

Ahora, para scannear vulnerabilidades, hacemos clic derecho sobre el registro y después en "Scan this history". ZAP iniciará el escaneo en la pestaña "Active Scan".


Si ha detectado alguna vulnerabilidad lo podremos ver en la pestaña "Alerts". En este caso encontró un SQLi


ZAP puede detectar inyecciones SQL en parámetros enviados tanto por GET como por POST pero al parecer no testea los headers HTTP. Para hacer un testeo manual de esos campos es muy útil la opción "break". Lo que hará sera preguntarnos si queremos modificar los mensajes HTTP tanto de entrada como de salida, es decir, solicitudes y respuestas HTTP. Para activar esta opción solo hay que dar clic sobre el botón "Set Break" (el circulo verde) de la parte superior.

Luego cuando ZAP capture un mensaje, antes de dejarlo seguir, podremos modificarlo en la pestaña "Break" que ya antes habíamos mencionado. Cuando terminamos la edición le damos clic al botón "Step" (el que esta a la derecha de set break) para pasar al siguiente mensaje. Si ya no queremos seguir modificando mensajes le damos clic al botón "Continue" o si queremos descartar un mensaje al boton "Bin".

5.2 Cómo testear un SQLi.

El proceso de testeo consiste en modificar el valor de las entradas buscando deducir si conseguimos alterar la consulta SQL. Para ello podemos aprovecharnos de algunas propiedades lógicas, errores de la base de datos, características del lenguaje SQL, etc.

Ahora veremos algunas técnicas para deducir un SQL injection. Por simplicidad los ejemplos que se proponen supondrán inyecciones en entradas enviadas por GET, no obstante ello no significa que las técnicas expuestas no puedan aplicarse usando otros vectores de inyección como POST o Headers HTTP.

Por concatenación

En una consulta SQL los valores de texto, a diferencia de los numéricos, necesitan aparecer entre comillas. Por ejemplo:

SELECT * FROM noticias WHERE categoria='deportes'

La cadena "deportes" se indica entre comillas simples. El script php para formar la consulta anterior sería algo así:

//...
$cat = $_GET['cat'];
$query = "SELECT * FROM noticias WHERE categoria='" . $cat . "'";
//...

Observe que el valor de la variable "$cat" se inserta entre dos comillas simples. Luego la llamada desde el navegador sería así:

http://ejemplo.com/news.php?cat=deportes

Para alterar una orden SQL formada de esta manera, habrá que cerrar la comilla que aparece primero con otra comilla y comentar la que viene después. Es decir, la llamada a la página sería:

http://ejemplo.com/news.php?cat=deportes' or 1=1--

Con lo que la consulta resultante quedará así:

SELECT * FROM noticias WHERE categoria='deportes' or 1=1--'

Claramente podemos apreciar que inyectamos un "or 1=1".

Una forma de testear inyecciones en campos de texto es mediante concatenación. Es decir, será lo mismo seleccionar las noticias de la categoria "deportes" y "depo"+"rtes". Ya que al concatenar las cadenas "depo" y "rtes" se forma "deportes". La sintaxis de concatenación dependerá del dialecto SQL de los diferentes gestores de bases de datos (MySQL, Oracle, SQL Sever, etc). En el caso de MySQL es así:

SELECT * FROM noticias WHERE categoria='depo' 'rtes'

Basta con que las cadenas estén separadas por un espacio en blanco.

Entonces la llamada a la página web quedaría así:

http://ejemplo.com/news.php?cat=depo'+'rtes

Recuerde que el signo "+" en la URL indica un espacio en blanco, también se puede usar la representación hexadecimal del espacio en blanco, que es "%20", así:

http://ejemplo.com/news.php?cat=depo'%20'rtes

Si estas ultimas llamadas producen el mismo resultado que la llamada original se puede deducir que la concatenación se está realizando y por tanto que la aplicación es vulnerable a SQLi.

Por operaciones aritméticas

Cuando el valor que se inserta en la consulta SQL es numérico podemos utilizar algunas operaciones aritméticas para deducir un SQLi. Por ejemplo:

http://ejemplo.com/news.php?id=10

Supongamos que la llamada anterior muestra la noticia con identificador número 10 dentro de la base de datos. Es decir se forma una consulta algo así:

SELECT * FROM noticias WHERE id=10

Si ahora cambiamos el valor de id a "10-1" así:

http://ejemplo.com/news.php?id=10-1

y se selecciona la noticia con identificador 9, podemos deducir que la operacion de resta se esta ejecutando y que la aplicación es vulnerable. Tambien se puede hacer con la operación de suma pero recuerda que el signo "+" en la URL significa un espacio en blanco por lo que es mejor usar su representación hexadecimal "%2b" así:

http://ejemplo.com/news.php?id=10%2b1

Otra operación interesante es la división entre cero ya que matemáticamente esta operación no está definida. Ejemplo:

http://ejemplo.com/news.php?id=10/0

Cuando MySQL tiene que operar una división entre cero devuelve NULL y no se seleccionará ningún registro. Podemos identificar que sucede esto cuando la web responde con una pagina de error genérica o con un mensaje similar a "No se encontraron resultados".

Por comentarios

Se trata de introducir un comentario en la consulta sin alterarla. Si no se producen errores o no cambia el resultado entonces deducimos que nuestro comentario ha sido aceptado y que la aplicación es vulnerable.

Estos tests también se pueden utilizar para saber que forma de comentario es aceptada y utilizarla mas adelante en la explotación.

Algunas formas son:

http://ejemplo.com/news.php?id=10--comentario
http://ejemplo.com/news.php?id=10/*comentario
http://ejemplo.com/news.php?id=10#comentario
http://ejemplo.com/news.php?id=10%23comentario

http://ejemplo.com/news.php?cat=deportes'--comentario
http://ejemplo.com/news.php?cat=deportes'/*comentario
http://ejemplo.com/news.php?cat=deportes'#comentario
http://ejemplo.com/news.php?cat=deportes'%23comentario

Por operaciones lógicas

Se trata de modificar el resultado lógico de la clausula WHERE usando contradicciones (resultado siempre falso) o tautologias (resultado siempre verdadero) para luego observar el comportamiento de la web y deducir si es vulnerable.

La clausula WHERE es la parte de la sentencia SQL donde se condicionan los registros que deben ser seleccionados. Por ejemplo:

SELECT * FROM noticias WHERE cat='deportes' AND autor='Juan Perez'

Las condiciones son que la columna "cat" sea igual a "deportes" y la columna "autor" sea igual a "Juan Perez".

Imaginemos que la llamada que forma esa consulta es así:

http://ejemplo.com/news.php?cat=deportes&autor=Juan+Perez

Podemos generar una tautología de esta manera:

http://ejemplo.com/news.php?cat=deportes&autor=Juan+Perez'+or+''='

Ya que la consulta quedaría así:

SELECT * FROM noticias WHERE cat='deportes' AND autor='Juan Perez' or ''=''

El "OR ''=''" causa que el resultado de WHERE sea siempre verdadero. En respuesta a esto la web podría mostrarnos todos los registros de la tabla noticias.

Esta forma de inyeccion también se utiliza para saltar algunos formularios de inicio de sesión, poniendo en el campo password "' or ''='". Ya que produce el mismo efecto:

SELECT * FROM users WHERE username='admin' AND password='' or ''=''

El testeo por contradicción busca hacer lo contrario: conseguir que la clausula WHERE arroje siempre falso y por tanto no se seleccione ningún resultado. Por ejemplo:

http://ejemplo.com/news.php?id=10+and+1=0

Esa llamada producirá que la consulta tome esta forma:

SELECT * FROM noticias WHERE id=10 and 1=0

De modo que ahora el WHERE siempre será falso y no se seleccionará ningún registro. Como respuesta deberíamos observar una pagina de error, algún mensaje indicando que no hay resultados o simplemente un espacio vacío donde antes aparecía contenido.

Por errores

Una forma bastante sencilla de saber si podemos alterar la consulta SQL es generando un error de sintaxis. Podemos lograr esto tanto en campos de texto como numéricos incluyendo una simple comilla. Veamos:

http://ejemplo.com/news.php?cat='deportes
SELECT * FROM noticias WHERE cat=''deportes'

http://ejemplo.com/news.php?id='10
SELECT * FROM noticias WHERE id='10

En ambos casos la consulta esta mal formada y veremos un error de sintaxis como este:

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 ''10' at line 1

Sin embargo no siempre PHP está configurado para mostrar los errores de la base de datos y bien podríamos ver una página en blanco, una pagina de error o nuevamente un espacio vacío.

Bueno, hasta aquí la segunda parte. Quizá estoy omitiendo algunas formas más de testeo por olvido o desconocimiento. Si conoces alguna no dudes en comentarla ;)

Un saludo, hasta la próxima...

Otros capítulos de la serie:

4 comentarios:

  1. super bueno, muchas gracias......xd

    ResponderEliminar
  2. esta muy copleto e interesante.
    Gracias
    :)

    ResponderEliminar
  3. Nada... gracias por los comments :)

    Cualquier duda solo comentenla, que apenas tenga tiempo la reviso y respondo :)

    Un saludo.

    ResponderEliminar