← Volver al buscador
Seguridad web Intermedio

Password reset — Puny code, header injection, email normalization, host header

Bypass de password reset con Unicode confusables, Host header injection, email normalization (gmail + alias), token leakage en referer, response manipulation.

Respuesta rápida

Password reset es el endpoint más auditado y aún así el más roto del bug bounty. La razón: cualquier "feature" del flow (case insensitivity, internationalization, alias support) crea collision space. Cuatro bypasses rentables: Puny code / IDN homograph (registrar víctima@gmail.com con cirílico → recibe el reset email), Host header injection (cambia el link del email para apuntar a tu dominio), email normalization (gmail dot/plus aliases tratados como distintos en register pero iguales en reset), token leakage (referer, response body, log). Bounties: €1.500-€12.000 con ATO chain.


El flow estándar y dónde rompe

1. User submits email → POST /auth/forgot { email }
2. Server busca user por email
3. Server genera token reset (random 32-128 chars)
4. Server guarda { userId, token, expiresAt } en DB
5. Server envía email con link: https://target.com/reset?token=XXX
6. User clica → GET /reset?token=XXX → form de nuevo password
7. POST /reset { token, newPassword } → password updated

Cada paso es atacable.


Puny code / IDN homograph attack

Unicode permite registrar victim@gmail.com con caracteres cirílicos que se ven idénticos al ojo humano pero son diferentes para el sistema. Combinado con email normalization downstream → ATO.

El truco

Las letras latinas y cirílicas comparten glifos:

LatínCirílicoPunto código
aаU+0061 vs U+0430
eеU+0065 vs U+0435
iіU+0069 vs U+0456
oоU+006F vs U+043E
pрU+0070 vs U+0440
cсU+0063 vs U+0441

Escenario de exploit

  1. Víctima tiene cuenta gorka@target.com.
  2. Atacante registra cuenta con gоrka@target.com (la о es cirílica U+043E).
  3. Para registro pasa porque la app trata strings byte-exact → es un email "diferente".
  4. Atacante solicita password reset de la VÍCTIMA gorka@target.com.
  5. Si la app normaliza Unicode en el lookup (NFKC, Punycode lookup) pero no en el storage → encuentra al user atacante → manda email al atacante.

Variante: mailbox provider acepta ambos

Algunos mail providers (Gmail) rechazan caracteres cirílicos en mailbox local-part — pero ProtonMail, Yandex, custom domain mailservers a veces sí los aceptan.

gorka@yandex.com
gоrka@yandex.com    ← cirílico, Yandex acepta ambos como mailboxes separados

Si target.com no normaliza pero el provider sí → atacante recibe correos de la víctima.

Detección

Test register con victim+a@target.com y victim+ɑ@target.com (latin alpha). ¿Acepta ambos como distintos? Si sí → posible chain.


Email normalization — Gmail dots and plus aliases

Gmail trata usuario@gmail.com == us.uario@gmail.com == usu.ar.io@gmail.com == usuario+anything@gmail.com. Casi nadie más lo hace.

Chain común

  1. Víctima registra usuario@gmail.com en target.com.
  2. Atacante registra us.uario@gmail.com en target.com (Gmail entrega ambos a la misma inbox; target.com los trata como distintos).
  3. Atacante solicita reset password de usuario@gmail.com.
  4. Email llega a usuario@gmail.com (víctima).
  5. Pero si la app normaliza al hacer reset (u.s.uario → usuario) y el lookup encuentra al atacante → atacante recibe reset → ATO.

Plus aliases

usuario@gmail.com
usuario+atacante@gmail.com   ← misma inbox para Gmail; misma cuenta para apps que normalizan

App que acepta plus alias en register pero lo strippea en reset → atacante registra usuario+evil@gmail.com, pide reset a usuario@gmail.com (sin alias), token va a la víctima... no, espera, va a la víctima. El bug real es al revés: registrar usuario+evil@gmail.com, pedir reset de usuario+evil@gmail.com (que está en tu inbox como usuario) → token llega a la víctima.

Más exacta: registra usuario+atacante@gmail.com → eres usuario para la app si normaliza → pides reset → email va a usuario+atacante@gmail.com (tu inbox) → ATO de la víctima usuario@gmail.com.

<!-- PAYWALL -->

Host header injection

Quizás el bypass más viejo y aún más explotable. La app construye el link del reset usando el Host header del request:

const resetLink = `https://${req.headers.host}/reset?token=${token}`;
sendEmail(user.email, resetLink);

Atacante manda:

POST /auth/forgot HTTP/1.1
Host: evil.tld
Content-Type: application/json

{"email": "victim@target.com"}

Si el server confía en el Host header del request → email a víctima contiene:

Click here to reset: https://evil.tld/reset?token=XXX

Víctima clica → token va a evil.tld (en path o query) → atacante usa el token en target.com/reset → ATO.

