viernes, 1 de abril de 2011

SQL Injection Web Attacks [Parte III]

Hasta este punto ya debemos ser capaces de identificar cuando una aplicación es vulnerable a inyecciones SQL. Lo que aprenderemos en esta y las siguientes partes de la serie serán algunas técnicas para explotar estas vulnerabilidades. Al decir "explotar" me refiero a comprometer la seguridad de la organización ya sea obteniendo información o acceso privilegiados.

Pero primero vamos a montar un entorno vulnerable donde practicar. Usaremos Damn Vulnerable Web App (DVWA), esta es una aplicación web intencionalmente vulnerable que nos permitirá practicar y aprender sobre vulnerabilidades web sin meternos en líos con la justicia xD

Pueden descargar DVWA desde aquí:

http://sourceforge.net/projects/dvwa/files/DVWA-1.0.7.zip/download

Como en la primera parte mostramos como instalar el XAMPP voy a suponer que ya lo tienes en tu máquina. Si no, date una vuelta por aquí:

http://alguienenlafisi.blogspot.com/2011/01/sql-injection-web-attacks-parte-i.html

El archivo que descargamos es un zip. Extraemos su contenido en el directorio /opt/lampp/htdocs así (necesitaremos privilegios de root):

# unzip DVWA-1.0.7.zip -d /opt/lampp/htdocs/

Ahora tenemos que configurar los parámetros para que DVWA pueda acceder a la base de datos. Abriremos el archivo /opt/lampp/htdocs/dvwa/config/config.inc.php

# gedit /opt/lampp/htdocs/dvwa/config/config.inc.php

Únicamente hay que poner el password del usuario root de mysql en la variable $_DVWA[ 'db_password' ]

<?php
# Database management system to use
$DBMS = 'MySQL';
#$DBMS = 'PGSQL';

# Database variables
$_DVWA = array();
$_DVWA[ 'db_server' ] = 'localhost';
$_DVWA[ 'db_database' ] = 'dvwa';
$_DVWA[ 'db_user' ] = 'root';
$_DVWA[ 'db_password' ] = 'rootpassword'; //aquí pon el password del root

# Only needed for PGSQL
$_DVWA[ 'db_port' ] = '5432';
?>

Luego si no tenemos corriendo el xampp lo iniciamos con:

# /opt/lampp/lampp start

Ahora con nuestro navegador vamos a http://localhost/dvwa veremos algo como esto:

Fig. 1 - Error porque aún no existe la base de datos.

Hacemos clic en "here". Cargará otra página donde se muestra un botón para crear la base de datos de DVWA. Le hacemos clic.

Fig. 2 - Botón para crear la base de datos.

Hasta aquí ya terminamos de instalar DVWA.

Has clic en "Logout" e inicia sesión con estos datos:

Username = admin
Password = password

Verás una página como esta:

Fig. 3 - Pagina principal de DVWA.

He resaltado con verde las diferentes vulnerabilidades con las que podemos practicar. Más abajo está resaltado con amarillo el menú para cambiar la dificultad de DVWA. DVWA permite 3 niveles de dificultad: low, medium y high. Por defecto está en high así que debes cambiarlo a low.

Nuestro entorno para prácticas ya esta listo. Continuemos...

6 Extracción de datos

Empezaremos aprendiendo algunas técnicas para extracción de datos. Para ello seleccionaremos la vulnerabilidad "SQL Injection" (en nivel low). Muestra un formulario de búsqueda por "Id" de usuario donde, por ejemplo, si colocamos 1 mostrará los detalles del usuario "admin".

Fig. 4 - Vulnerabilidad SQL Injection.

