hacktricks/pentesting-web/race-condition.md

19 KiB

Condición de Carrera


Utiliza Trickest para construir y automatizar fácilmente flujos de trabajo con las herramientas comunitarias más avanzadas del mundo.
Obtén acceso hoy mismo:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Explotando la Condición de Carrera

El principal problema de abusar de las CC es que necesitas que las solicitudes se procesen en paralelo con una diferencia de tiempo muy corta (generalmente >1ms). En la siguiente sección, se proponen diferentes soluciones para hacer esto posible.

Ataque de un solo paquete

HTTP2 permite enviar 2 solicitudes en una sola conexión TCP (mientras que en HTTP/1.1 deben ser secuenciales).
El uso de un solo paquete TCP elimina por completo el efecto del jitter de la red, por lo que esto claramente tiene potencial para ataques de condición de carrera también. Sin embargo, dos solicitudes no son suficientes para un ataque de carrera confiable gracias al jitter del lado del servidor - variaciones en el tiempo de procesamiento de la solicitud de la aplicación causadas por variables incontrolables como la contención de la CPU.

Pero, utilizando la técnica de 'sincronización del último byte' de HTTP/1.1, es posible enviar previamente la mayor parte de los datos reteniendo un fragmento pequeño de cada solicitud y luego 'completar' 20-30 solicitudes con un solo paquete TCP.

Para enviar previamente la mayor parte de cada solicitud:

  • Si la solicitud no tiene cuerpo, envía todas las cabeceras, pero no establezcas la bandera END_STREAM. Retén un marco de datos vacío con END_STREAM establecido.
  • Si la solicitud tiene un cuerpo, envía las cabeceras y todos los datos del cuerpo excepto el último byte. Retén un marco de datos que contenga el último byte.

A continuación, prepárate para enviar los marcos finales:

  • Espera 100 ms para asegurarte de que se hayan enviado los marcos iniciales.
  • Asegúrate de que TCP_NODELAY esté desactivado: es crucial que el algoritmo de Nagle agrupe los marcos finales.
  • Envía un paquete de ping para calentar la conexión local. Si no haces esto, la pila de red del sistema operativo colocará el primer marco final en un paquete separado.

Finalmente, envía los marcos retenidos. Deberías poder verificar que llegaron en un solo paquete utilizando Wireshark.

{% hint style="info" %} Ten en cuenta que esto no funciona para archivos estáticos en ciertos servidores, pero como los archivos estáticos no son relevantes para los ataques de condición de carrera, esto no es importante. {% endhint %}

Utilizando esta técnica, puedes hacer que 20-30 solicitudes lleguen al servidor simultáneamente, sin importar el jitter de la red:

Adaptándose a la arquitectura objetivo

Vale la pena señalar que muchas aplicaciones se encuentran detrás de un servidor frontal, y estos pueden decidir reenviar algunas solicitudes a través de conexiones existentes hacia el backend, y crear conexiones nuevas para otras.

Como resultado, es importante no atribuir el tiempo inconsistente de las solicitudes al comportamiento de la aplicación, como mecanismos de bloqueo que solo permiten que un solo hilo acceda a un recurso a la vez. Además, el enrutamiento de solicitudes del servidor frontal se realiza a menudo por conexión, por lo que es posible que puedas suavizar el tiempo de las solicitudes realizando un calentamiento de conexión en el lado del servidor, enviando algunas solicitudes inconsecuentes a través de tu conexión antes de realizar el ataque.

Ten en cuenta que PHP bloquea el sessionid de forma predeterminada, por lo que debes usar una sesión separada para cada solicitud en tu lote o se procesarán de forma secuencial.

{% hint style="warning" %} Para obtener más información sobre esta técnica, consulta el informe original en https://portswigger.net/research/smashing-the-state-machine {% endhint %}

Ejemplos

Puedes ver un ejemplo sencillo de cómo utilizar esto en Turbo Intruder en https://github.com/PortSwigger/turbo-intruder/blob/master/resources/examples/race-single-packet-attack.py.

También está disponible en Repeater a través de la nueva opción 'Enviar grupo en paralelo' en Burp Suite.

BF en bruto

