¡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 Race Conditions. 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!
Este tipo de vulnerabilidades son más anormales de encontrar en sistemas reales. Además, algunas de ellas requieren acceso a localizaciones arbitrarias por lo que incluso existiendo, es complicado explotarlas. No obstante, sí que las hemos visto algunas veces y en caso de existir suelen ser bastante devastadoras por las acciones que se pueden realizar.
Lab1 (Apprentice) – Limit overrun race conditions
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, successfully purchase a Lightweight L33t Leather Jacket.». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita comprar el artículo indicado.
Comenzamos visualizando que la aplicación nos provee un código de promoción «PROMO20», por lo que lo aplicamos y capturamos la petición enviada para redimirlo teniendo ya el artículo en la cesta con el usuario «wiener:peter».

Vemos que, si intentamos aplicarlo de nuevo saltándonos el límite, la aplicación falla de manera esperable:

Para probar la condición de carrera como nosotros lo haremos, es necesario Burp Suite Professional (la versión Community no tiene esta funcionalidad). Simplemente, eliminamos el código de descuento, volvemos a Repeater y en la pestaña «Custom Actions» a la derecha, añadimos desde plantilla «Trigger Race Condition». Seleccionamos el número de peticiones en paralelo que queremos enviar y lo dejamos configurado.

Una vez configurado, en el menú de la derecha podemos darle al botón de «play» y las peticiones se enviarán en paralelo. Si lanzamos el ataque, observamos que el código de descuento se ha aplicado varias veces, ya que la reducción es mayor de un 20%:

Finalmente, podemos emplazar la orden y resolver el laboratorio, habiendo explotado nuestra primera condición de carrera.
Lab2 (Practicioner) – Bypassing rate limits via race conditions
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, delete the user «carlos».». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita hacer fuerza bruta de la contraseña de «carlos» para borrar su usuario.
Comenzamos, en primer lugar, haciendo una petición de login con el usuario «wiener:peter» para conocer cómo se compone la petición de inicio de sesión.

Vemos que la petición es simple. Si hiciéramos ataque de fuerza bruta común, seríamos bloqueados. Por ello, probaremos a explotar una condición de carrera. Para ello, usaremos Turbo Intruder, una extensión muy útil en estos casos. Hacemos botón derecho y enviamos la petición a dicha extensión de un nuevo login (si no, falla el token CSRF). Configuramos el Turbo Intruder para que el usuario sea «carlos», el password un placeholder y adaptamos el script para usar nuestro diccionario:

def queueRequests(target, wordlists):
# if the target supports HTTP/2, use engine=Engine.BURP2 to trigger the single-packet attack
# if they only support HTTP/1, use Engine.THREADED or Engine.BURP instead
# for more information, check out https://portswigger.net/research/smashing-the-state-machine
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
passwords = ["123123", "abc123", "football", "monkey", "letmein", "shadow", "master", "666666", "qwertyuiop", "123321", "mustang", "123456", "password", "12345678", "qwerty", "123456789", "12345", "1234", "111111", "1234567", "dragon", "1234567890", "michael", "x654321", "superman", "1qaz2wsx","baseball","7777777", "121212", "000000"]
# the 'gate' argument withholds part of each request until openGate is invoked
# if you see a negative timestamp, the server responded before the request was complete
for password in passwords:
engine.queue(target.req, password, gate='race1')
# once every 'race1' tagged request has been queued
# invoke engine.openGate() to send them in sync
engine.openGate('race1')
def handleResponse(req, interesting):
table.add(req)
Lanzamos el ataque y observamos las respuestas:

Analizando la captura anterior, vemos varias cosas:
- Respuestas con código 200 indican que el usuario o contraseña son inválidos. Sin embargo, no se produce el bloqueo de la cuenta, por lo que hemos hecho bypass del rate limit
- Una respuesta con código 302 de redirección lo cual se corresponde con un inicio de sesión correcto
- Respuestas con código 400 indican token CSRF inválido (dado que ya se ha usado en la petición con respuesta 302)
De todo ello, extraemos que la contraseña es la usada en la petición con respuesta 302, por lo que podemos usarla para acceder como «carlos» y borrar su propio usuario, resolviendo el laboratorio.
Lab3 (Practicioner) – Multi-endpoint race conditions
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, successfully purchase a Lightweight L33t Leather Jacket.». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita hacer fuerza bruta de la contraseña de «carlos» para borrar su usuario.
Revisamos, en primer lugar, el proceso de compra a través de la compra de una tarjeta regalo. Tal como podemos ver, el flujo es el siguiente:

El listado de peticiones es el siguiente:
- POST a «/cart» con el tipo de producto y la cantidad a comprar para validar la compra
- POST a «/cart/checkout» para confirmar la compra
Por tanto, podríamos intentar realizar una compra y, en el lapso de tiempo en que se confirma, añadir productos a la cesta. Para ello, en primer lugar, recogemos las dos peticiones y además un GET a la raíz de la web creando un nuevo grupo de peticiones para enviarlo. Lo que intentamos con esto es evaluar si a nivel de backend se produce mucho delay que nos pueda perjudicar:


Si nos fijamos en el tiempo marcado en amarillo abajo, veremos cómo la primera petición tarda un poco más, pero el resto tienen un tiempo menor. Esto nos hace ver que el delay del backend no será limitante. Lo que haremos ahora es el ataque de condición de carrera. Enviaremos:
- Añadir una gift card al carrito y posteriormente seguir el flujo de peticiones siguientes
- Una petición POST con el artículo que el laboratorio nos pide que compremos
- Una petición POST al checkout
Pondremos todas las peticiones para enviar agrupadas a través de la misma conexión, aunque esta vez las enviamos en paralelo para explotar la condición de carrera:

Como vemos, recibimos un mensaje de fondos insuficientes. Puede parecer contradictorio pero, para explotar la condición de carrera, debemos tener algo de «suerte» y que las ventanas temporales se ajusten. Por ello, necesitamos lanzar este mismo ataque varias veces, eliminando todas las veces el artículo que queremos comprar del carrito y dejando solo la gift card (acuérdate de cambiar el ID de producto en la petición POST para actualizar el carrito):

¡¡Cuidado, esta condición de carrera no se da siempre, por lo que es posible que debas repetir el proceso varias veces antes de poder darlo por resuelto!! Simplemente, repite el proceso tantas veces como necesites.
Lab4 (Practicioner) – Single-endpoint race conditions
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, delete the user «carlos».». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita hacernos con el control de la cuenta «carlos@ginandjuice.com» para borrarla.
En primer lugar, vemos cómo se hace uso de la herramienta para cambiar el correo electrónico del usuario:

Tal como vemos, un cambio de correo electrónico genera la petición anterior y, a su vez, se genera un correo con un enlace de un solo uso (importante esto de un solo uso, ya que indica que cada vez que se realiza un cambio, se asocia a la sesión del usuario dicho enlace).

Para confirmar que existe una condición de carrera, usaremos correos de tipo «testX@…» donde X son números correlativos. Hacemos un grupo de peticiones de cambio de correo con dichos correos correlativos y las enviamos en paralelo, observando las respuestas:


Como vemos, hay una desincronización de los correos producida porque la aplicación tiene dos estados probablemente: uno en el que el correo del usuario es cambiado y otro en el que finalmente se envía el correo. Al producirse la condición de carrera, se puede dar la situación de enviar un correo a la cuenta errónea tras haber sido cambiada. Aplicamos el mismo procedimiento pero con dos peticiones, una para cada uno de los siguientes correos:
test@exploit-0aa0005103864ae684f85fe3011900dd.exploit-server.net
carlos@ginandjuice.shop
Lanzamos el ataque tantas veces como sea necesario hasta recibir el link de confirmación de la cuenta de correo de «carlos@ginandjuice.com».