Bien... haremos un sencillo test para verificar que el formulario es vulnerable a SQLi. Colocamos una comilla simple (') y le damos a "Submit". Debes observar un mensaje de error similar a 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 ''''' at line 1

Excelente, ello significa que podemos alterar la sintaxis de la consulta SQL. Haremos otro test, ahora por tautología, para confirmar esto. Colocamos: ' or ''='

Fig. 5 - Test por tautología.

Como ves se ha listado la información de todos los usuarios, esto sucedió porque logramos anular la condición que filtra los resultados con una tautología. No siempre va a ser posible mostrar todos los resultados generando tautologías, en ocasiones la aplicacion siempre mostrará un único resultado ya sea por que usa un LIMIT o porque solo coge el primer elemento de la respuesta.

Si no estas comprendiendo muy bien, quizá te ayude revisar el código fuente de la aplicación. Para ello has clic en el botón "View Source" que aparece en la parte inferior derecha. Si aún así tienes dificultades deberías revisar nuevamente la primera y segunda parte de esta serie.

6.1 Obtener el número de campos seleccionados con ORDER BY

El primer paso para explotar una vulnerabilidad SQL es identificar cuantos campos se están seleccionando en la consulta. Por ejemplo, si ya has visto el código fuente, veras que la consulta es más o menos así:

SELECT first_name, last_name FROM users WHERE user_id = '1';

Como verás se están seleccionando dos campos: first_name y last_name. Pero ¿Como podemos saberlo sin necesidad de tener el código fuente?

A veces es suficiente con generar un error de sintaxis para ver toda la consulta SQL y de ahí obtener el número de campos seleccionados, sin embargo no siempre es así. Lo más común es que el error de sintaxis solo muestre una pequeña parte de la consulta y no veamos cuantos campos se seleccionan.

Existe otro método usando la clausula ORDER BY. Esta clausula nos permite ordenar los resultados de la consulta en función de cualquiera de los campos seleccionados. Por Ejemplo:

SELECT first_name, last_name FROM users ORDER BY first_name

La consulta anterior ordena la respuesta por el campo "first_name".

Una forma alternativa de usar ORDER BY es no indicando el nombre del campo sino su posición. Así:

SELECT first_name, last_name FROM users ORDER BY 1

Esta otra consulta también ordena los resultados por el campo "first_name" ya que es el que aparece en la primera posición. Pero ¿Qué sucede si tratamos de ordenar por una posición que no existe? Por ejemplo:

SELECT first_name, last_name FROM users ORDER BY 3

La posición 3 no existe porque solo se seleccionan 2 campos. Por ello esta consulta generará un error parecido a este:

Unknown column '3' in 'order clause'

Entonces, para averiguar el número de campos seleccionados, la idea es inyectar un ORDER BY e ir ordenando por el primer campo, luego por el segundo y así de forma incremental hasta generar un error. Cuando ello suceda sabremos cuantos campos se seleccionan en la consulta.

6.1.1 Binary Search con ORDER BY

Lo anterior puede funcionar, pero es poco eficiente. Lo que en realidad se usa es una técnica de búsqueda binaria (Binary Search). Esta técnica consiste en coger un valor arbitrario N y hacer una consulta ordenando por dicho valor. Hay dos posibles respuestas: bota error o no bota error. En caso se produjera un error deducimos que el número de campos seleccionados es menor al N que tomamos. Y en caso no haya error en la consulta, deducimos que el número de campos es igual o mayor que N. En el primer caso ya hemos establecido un intervalo donde se encuentra el número buscado (de 1 a N) sin embargo, en el segundo, solo hemos establecido un limite inferior (de N a más). Para tener un limite superior en el segundo caso simplemente volveremos a consultar ordenando esta vez por el doble de N y dependiendo de la respuesta, hay error o no hay error, estableceremos un limite superior (de N a 2N) o un nuevo limite inferior (de 2N a más) respectivamente. Se repite la operación anterior hasta tener un intervalo bien definido, con limite inferior y superior. Cuando ya hemos determinado el intervalo procedemos a encontrar su elemento medio, este se puede definir como el cociente entero de la suma de límite inferior y el superior entre dos. Luego ordenamos por el elemento medio y dependiendo de la respuesta tomaremos la mitad inferior o superior del intervalo como nuevo intervalo y al elemento medio como nuevo limite superior o inferior respectivamente. Se continua dividiendo el intervalo cada vez por la mitad hasta deducir el número de columnas seleccionadas.

Para comprenderlo mejor pongamos un ejemplo: si en una determinada consulta se seleccionaran 11 campos haríamos lo siguiente:

Tomamos como N arbitrario el número 10 (podría ser cualquier otro).
CONSULTA                                     RESPUESTA  DEDUCCIÓN
select 1,2,3,4,5,6,7,8,9,10,11 ORDER BY 10;  OK         Hay 10 o más. Limite inferior = 10

La consulta no produjo error. Eso quiere decir que hay 10 o más campos y que aun no tenemos un limite superior. Ahora ordenamos por el doble de N. En este caso por 20.
CONSULTA                                     RESPUESTA  DEDUCCIÓN
select 1,2,3,4,5,6,7,8,9,10,11 ORDER BY 20;  ERROR      Hay menos de 20. Limite superior = 20

Ya tenemos un intervalo definido con limite superior (20) e inferior (10). Ahora buscamos el elemento medio: (10+20)/2 = 15 Y ordenamos por ese valor.
CONSULTA                                     RESPUESTA  DEDUCCIÓN
select 1,2,3,4,5,6,7,8,9,10,11 ORDER BY 15;  ERROR      Hay menos de 15. Limite superior = 15

Nuevamente calculamos el elemento medio: (10+15)/2 = 12 (cociente entero)
CONSULTA                                     RESPUESTA  DEDUCCIÓN
select 1,2,3,4,5,6,7,8,9,10,11 ORDER BY 12;  ERROR      Hay menos de 12. Limite superior = 12

Elemento medio: (10+12)/2 = 11
CONSULTA                                     RESPUESTA  DEDUCCIÓN
select 1,2,3,4,5,6,7,8,9,10,11 ORDER BY 11;  OK         Hay 11 o más. Limite inferior = 11

Si hay 11 o más y menos de 12, por deducción simple se están seleccionando 11 campos. Si hubiéramos utilizado la técnica de ordenamiento incremental habrían sido necesarias 12 consultas pero con la técnica de búsqueda binaria solo fueron necesarias 5 (menos de la mitad). Espero que con ese ejemplo se haya comprendido bien la técnica de búsqueda binaria ya que nos ayudará a ahorrar tiempo y es la que usan muchas herramientas de explotación automatizada.

6.1.2 Práctica ORDER BY en DVWA

Ahora hagámoslo en el DVWA. La inyección, utilizando la técnica de ordenamiento incremental, sería así:

INYECCIÓN        RESPUESTA
' order by 1#    no pasa nada
' order by 2#    no pasa nada
' order by 3#    Unknown column '3' in 'order clause'

Fig. 6 - Inyección ORDER BY.

Fig. 7 - Error ORDER BY.

Observe que coloque el carácter # al final para comentar la comilla que viene después. La consulta final con esta inyección se vería así:

SELECT first_name, last_name FROM users WHERE user_id = '' ORDER BY 3#';

Te dejo la práctica de deducción por búsqueda binaria a ti.

6.2 Extracción de datos con UNION SELECT

Bien, ya sabemos cuantos campos se están seleccionando en la consulta. Ahora lo que sigue es saber cuales de esos campos se muestran al usuario en la página web. No todos los campos que se seleccionan se insertan en la pagina web de respuesta, algunos solo son usados internamente por la aplicación. Saber que campos son visibles nos servirá para sacar a través de ellos la información que queremos de la base de datos.

Si la aplicación no muestra ningún campo en la respuesta estaremos frente a un caso de Blind SQL Injection o inyección SQL a ciegas. Para esos casos hay otras técnicas que podemos utilizar pero esas las explicaremos más adelante. Por ahora solo nos interesa aprender a explotar un SQLi común y corriente.

Para averiguar qué campos se muestrán en la página web usaremos la clausula UNION. Esta clausula le indica a MySQL que debe unir la respuesta de la consulta anterior a UNION con los resultados de la consulta que le sigue. Sin embargo para que esto sea posible es requisito necesario que ambas consultas tengan el mismo número de campos. Si no es así, producirá un error. Por ello era preciso aprender primero a deducir el número de campos ;)

Por ejemplo:

SELECT first_name, last_name FROM users WHERE user_id = '1' UNION SELECT first_name, last_name FROM users WHERE user_id = '2';

La unión de las consultas anteriores seleccionará los datos de los usuarios con id 1 y 2.

A pesar que en el ejemplo anterior se está seleccionando en ambas consultas los mismos campos de la misma tabla, esto en realidad no es necesario. El único requisito obligatorio es que ambas consultas tengan el mismo número de campos, por lo demás, estos campos podrían ser constantes, resultados de funciones, columnas de diferentes tablas o incluso de diferentes bases de datos.

Entonces podemos construir inyecciones con UNION SELECT para hacer nuestras propias consultas a la base de datos y extraer la información que queramos.

6.2.1 Práctica de extracción de datos en DVWA

La primera inyeccion que haremos con UNION SELECT será para ver qué campos se muestran en la página web. Esto lo podemos conseguir así:

' AND 1=0 UNION SELECT 1,2#

Fig. 8 - Averiguar campos visibles con UNION SELECT.

La consulta que le llegaría a la base de datos se vería así:

SELECT first_name, last_name FROM users WHERE user_id = '' AND 1=0 UNION SELECT 1,2#';

Como puedes observar he añadido un AND 1=0 antes de UNION. Esto es para anular la consulta anterior y solo se muestren los resultados de nuestra consulta inyectada.

Como resultado de nuestra inyección se seleccionan los números 1 y 2 que podemos ver en la página de respuesta en el área que corresponde a "First Name" y "Surname" respectivamente. Es decir que los dos campos de la consulta son visibles. En algunas ocasiones solo se muestran unos cuantos.

Ahora que ya sabemos que los campos 1 y 2 son visibles los utilizaremos para obtener algo de información:

' AND 1=0 UNION SELECT user(),version()#

Fig. 9 - Usuario y versión de la base de datos.

Las funciones user() y version() devuelven el usuario de la base de datos y la versión de MySQL respectivamente. Otra función interesante es database() que devuelve el nombre de la base de datos.

Bien hasta aquí ya hemos aprendido como funciona la extracción de datos con UNION SELECT. En el próximo capitulo de la serie (que no tengo ni idea de cuando saldrá xD) profundizaremos un poco más en la extracción de datos.

Un saludo y hasta pronto.

Otros capítulos de la serie:

5 comentarios:

  1. Hola Amigo, esperando el IV capitulo
    Gracias

    ResponderEliminar
  2. Hola Anónimo, gracias por el interés en la serie... pero lamentablemente todavía va a demorar...

    Un saludo.

    ResponderEliminar
  3. Hola muy interesante este articulo espero que sigas publicando las siguientes partes porque explicas de una manera facil de entender :)
    Gracias.

    ResponderEliminar
  4. Excelente viejo!!! a la espera de la siguiente lección.

    ResponderEliminar