# Condición de Carrera
\
Utiliza [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) para construir y **automatizar flujos de trabajo** con las herramientas comunitarias **más avanzadas** del mundo.\
Obtén Acceso Hoy:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
Aprende hacking en AWS de cero a héroe conhtARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
* Si quieres ver a tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF**, consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Consigue el [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sigue** a **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de GitHub de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
## Explotando RC
El principal problema al abusar de los RC es que necesitas que las solicitudes se procesen en paralelo con una diferencia de tiempo muy corta (usualmente >1ms). En la siguiente sección, se proponen diferentes soluciones para hacer esto posible.
### Ataque de un solo paquete (HTTP/2) / Sincronización del último byte (HTTP/1.1)
HTTP2 permite enviar **2 solicitudes en una sola conexión TCP** (mientras que en HTTP/1.1 tienen que ser secuenciales).\
El uso de un solo paquete TCP **elimina completamente el efecto del jitter de 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 fiable** debido al **jitter del lado del servidor** - variaciones en el tiempo de procesamiento de solicitudes de la aplicación causadas por variables incontrolables como la contención de CPU.
Pero, utilizando la técnica de '**sincronización del último byte**' de HTTP/1.1 es posible pre-enviar la mayor parte de los datos reteniendo un pequeño fragmento de cada solicitud y luego 'completar' **20-30 solicitudes con un solo paquete TCP**.
Para **pre-enviar la mayor parte de cada solicitud**:
* Si la solicitud no tiene cuerpo, envía todos los encabezados, pero no establezcas la bandera END\_STREAM. Retén un marco de datos vacío con la bandera END\_STREAM establecida.
* Si la solicitud tiene un cuerpo, envía los encabezados y todos los datos del cuerpo excepto el último byte y la bandera END\_STREAM. Retén un marco de datos que contenga el último byte.
A continuación, **prepárate para enviar los marcos finales**:
* Espera 100ms para asegurarte de que los marcos iniciales se han enviado.
* 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 SO colocará el primer marco final en un paquete separado.
Finalmente, envía los marcos retenidos. Deberías poder verificar que aterrizaron en un solo paquete usando Wireshark.
{% hint style="info" %}
Nota que **no funciona para archivos estáticos** en ciertos servidores, pero los archivos estáticos no son relevantes para ataques de RC.
{% endhint %}
Utilizando esta técnica, puedes hacer que 20-30 solicitudes lleguen al servidor simultáneamente - independientemente del jitter de red:
**Adaptándose a la arquitectura del objetivo**
Es importante notar que muchas aplicaciones están detrás de un servidor frontal, y estos pueden decidir reenviar algunas solicitudes a través de conexiones existentes al back-end, y crear nuevas conexiones para otras.
Como resultado, es importante no atribuir tiempos de solicitud inconsistentes 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 front-end a menudo se hace en base a una conexión por conexión, por lo que puedes suavizar el tiempo de las solicitudes realizando un calentamiento de conexión del lado del servidor - **enviando algunas solicitudes inconsecuentes por tu conexión antes de realizar el ataque** (esto es solo enviar varias solicitudes antes de comenzar el ataque real).
#### Mecanismos de bloqueo basados en sesión
Algunos frameworks intentan prevenir la corrupción accidental de datos utilizando alguna forma de **bloqueo de solicitudes**. Por ejemplo, el módulo de **manejador de sesiones nativo de PHP** solo procesa **una solicitud por sesión a la vez**.
Es extremadamente importante detectar este tipo de comportamiento ya que de lo contrario puede ocultar vulnerabilidades trivialmente explotables. Si notas que todas tus solicitudes se procesan secuencialmente, intenta enviar cada una de ellas utilizando un token de sesión diferente.
#### **Abusando de límites de tasa o recursos**
Si el calentamiento de la conexión no hace ninguna diferencia, hay varias soluciones a este problema.
Usando Turbo Intruder, puedes introducir un corto retraso del lado del cliente. Sin embargo, como esto implica dividir tus solicitudes de ataque reales en múltiples paquetes TCP, no podrás utilizar la técnica de ataque de un solo paquete. Como resultado, en objetivos con alto jitter, el ataque es poco probable que funcione de manera fiable, independientemente del retraso que establezcas.
En cambio, podrías ser capaz de resolver este problema abusando de una característica de seguridad común.
Los servidores web a menudo **retrasan el procesamiento de las solicitudes si se envían demasiadas demasiado rápido**. Al enviar un gran número de solicitudes ficticias para desencadenar intencionalmente el límite de tasa o recursos, podrías ser capaz de causar un retraso adecuado del lado del servidor. Esto hace viable el ataque de un solo paquete incluso cuando se requiere una ejecución retrasada.
{% hint style="warning" %}
Para más información sobre esta técnica, consulta el informe original en [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
{% endhint %}
#### Ejemplos de Ataque
* **Tubo Intruder - Ataque de un solo paquete HTTP2 (1 endpoint)**: Puedes enviar la solicitud a **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), puedes cambiar en la solicitud el valor que quieres forzar bruscamente por **`%s`** como en `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` y luego seleccionar **`examples/race-single-packer-attack.py`** del menú desplegable:
Si vas a **enviar diferentes valores**, podrías modificar el código con este que usa una lista de palabras del portapapeles:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
{% hint style="warning" %}
Si la web no soporta HTTP2 (solo HTTP1.1) usa `Engine.THREADED` o `Engine.BURP` en lugar de `Engine.BURP2`.
{% endhint %}
* **Tubo Intruder - Ataque de paquete único HTTP2 (Varios endpoints)**: En caso de que necesites enviar una solicitud a 1 endpoint y luego múltiples a otros endpoints para activar el RCE, puedes cambiar el script `race-single-packet-attack.py` con algo como:
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net
Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU
Content-Length: 0
'''
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
```
* También está disponible en **Repeater** a través de la nueva opción '**Enviar grupo en paralelo**' en Burp Suite.
* Para **limit-overrun** podrías simplemente añadir **la misma solicitud 50 veces** en el grupo.
* Para **connection warming**, podrías **añadir** al **inicio** del **grupo** algunas **solicitudes** a alguna parte no estática del servidor web.
* Para **retrasar** el proceso **entre** el procesamiento de **una solicitud y otra** en pasos de 2 subestados, podrías **añadir solicitudes adicionales entre** ambas solicitudes.
* Para un RC de **multi-endpoint** podrías comenzar enviando la **solicitud** que **va al estado oculto** y luego **50 solicitudes** justo después que **explotan el estado oculto**.
### Raw BF
Antes de la investigación previa, estos eran algunos payloads utilizados que simplemente intentaban enviar los paquetes lo más rápido posible para causar un RC.
* **Repeater:** Revisa los ejemplos de la sección anterior.
* **Intruder**: Envía la **solicitud** a **Intruder**, establece el **número de hilos** a **30** dentro del **menú de Opciones y,** selecciona como payload **Null payloads** y genera **30.**
* **Turbo Intruder**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=','Session=','Session=']
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**
```python
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())
```
## **Metodología RC**
### Exceso de límite / TOCTOU
Este es el tipo más básico de condición de carrera donde las **vulnerabilidades** que **aparecen** en lugares que **limitan la cantidad de veces que puedes realizar una acción**. Como usar el mismo código de descuento varias veces en una tienda web. Un ejemplo muy fácil se puede encontrar en [**este informe**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) o en [**este bug**](https://hackerone.com/reports/759247)**.**
Hay muchas variaciones de este tipo de ataque, incluyendo:
* Canjear una tarjeta de regalo varias veces
* Valorar un producto varias veces
* Retirar o transferir efectivo en exceso del saldo de tu cuenta
* Reutilizar una única solución CAPTCHA
* Evitar un límite de tasa anti-fuerza bruta
### **Subestados ocultos**
Otros RC más complicados explotarán **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 tuviera acceso**, pero hay una **pequeña ventana** para que el atacante acceda a él.
1. **Predecir subestados ocultos e interesantes potenciales**
El primer paso es identificar todos los puntos finales que escriben en él o leen datos de él y luego usan esos datos para algo importante. Por ejemplo, los usuarios pueden almacenarse en una tabla de base de datos que es modificada por el registro, ediciones de perfil, inicio de restablecimiento de contraseña y finalización de restablecimiento de contraseña.
Podemos usar tres preguntas clave para descartar puntos finales que probablemente no causen colisiones. Para cada objeto y los puntos finales asociados, pregúntate:
* **¿Cómo se almacena el estado?**
Los datos almacenados en una estructura de datos persistente del lado del servidor son ideales para la explotación. Algunos puntos finales almacenan su estado completamente del lado del cliente, como los restablecimientos de contraseña que funcionan enviando un JWT por correo electrónico; estos se pueden omitir con seguridad.
Las aplicaciones a menudo almacenan algún estado en la sesión del usuario. Estos a menudo están algo protegidos contra subestados - más sobre eso más adelante.
* **¿Estamos editando o añadiendo?**
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 se añaden a los datos existentes (como agregar una dirección de correo electrónico adicional) probablemente no sean vulnerables a nada más que ataques de exceso de límite.
* **¿En qué está basada la operación?**
La mayoría de los puntos finales operan sobre un registro específico, que se busca utilizando una 'clave', como un nombre de usuario, token de restablecimiento de contraseña o nombre de archivo. Para un ataque exitoso, necesitamos dos operaciones que usen la misma clave. Por ejemplo, imagina dos implementaciones plausibles de restablecimiento de contraseña:
2. **Buscar pistas**
En este punto es hora de **lanzar algunos ataques RC** sobre los puntos finales potencialmente interesantes 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 secundario como contenidos de correo electrónico diferentes o un cambio visible en tu sesión, podría ser una pista que indica que algo está mal.
3. **Probar el concepto**
El paso final es **probar el concepto y convertirlo en un ataque viable**.
Cuando envías un lote de solicitudes, puedes encontrar que un par de solicitudes tempranas desencadenan un estado final vulnerable, pero las solicitudes posteriores lo sobrescriben/invalidan y el estado final es inexplotable. 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 necesites intentar el ataque varias veces o automatizarlo.
### Ataques Sensibles al Tiempo
A veces es posible que no encuentres condiciones de carrera, pero las **técnicas para entregar solicitudes con un tiempo preciso** aún pueden revelar la presencia de otras vulnerabilidades.
Un ejemplo es cuando se utilizan **marcas de tiempo de alta resolución en lugar de cadenas aleatorias seguras criptográficamente** para generar tokens de seguridad.
Considera un **token de restablecimiento de contraseña que solo se aleatoriza usando una marca de tiempo**. En este caso, podría ser posible **desencadenar dos restablecimientos de contraseña para dos usuarios diferentes**, que ambos usen el **mismo token**. Todo lo que necesitas hacer es cronometrar las solicitudes para que generen la misma marca de tiempo.
{% hint style="warning" %}
Para confirmar, por ejemplo, la situación anterior, podrías simplemente pedir **2 tokens de restablecimiento de contraseña al mismo tiempo** (usando ataque de paquete único) y verificar si son **iguales**.
{% endhint %}
Revisa el [**ejemplo en este laboratorio**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities).
## Estudios de caso de subestados ocultos
### Pagar y añadir un artículo
[**Revisa este laboratorio**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) para ver cómo **pagar** en una tienda y **añadir un artículo extra** que **no necesitarás pagar**.
### Confirmar otros correos electrónicos
La idea es **verificar una dirección de correo electrónico y cambiarla por otra diferente al mismo tiempo** para averiguar si la plataforma verifica la nueva cambiada.
### Cambiar correo electrónico a 2 direcciones de correo electrónico basado en Cookie
Según [**este informe**](https://portswigger.net/research/smashing-the-state-machine) Gitlab era vulnerable a una toma de control de esta manera porque podría **enviar** el **token de verificación de correo electrónico de un correo a otro**.
También puedes revisar [**este laboratorio**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) para aprender sobre esto.
### Estados de base de datos ocultos / Evitar la confirmación
Si se utilizan **2 escrituras diferentes** para **añadir** **información** dentro de una **base de datos**, hay una pequeña porción de tiempo donde **solo se ha escrito el primer dato** dentro de la base de datos. Por ejemplo, al crear un usuario, el **nombre de usuario** y la **contraseña** podrían ser **escritos** y **luego el token** para confirmar la cuenta recién creada se escribe. Esto significa que por un pequeño tiempo el **token para confirmar una cuenta es nulo**.
Por lo tanto, **registrar una cuenta y enviar varias solicitudes con un token vacío** (`token=` o `token[]=` o cualquier otra variación) para confirmar la cuenta de inmediato podría permitir **confirmar una cuenta** donde no controlas el correo electrónico.
Revisa [**este laboratorio**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) para ver un ejemplo.
### Evitar 2FA
El siguiente pseudo-código demuestra cómo un sitio web podría ser vulnerable a una variación de ataque de carrera de esta manera:
```python
session['userid'] = user.userid
if user.mfa_enabled:
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
```
Como puedes ver, esto es de hecho una **secuencia de varios pasos dentro del lapso de una sola solicitud**. Lo más importante es que pasa por un subestado en el que el **usuario temporalmente tiene una sesión válida iniciada**, **pero la MFA aún no se está aplicando**. Un atacante podría potencialmente explotar esto enviando una solicitud de inicio de sesión junto con una solicitud a un punto final sensible y autenticado.
### Persistencia eterna de OAuth2
Hay varios [**proveedores de OAuth**](https://en.wikipedia.org/wiki/List_of_OAuth_providers). Estos servicios te permitirán crear una aplicación y autenticar usuarios que el proveedor tiene registrados. Para hacerlo, el **cliente** necesitará **permitir que tu aplicación** acceda a algunos de sus datos dentro del **proveedor de OAuth**.\
Hasta aquí, solo un inicio de sesión común con Google/LinkedIn/GitHub... donde te aparece una página que dice: "_La aplicación \ quiere acceder a tu información, ¿quieres permitirlo?_"
#### Condición de carrera en `authorization_code`
El **problema** aparece cuando lo **aceptas** y automáticamente se 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** (_Token de Autenticación/Token de Actualización_) a partir del **`authorization_code`** de tu cuenta. Básicamente, abusará del hecho de que has aceptado que la aplicación acceda a tus datos para **crear varias cuentas**. Entonces, si **dejas de permitir que la aplicación acceda a tus datos, se eliminará un par de AT/RT, pero los otros 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** y **aunque el usuario cancele los permisos** para que la aplicación maliciosa acceda a sus datos, **varios RT seguirán siendo válidos**.
## **RC en WebSockets**
En [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) puedes encontrar un PoC en Java para enviar mensajes de WebSocket en **paralelo** para abusar de **Condiciones de Carrera también en Web Sockets**.
## Referencias
* [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247)
* [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html)
* [https://hackerone.com/reports/55140](https://hackerone.com/reports/55140)
* [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
* [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions)
Aprende hacking en AWS de cero a héroe conhtARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
* Si quieres ver a tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Consigue el [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección de [**NFTs exclusivos**](https://opensea.io/collection/the-peass-family)
* **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de Telegram**](https://t.me/peass) o **sígueme** en **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de GitHub** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
\
Usa [**Trickest**](https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks) para construir y **automatizar flujos de trabajo** con las herramientas comunitarias **más avanzadas**.\
Obtén acceso hoy:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}