Respuesta rápida
2FA reduce el riesgo de credential stuffing pero no es bala de plata — cada implementación introduce su propia clase de bug. Los cinco bypasses más rentables: brute force (rate limit ausente o débil → 6 dígitos son 1M combos, vencibles en horas), response manipulation (cambiar el status del verify endpoint), fallback abuse (saltar TOTP usando recovery codes / email OTP), race conditions en setup (linkear tu authenticator a la cuenta de la víctima entre register y enroll) y session fixation (la session post-2FA hereda permisos pre-2FA). Bounties: €1.500-€15.000 — el bug class más rentable del 2026 detrás de IDOR.
TOTP brute force — el bug que no debería existir pero existe
TOTP genera 6 dígitos cada 30s. Espacio total: 1.000.000 combos. Si el server no rate-limita el endpoint de verify → vencible.
Test rápido
POST /api/auth/2fa/verify
{"code": "000000"} → 401 Invalid code
{"code": "000001"} → 401 Invalid code
{"code": "000002"} → 401 Invalid code
...
¿La respuesta cambia tras N requests? Si tras 1000 sigues recibiendo 401 (no 429 Too Many Requests), brute force es viable.
Cuenta atrás
Con TOTP de 30s y rate de 100 req/s contra 1M combos → media de 5000s ≈ 1.5h. Si el rate es 500/s (paralelo) → 17min. Si el rate es 10/s → casi 14h pero sigue vencible.
Asumiendo que el código cambia cada 30s, necesitas resincronizar tras cada ventana — pero como el código sigue siendo válido durante esos 30s, en cualquier batch tienes la oportunidad de hit.
Tools
# turbo-intruder (Burp extension) — single-packet HTTP/2 attack
# Permite ~30 requests/segundo con un solo paquete TCP
# turbo-intruder script
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=5, requestsPerConnection=100, engine=Engine.HTTP2)
for i in range(1000000):
code = str(i).zfill(6)
engine.queue(target.req, code)
def handleResponse(req, interesting):
if req.status != 401: # 200 = bypass
table.add(req)
<!-- PAYWALL -->[!warning] No quemes la cuenta de tu test user Brute force genera muchísimos logs. Avisa al program antes. Algunos consideran "denial of service against own account" out of scope.
Response manipulation — el €5000 fácil
El client trustea la respuesta del server. Si la app es SPA y verify retorna {"success": true, "redirect": "/dashboard"} para válido y {"success": false} para inválido — manipular la respuesta en Burp engaña al frontend.
Patrón
POST /api/auth/2fa/verify
{"code": "000000"}
← Response (interceptada en Burp):
HTTP/1.1 401 Unauthorized
{"success": false}
← cambia a
HTTP/1.1 200 OK
{"success": true, "redirect": "/dashboard"}
Si el frontend lee success para decidir si redirigir + guardar el token → entras al dashboard. Funciona si:
- El server emite la cookie de sesión antes del 2FA (anti-pattern frecuente).
- La autenticación final está en el frontend (SPA mal implementada).
Status code only
Algunos clients miran solo status === 200:
HTTP/1.1 401 Unauthorized → cambia a HTTP/1.1 200 OK
Frontend redirige.
Patrón "code accepted" en endpoint distinto
Algunas apps validan 2FA en /verify y luego llaman a /finalize-login con el token de la sesión pre-2FA. Si /finalize-login no verifica que /verify haya pasado (asume que el frontend lo hizo) → call directo a /finalize-login salta 2FA:
POST /api/auth/finalize-login
Cookie: pre_2fa_session=XXX
← 200 OK + cookie de sesión post-2FA
Fallback abuse — el 2FA del 2FA
Casi todas las apps tienen fallbacks por si el user pierde el authenticator: backup codes, OTP por email/SMS, security questions. Cada fallback es una superficie nueva.
OTP por email — débil
Si TOTP requiere brute force de 1M combos, OTP por email suele ser 4-6 dígitos pero sin rate limit estricto porque "el atacante necesita el email":
POST /api/auth/2fa/send-email-otp
{"userId": VICTIMA}
← email enviado
POST /api/auth/2fa/verify-email-otp
{"code": "0000"} ← brute force 4 dígitos = 10K combos
{"code": "0001"}
...
Especialmente sangriento si el OTP no expira o expira tras horas — tienes ventana grande.
Recovery codes — leak en setup
Cuando enrolla 2FA, el server muestra 10 backup codes. Si el endpoint que los genera tiene IDOR:
GET /api/auth/2fa/recovery-codes/USERID
← idealmente solo USERID==me, pero IDOR a otros
→ Lista de 10 codes que bypassean TOTP para ese user.
Security questions
Endpoint de "olvidé mi 2FA" pide questions:
- "What's your mother's maiden name?" → OSINT / LinkedIn
- "What's your first pet's name?" → frecuentemente en social media
- Algunas apps tienen el question predefinido ("First school?") → respuestas comunes en wordlist
Race condition en 2FA setup
Cuando un user enrolla 2FA, hay una ventana entre "muestra QR code" y "verifica primer código". Si el server permite múltiples enrollments paralelos:
Patrón
Atacante (con sesión robada por XSS/credential stuff, sin 2FA aún):
POST /api/auth/2fa/setup → QR code 1 (secret A)
POST /api/auth/2fa/setup → QR code 2 (secret B) ← sobrescribe el secret?
POST /api/auth/2fa/setup → QR code 3 (secret C)
Si el último request sobrescribe el secret en DB sin invalidar los anteriores:
- Víctima escanea QR 1, enrolla TOTP normalmente.
- Pero el secret final en DB es C (controlado por atacante).
- Atacante genera código con secret C → entra como víctima.
Bounty real documentado: €3.500 (fintech).
Setup + verify split
Algunas apps separan setup (genera secret + persistencia inmediata) de verify (confirma que el user lo escaneó). Si setup persiste el secret antes de verify → atacante con sesión válida puede ejecutar setup en cuenta ajena vía IDOR en userId body:
POST /api/auth/2fa/setup
{"userId": VICTIMA, "secret": "ATACANTE_SECRET"}
← secret guardado para víctima
Próximo login de víctima → pide 2FA → atacante tiene el secret → entra.
Session fixation post-2FA
Anti-pattern frecuente: la sesión emitida pre-2FA (tras password) sigue siendo válida tras 2FA. Si el server no rota el session token tras verify:
Patrón
- Atacante captura sesión pre-2FA de víctima (XSS, sniffing en HTTP, etc.).
- Víctima completa 2FA normalmente.
- Atacante usa la misma sesión pre-2FA → ahora es post-2FA con todos los permisos.
Fix correcto: rotar session token tras verify (igual que tras password reset).
Cookie scope confusion
Algunas apps emiten dos cookies: session (post-password) y session_2fa (post-2FA). Si el backend solo chequea session para endpoints "públicos" pero asume que el 2FA está validado para todo → bypass parcial.
Magic link / passwordless bypass 2FA
Apps con magic link como alternative auth method frecuentemente saltan 2FA porque "el email es factor". Pero si el email no requiere 2FA → atacante con acceso al email gana incluso si la app principal tiene 2FA.
Chain
1. Atacante envía magic link a evil-controlled-email (vía IDOR de profile update)
2. Magic link → login completo, salta 2FA
3. Cambia 2FA settings, password, etc.
Default credentials en backup codes
Algunas apps generan backup codes con patrones predecibles:
- 8 dígitos derivados del
userIdcon seed estático. - Hash del
usernametruncado. - "BACKUP-00001" secuencial por user.
Test: enrolla 2FA en dos cuentas test, compara backup codes. ¿Hay patrón o son random reales?
Hunting checklist
- Rate limit en verify endpoint — ¿1000 requests sin 429?
- Response manipulation: prueba modificar status + body para forzar success.
- Endpoint /finalize-login o equivalente — ¿se puede llamar sin pasar /verify?
- Fallbacks: OTP email/SMS (rate limit, expiración), recovery codes (IDOR de listado), security questions (predecibles/OSINT).
- Race condition en /setup: múltiples requests paralelos → ¿sobrescribe secret?
- IDOR en /setup:
userIdbody — ¿puedo enrollar 2FA en cuenta ajena? - Session rotation tras verify: el token cambia o sigue igual?
- Magic link / passwordless que saltan 2FA.
- Backup codes con patrones predecibles (compara entre cuentas).
- Cookie post-2FA validada en TODOS los endpoints sensibles (no solo el primer).
- Documenta el bypass con request mínimo + impact: ATO vs persistence vs role escalation.
Labs relacionados
Practica TOTP brute force, response manipulation, fallback abuse y race conditions de setup en labs de 2FA y Auth.
Practica esto en un lab
0 Click Ato Otp Brute Force
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Hay un payload extra al final
El bypass de 2FA via session fixation + IDOR del setup endpoint que da control completo sin nunca conocer el código original. Reportado a 3 fintech con bounties combinados de €8000.
5 €/mes · cancela cuando quieras
Artículos relacionados
CSRF (Cross-Site Request Forgery) — explicado completo con bypasses
CSRF: cómo se explota, defensas comunes (tokens, SameSite, Origin), bypasses (method change, JSON, double-submit, content-type) y dónde buscarlo en cualquier app.
0-click Account Takeover — OTP brute force + Email Normalization
Dos fallos por separado parecen menores. Juntos, te dan ATO completo conociendo solo el email. Bounty real: €560 y 12 minutos de explotación.
JWT — vulnerabilidades, bypasses y manipulación de claims
alg=none, RS256→HS256 confusion, kid SQLi/path traversal, jku spoofing, secret cracking con hashcat. Cómo cazar JWTs mal verificados.