Resumen
Durante la auditoria de una aplicacion web con autenticacion en dos factores (2FA) basada en codigos OTP enviados por email, se descubrio que el sistema de rate limiting y generacion de codigos trataba la direccion de email de forma case-sensitive, mientras que el protocolo SMTP entrega los emails de forma case-insensitive (segun RFC 5321).
Esto significaba que user@target.com, User@target.com, USER@target.com y cualquier otra variacion de case generaban codigos OTP independientes con rate limits separados, pero todos llegaban al mismo buzon de correo del atacante o la victima.
Detalles tecnicos
Comportamiento normal
El flujo de autenticacion era: login con email + password, y luego un codigo OTP de 6 digitos enviado al email. El rate limit era de 3 intentos por codigo y 1 codigo cada 60 segundos por email.
POST /api/auth/request-otp HTTP/1.1
Content-Type: application/json
{ "email": "victim@company.com" }
→ 200 OK — Codigo OTP enviado (rate limit: 1/min para este email)
Bypass mediante variaciones de case
Cada variacion de case se trataba como un email distinto a nivel de aplicacion, generando un nuevo codigo OTP con su propio rate limit:
POST /api/auth/request-otp { "email": "victim@company.com" } → Codigo A
POST /api/auth/request-otp { "email": "Victim@company.com" } → Codigo B
POST /api/auth/request-otp { "email": "VICTIM@company.com" } → Codigo C
POST /api/auth/request-otp { "email": "vIctim@company.com" } → Codigo D
POST /api/auth/request-otp { "email": "viCtim@company.com" } → Codigo E
...
Todos los codigos llegan al mismo buzon: victim@company.com
Cada variacion tiene su propio rate limit de 3 intentos
Impacto en el brute force
Con un email de 6 caracteres en la parte local, existen 2^6 = 64 variaciones de case posibles. Cada una genera un codigo OTP valido con 3 intentos:
Codigo OTP: 6 digitos → 1.000.000 combinaciones posibles
Rate limit normal: 3 intentos / codigo = 0.0003% probabilidad
Con bypass:
- 64 variaciones de case × 1 codigo cada una = 64 codigos validos
- Cualquier codigo es valido para la misma cuenta
- 64 codigos × 3 intentos = 192 intentos totales
- Con 64 codigos validos de 6 digitos:
P(exito) = 1 - (999936/1000000)^192 ≈ 1.2%
Repitiendo el proceso cada 60 segundos:
- En 10 minutos: P(exito) ≈ 11.5%
- En 1 hora: P(exito) ≈ 52%
Impacto
- Bypass del mecanismo 2FA mediante multiplicacion de codigos validos
- Evasion de rate limiting al generar identidades distintas para el mismo buzon
- Acceso no autorizado a cuentas protegidas con OTP por email
- Flood del buzon de la victima con multiples emails de codigo OTP
Remediacion
- Normalizar el email a minusculas antes de cualquier operacion —
email = email.strip().lower() - Rate limiting por email normalizado — Aplicar limites sobre la version canonicalizada del email.
- Limitar codigos OTP activos — Invalidar el codigo anterior al generar uno nuevo para el mismo buzon.
- Implementar lockout progresivo — Bloquear la cuenta temporalmente tras N intentos fallidos acumulados.
- Considerar TOTP — Migrar a codigos basados en tiempo (Google Authenticator) que no dependen del email.