Antes de la investigación anterior, estos eran algunos payloads utilizados que simplemente intentaban enviar los paquetes lo más rápido posible para causar una RC.

  • Turbo Intruder
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)

def handleResponse(req, interesting):
table.add(req)
  • Python - asyncio

La biblioteca asyncio de Python es una biblioteca de programación asíncrona que permite escribir código concurrente utilizando la sintaxis async/await. Proporciona una forma sencilla de escribir programas que realizan operaciones de entrada/salida de manera eficiente y no bloqueante.

La programación asíncrona es especialmente útil en situaciones en las que se necesita realizar múltiples tareas simultáneamente, como en el caso de las pruebas de penetración web. Una técnica común utilizada en las pruebas de penetración web es la condición de carrera, que aprovecha las condiciones de ejecución simultánea para obtener acceso no autorizado a recursos protegidos.

Una condición de carrera ocurre cuando dos o más procesos compiten por acceder o modificar un recurso compartido al mismo tiempo. En el contexto de las pruebas de penetración web, esto puede ocurrir cuando múltiples solicitudes se envían al servidor al mismo tiempo y se ejecutan en paralelo.

Para aprovechar una condición de carrera en una aplicación web, es necesario identificar un punto de vulnerabilidad donde se pueda producir una condición de carrera. Esto puede ser, por ejemplo, un proceso de autenticación o un proceso de actualización de datos.

Una vez identificado el punto de vulnerabilidad, se pueden enviar múltiples solicitudes simultáneas al servidor para intentar explotar la condición de carrera. Esto se puede lograr utilizando la biblioteca asyncio de Python para enviar solicitudes asíncronas y controlar el flujo de ejecución.

Al utilizar asyncio, se pueden enviar múltiples solicitudes al servidor al mismo tiempo y esperar las respuestas de manera eficiente sin bloquear el hilo principal de ejecución. Esto permite aprovechar las condiciones de carrera y realizar pruebas de penetración web de manera más efectiva.

En resumen, la biblioteca asyncio de Python es una herramienta poderosa para realizar pruebas de penetración web y aprovechar las condiciones de carrera. Al utilizar la programación asíncrona, se pueden enviar múltiples solicitudes simultáneas y controlar el flujo de ejecución de manera eficiente.

import asyncio
import httpx

async def use_code(client):
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text

async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))

# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)

# Print results
for r in results:
print(r)

# Async2sync sleep
await asyncio.sleep(0.5)
print(results)

asyncio.run(main())
  • Intruso: Envía la solicitud al Intruso, establece el número de hilos en 30 dentro del menú de Opciones, selecciona como carga útil Cargas útiles nulas y genera 30.

Metodología de RC

Impactos de RC

Desbordamiento de límite

Este es el tipo más básico de condición de carrera donde se encuentran vulnerabilidades que limitan el número de veces que se puede realizar una acción. Por ejemplo, usar el mismo código de descuento varias veces en una tienda web. Un ejemplo muy sencillo se puede encontrar en este informe o en este error.

Subestados ocultos

Otra condición de carrera más complicada explotará subestados en el estado de la máquina que podrían permitir a un atacante abusar de estados a los que nunca se suponía que debía tener acceso, pero hay una pequeña ventana para que el atacante acceda a ellos.

  1. Predecir subestados ocultos e interesantes potenciales

El primer paso es identificar todos los puntos finales que ya sea escriben en él o leen datos de él y luego utilizan esos datos para algo importante. Por ejemplo, los usuarios pueden almacenarse en una tabla de base de datos que se modifica mediante el registro, la edición de perfiles, la iniciación de restablecimiento de contraseña y la finalización del restablecimiento de contraseña.

Podemos utilizar tres preguntas clave para descartar los puntos finales que es poco probable que causen colisiones. Para cada objeto y los puntos finales asociados, pregunte:

1) ¿Cómo se almacena el estado?

Los datos que se almacenan en una estructura de datos persistente en el servidor son ideales para la explotación. Algunos puntos finales almacenan su estado completamente en el lado del cliente, como los restablecimientos de contraseña que funcionan enviando un JWT por correo electrónico; estos se pueden omitir de forma segura.

