← Writeups

Limit overrun race conditions

Explotación de una race condition de límite con single-packet attack: la validación del cupón se hace antes del uso, permitiendo aplicar el mismo descuento múltiples veces en paralelo.

Target Overview & Vulnerability Summary

La tienda permite aplicar un cupón de descuento al carrito. La validación del cupón (una sola aplicación por usuario) no es atómica respecto al uso del descuento: existe una ventana entre "verificar que el cupón no se usó" y "marcarlo como usado". Disparando múltiples requests POST /cart/coupon en paralelo, todas atraviesan la validación antes de que ninguna actualice el estado, aplicando el descuento N veces.

Campo Valor
Severidad Alta (abuso financiero: descuento aplicado múltiples veces / bypass de límite)
Endpoint vulnerable POST /cart/coupon
Root cause Check-then-act no atómico sobre el uso del cupón (TOCTOU)

Technical Breakdown & Exploitation Flow

1. Preparación

  1. Añadir al carrito el producto objetivo (en este lab, el "l33t leather jacket").
  2. Añadir un cupón al carrito.
  3. Capturar la request POST /cart/coupon y enviarla a Repeater.
  4. Eliminar el cupón del carrito para que el carrito quede en estado inicial (cada aplicación del cupón debe poder validarse contra un carrito apto).

2. Ráfaga paralela con single-packet attack

En Repeater se crea un grupo de tabs con la request POST /cart/coupon y se envía la ráfaga en paralelo. El single-packet attack (Burp's Turbo Intruder / Repeater "Send group in parallel" sobre HTTP/2) coloca las requests en un mismo paquete TCP para que lleguen al servidor en la misma ventana de tiempo:

POST /cart/coupon HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
Content-Type: application/x-www-form-urlencoded

csrf=<CSRF_TOKEN>&coupon=<COUPON_CODE>

Pasos:

  1. Capturar POST /cart/coupon tras añadir el cupón.
  2. Enviarla a Repeater y abrir un grupo de N tabs con la misma request.
  3. Disparar el envío en paralelo (single-packet).
  4. Verificar en /cart que el descuento se aplicó múltiples veces (total inferior al precio correcto, o el cupón se reutilizó en varias órdenes).

3. Verificación

Consultando el carrito tras la ráfaga, el descuento del cupón aparece aplicado por cada request que atravesó la ventana de carrera, reduciendo el total de forma abusiva.

Root Cause Analysis

El patrón defectuoso es check-then-act sobre el estado del cupón:

# Implementación vulnerable
def apply_coupon(request):
    coupon = db.get_coupon(request.coupon)
    if coupon.used:                    # 1. check
        return "Coupon already used"
    discount = compute_discount(coupon)
    db.mark_coupon_used(coupon)        # 2. act
    apply_discount(cart, discount)

Entre el check (1) y el act (2) hay una ventana de carrera: requests concurrentes leen used == False antes de que ninguna ejecute el mark_coupon_used, y todas aplican el descuento. La serialización de esta sección crítica es la única forma de cerrar el TOCTOU.

Secure Remediation

  • Hacer la operación atómica a nivel de base de datos, validando y marcando en un solo paso:
-- Marcar el cupón solo si aún no fue usado
UPDATE coupons SET used = TRUE
WHERE code = :code AND used = FALSE
RETURNING id;
# El UPDATE retorna fila solo si el cupón estaba sin usar
if db.execute(mark_coupon_used_sql, code=request.coupon).rowcount == 1:
    apply_discount(cart, compute_discount(coupon))
else:
    return "Coupon already used"
  • Alternativas equivalentes: SELECT ... FOR UPDATE sobre la fila del cupón (lock a nivel de fila) o un contador atómico (INCR/CAS) en el almacén de estado.
  • Aplicar rate limiting/límite de aplicaciones por sesión y registrar todas las aplicaciones del cupón para detección de abuso.