Finalmente, dado que hemos cambiado nuestro correo accediendo al enlace, podemos entrar de nuevo como «wiener:peter» y ver el panel de administración, así como borrar al usuario.
Lab5 (Expert) – Partial construction race conditions
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 this race condition to create an account, then log in and delete the user carlos.». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita borrar al usuario «carlos».
Antes de comenzar, mapeamos el registro de usuarios. Como vemos, debemos registrarnos con una cuenta «@ginandjuice.shop» pero no disponemos de acceso a dicho correo. Asimismo, analizando el Javascript vemos que el código de confirmación del token de registro es tal que así:
const confirmEmail = () => {
const container = document.getElementsByClassName('confirmation')[0];
const parts = window.location.href.split("?");
const query = parts.length == 2 ? parts[1] : "";
const action = query.includes('token') ? query : "";
const form = document.createElement('form');
form.method = 'POST';
form.action = '/confirm?' + action;
const button = document.createElement('button');
button.className = 'button';
button.type = 'submit';
button.textContent = 'Confirm';
form.appendChild(button);
container.appendChild(form);
}
Y, por tanto, la petición a enviar sería algo como:
POST /confirm?token=1 HTTP/2 Host: 0afb00ff046dd03d821e5c140029006e.web-security-academy.netContent-Type: x-www-form-urlencoded Content-Length: 0
Si construimos dicha petición y lanzamos valores arbitrarios, vemos que obtenemos los siguientes errores:
- Valor «token=1»: Incorrect token
- Sin parámetro: Missing parameter token
- Valor «token=»: Forbidden
- Valor «token[]=»: Invalid token: Array
Por tanto, intentamos explotar una condición de carrera en la que, tras registrar un correo de «@ginandjuice.shop», lancemos una confirmación de token con el valor vacío. Esto puede tener éxito siempre y cuando haya una condición de carrera en la cual se genere dicho valor vacío para el token durante un brevísimo período de tiempo. A la vista de los errores, parece que la aplicación debería manejar este aspecto.
Capturamos pues la petición de registro y la de confirmación y las enviamos a Turbo Intruder, generando un script como el siguiente:

Como vemos, simplemente configuramos un ataque en el que intentamos registrar un usuario arbitrario de «@ginandjuice.shop» que cambia en cada iteración y posteriormente lanzamos 25 peticiones para intentar confirmarlo con un array vacío. Si tenemos éxito, lograremos crear una cuenta y confirmarla con credenciales «wiener:peter».

Una vez que tenemos las credenciales y la cuenta confirmada, accedemos con «wiener:peter» y borramos el usuario «carlos», tal como se pide.
Lab6 (Practicioner) – Exploiting time-sensitive vulnerabilities
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 this race condition to delete the user carlos.». En este laboratorio debemos identificar y explotar una condición de carrera que nos permita borrar al usuario «carlos».
Comenzamos por evaluar el proceso de reseteo de contraseña. Como vemos, lanzamos una petición varias veces y el token que nos llega es diferente en todas las respuestas:


Del mismo modo, si intentamos enviar dos peticiones en paralelo, vemos que los tokens recibidos son igualmente diferentes. Sin embargo, debemos acordarnos que en lenguajes como PHP, solamente se atiende una petición por sesión. Por ello, capturaremos dos peticiones idénticas pero en sesiones diferentes y sus dos tokens CSRF asociados. Para ello, simplemente lanzamos una petición GET a «/forgot-password» sin cookies y obtenemos el valor de las nuevas cookies y del token CSRF.
Tras ello, configuramos dos peticiones POST al mismo endpoint en paralelo. Y, en base a estas dos peticiones, vemos que obtenemos un token idéntico en el cliente de correo.

Conocido esto, podemos inferir que la generación del token para dos sesiones diferentes si se envían a la vez es idéntico. Por ello, procedemos ahora a repetir el ataque, pero enviando esta vez una petición para el usuario «carlos» y otra para el usuario «wiener». Recibimos, en el cliente de correo, una URL como la siguiente para el usuario «wiener»:
https://0a1900c704f7e6f08061da6400310088.web-security-academy.net/forgot-password?user=wiener&token=fdae2ecd1b2abb4953ced7818ee7cb4fec565073
Simplemente, haciendo extrapolación, podemos saber que la URL de reseteo de contraseña del usuario «carlos» será:
https://0a1900c704f7e6f08061da6400310088.web-security-academy.net/forgot-password?user=carlos&token=fdae2ecd1b2abb4953ced7818ee7cb4fec565073
Accedemos a la URL, le cambiamos la contraseña y… ¡¡Laboratorios resueltos!!
~km0xu95