Las aplicaciones a menudo almacenan algún estado en la sesión del usuario. Estos a menudo están algo protegidos contra subestados, pero más sobre eso más adelante.

2) ¿Estamos editando o agregando?

Las operaciones que editan datos existentes (como cambiar la dirección de correo electrónico principal de una cuenta) tienen un amplio potencial de colisión, mientras que las acciones que simplemente agregan datos existentes (como agregar una dirección de correo electrónico adicional) es poco probable que sean vulnerables a algo que no sea ataques de desbordamiento de límite.

3) ¿En qué se basa la operación?

La mayoría de los puntos finales operan en un registro específico, que se busca utilizando una "clave", como un nombre de usuario, un token de restablecimiento de contraseña o un nombre de archivo. Para un ataque exitoso, necesitamos dos operaciones que utilicen la misma clave. Por ejemplo, imagine dos implementaciones plausibles de restablecimiento de contraseña:

  1. Buscar pistas

En este punto, es hora de lanzar algunos ataques de RC sobre los puntos finales interesantes potenciales para tratar de encontrar resultados inesperados en comparación con los regulares. Cualquier desviación de la respuesta esperada, como un cambio en una o más respuestas, o un efecto de segundo orden como diferentes contenidos de correo electrónico o un cambio visible en su sesión, podría ser una pista que indique que algo está mal.

  1. Demostrar el concepto

El último paso es demostrar el concepto y convertirlo en un ataque viable.

Cuando envíes un lote de solicitudes, es posible que descubras que un par de solicitudes tempranas desencadena un estado final vulnerable, pero las solicitudes posteriores lo sobrescriben/invalidan y el estado final no se puede explotar. En este escenario, querrás eliminar todas las solicitudes innecesarias; dos deberían ser suficientes para explotar la mayoría de las vulnerabilidades. Sin embargo, reducir a dos solicitudes hará que el ataque sea más sensible al tiempo, por lo que es posible que debas intentar el ataque varias veces o automatizarlo.

Estudios de casos de subestados ocultos

Pagar y agregar un artículo

Consulta este laboratorio para ver cómo pagar en una tienda y agregar un artículo adicional por el que no tendrás que pagar.

Confirmar otros correos electrónicos

La idea es verificar una dirección de correo electrónico y cambiarla al mismo tiempo para averiguar si la plataforma verifica la nueva dirección cambiada.

Cambiar el correo electrónico a 2 direcciones de correo electrónico

Según este informe, Gitlab era vulnerable a un secuestro de esta manera porque podría enviar el token de verificación de correo electrónico de un correo electrónico al otro correo electrónico.

Persistencia eterna de OAuth2

Existen varios proveedores de OAuth. Estos servicios te permitirán crear una aplicación y autenticar a los usuarios que el proveedor ha registrado. Para hacerlo, el cliente deberá permitir que tu aplicación acceda a algunos de sus datos dentro del proveedor de OAuth.
Entonces, hasta aquí solo un inicio de sesión común con Google/LinkedIn/GitHub... donde se te muestra una página que dice: "La aplicación <InsertCoolName> quiere acceder a tu información, ¿quieres permitirlo?"

Condición de carrera en authorization_code

El problema aparece cuando lo aceptas y automáticamente envía un authorization_code a la aplicación maliciosa. Luego, esta aplicación abusa de una condición de carrera en el proveedor de servicios de OAuth para generar más de un AT/RT (Authentication Token/Refresh Token) a partir del authorization_code para tu cuenta. Básicamente, abusará del hecho de que has aceptado que la aplicación acceda a tus datos para crear varias cuentas. Luego, si dejas de permitir que la aplicación acceda a tus datos, se eliminará un par de AT/RT, pero los demás seguirán siendo válidos.

Condición de carrera en Refresh Token

Una vez que hayas obtenido un RT válido, podrías intentar abusar de él para generar varios AT/RT e incluso si el usuario cancela los permisos para que la aplicación maliciosa acceda a sus datos, varios RT seguirán siendo válidos.

Referencias

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥


Utiliza Trickest para construir y automatizar flujos de trabajo impulsados por las herramientas comunitarias más avanzadas del mundo.
Obtén acceso hoy mismo:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}