Variantes

  • X-Forwarded-Host: X-Forwarded-Host: evil.tld — algunas frameworks priorizan este header.
  • Host: target.com:@evil.tld: parser confuso, algunos ven target.com (legit), email construido con evil.tld.
  • Double Host header: Host: target.com\r\nHost: evil.tld — algunos parsers leen el último, otros el primero.

Detección

Inspecciona el email que recibes con reset. ¿El dominio del link viene del input HTTP (Host header) o está hardcoded? Cambia Host y observa.


Token leakage

Via Referer

Si el link del email lleva a una página que carga assets de terceros (CDN, analytics, fonts), los terceros reciben Referer: target.com/reset?token=XXX.

Detección: visita el link de reset, captura todas las requests en Burp/DevTools → ¿alguna sale a otro dominio con Referer que incluye ?token=?

Fix correcto: Referrer-Policy: strict-origin en la página de reset.

Via response body

Algunos endpoints, en respuesta a la solicitud de reset (POST /forgot), incluyen el token:

{
  "message": "Email sent",
  "debug": {
    "token": "XXX"
  }
}

Endpoint dev olvidado en producción. Test con Burp Repeater.

Via logs / monitoring

Si la app loggea full URL en logs (Datadog, Sentry, custom logs) y los logs son accesibles vía vulnerable endpoint → exfil.

Via JS bundle

Algunos SPAs incluyen el token en window state al renderizar el reset page (hydration data). Si la página es indexada por crawler / cacheada / fugada por error → exfil.


Token reuse / not invalidated

Reuse tras uso

El token debería ser válido para una sola operación. Bug frecuente: tras usar el token, no se invalida. Atacante:

  1. Víctima resetea password con token X → OK.
  2. Atacante (si interceptó X via cualquier método) usa X otra vez → password reset otra vez.

Reuse tras login

Si la víctima loguea con nuevo password y luego solicita ANOTHER reset por error → el primer token sigue válido. Cualquiera con el primer token tiene acceso permanente.

Token no expira

Token válido por 24h es estándar — pero algunas apps no expiran nunca. Token leaked hace 6 meses sigue válido.


Response manipulation — el bypass tonto que funciona

Endpoint POST /reset retorna {"success": true} o {"success": false}. Frontend SPA redirige basándose en el success. Intercepta:

HTTP/1.1 400 Bad Request
{"success": false, "error": "Invalid token"}
                                  ← cambia a:
HTTP/1.1 200 OK
{"success": true}

→ Frontend redirige a "password changed", muestra login. Pero el password no se cambió en realidad. Bug no útil para ATO porque el password real no cambió.

Salvo que el endpoint /finalize-reset también esté roto: si reset completa la sesión + emite cookie de auth via response que también es manipulable → ATO.


Account linking / unverified email takeover

Algunas apps permiten password reset incluso si el email no está verificado (anti-pattern). Si la app permite cambiar email vía API:

  1. Atacante registra cuenta nueva.
  2. Cambia email a victim@target.com (sin verificación).
  3. Solicita password reset → email a victim@target.com.
  4. Víctima ve email "extraño" pero clica → reset el password del atacante (que ahora controla la cuenta linkeada a victim's email).

Variante: si target.com permite multiple emails por cuenta + verificación débil + reset por cualquier email asociado → reset por email atacante-controlado.


Hunting checklist

  • Host header injection: cambia Host / X-Forwarded-Host y observa email recibido.
  • Puny code: registra victim@target.com con cirílico (NIST de homographs).
  • Gmail normalization: prueba user@gmail.com vs u.ser@gmail.com vs user+x@gmail.com.
  • Token en response body del POST /forgot — endpoint dev olvidado.
  • Token en referer de assets externos en la página de reset.
  • Token reuse: usa el mismo token 2 veces, ¿ambos pasan?
  • Token expiration: deja token 24h+, ¿sigue válido?
  • Email change without verify + reset → ATO chain.
  • Response manipulation: status / body / cookie del endpoint final.
  • Rate limit en /forgot: si no hay, brute force token de 4-6 dígitos.
  • Token entropy: si el token es predecible (timestamp + userId) → forge.
  • Documenta: el chain mínimo + impact (ATO definitivo vs window de horas).

Labs relacionados

Practica Host header injection, Puny code y normalization attacks en labs de password reset.

Practica esto en un lab

Password Reset

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta
Premium · 1 técnica más

Hay un payload extra al final

El bypass de password reset usando Unicode collisions en `i` cyrillic + Gmail normalization para tomar accounts ajenas sin nunca tocar su email real.

Desbloquear

7,99 €/mes · cancela cuando quieras

Artículos relacionados

Gratis€560

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.

14 minaccount-takeoverotprate-limiting
Leer artículo
Gratis

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.

12 mincsrfauthsession
Leer artículo
Gratis

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.

13 minjwtauthsession
Leer artículo
Nivel intermedioToda la Academy

Contenido educativo de Kerb3r0ps. Úsalo solo sobre sistemas para los que tengas permiso. Volver al buscador.