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
- Añadir al carrito el producto objetivo (en este lab, el "l33t leather jacket").
- Añadir un cupón al carrito.
- Capturar la request
POST /cart/coupony enviarla a Repeater. - 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:
- Capturar
POST /cart/coupontras añadir el cupón. - Enviarla a Repeater y abrir un grupo de N tabs con la misma request.
- Disparar el envío en paralelo (single-packet).
- Verificar en
/cartque 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 UPDATEsobre 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.