OAuth & Authentication Flaws
What it is
Two related roads to the same outcome — full account takeover. One: broken password reset, email change, OTP verification or session handling. Two: manipulating an OAuth
redirect_uri parameter so the authorization code gets redirected to a domain you control instead of the legitimate app.Why it matters
Account takeover is consistently one of the highest-value bug classes: it's rarely 'just' one field or one resource, it's the whole account, including everything downstream of it (billing, data, other integrations).
Where to look
- Password reset, email/phone change, OTP verification, magic links, session management
- Any
,/authorize
,/oauth/authorize
endpoint with/connect/authorize
/client_id
/redirect_uri
parametersstate
How I test it
- Map every 'alternate path' in the auth flow: forgot password, no access to email, resend code, expired code, login with Google/SSO.
- Test reset-token reuse across sessions, reusing a token from one account on another, changing email without confirmation, changing password without the current one.
- For OAuth: capture the request containing
, decode it, and try subdomain swaps, theredirect_uri=
trick (@
), fragment tricks (https://trusted.com@attacker.com
), and single-vs-double URL encoding mismatches — the goal is a value the server validates as legitimate but the browser resolves to a different host.https://attacker.com#@trusted.com - Confirm end to end: does the authorization code actually land on an attacker-controlled origin, not just 'the validation message went away'?
Hunting checklist
- Two accounts to test victim/attacker roles
- Tested reset-token reuse and cross-account reuse
- Tested email/password change without the expected confirmation
- Mapped every redirect_uri-accepting endpoint
- Tried subdomain, @, fragment and double-encoding bypasses
- Confirmed the code/token actually reaches an external origin, end to end
Evidence to collect
- Original OAuth/reset request
- Modified request
- 302/301 response showing the redirect
- Final URL the browser lands on
- Proof the code/token reached the attacker's domain
Recommended fix
- Exact redirect_uri matching, no wildcard subdomains
- Normalize and decode URLs once before validating — never decode again afterward
- Invalidate all sessions on password change
- Expire and single-use reset tokens, scoped to one account
Qué es
Dos caminos relacionados al mismo destino — la toma total de una cuenta. Uno: recuperación de contraseña, cambio de email, verificación OTP o gestión de sesiones mal hechas. Dos: manipular el parámetro
redirect_uri de OAuth para que el authorization code se redirija a un dominio que controlas en vez de a la app legítima.Por qué importa
El account takeover es, de forma constante, una de las categorías de más valor: casi nunca es 'solo' un campo o un recurso, es la cuenta entera, con todo lo que cuelga de ella (facturación, datos, otras integraciones).
Dónde buscar
- Recuperación de contraseña, cambio de email/teléfono, verificación OTP, magic links, gestión de sesiones
- Cualquier endpoint
,/authorize
,/oauth/authorize
con parámetros/connect/authorize
/client_id
/redirect_uristate
Cómo lo pruebo
- Mapea cada 'camino alternativo' del flujo de auth: olvidé mi contraseña, no tengo acceso al email, reenviar código, código caducado, login con Google/SSO.
- Prueba reutilizar el token de reset entre sesiones, reutilizar el token de una cuenta en otra, cambiar el email sin confirmar, cambiar la contraseña sin la actual.
- Para OAuth: captura la petición con
, decodifícala, y prueba cambios de subdominio, el truco deredirect_uri=
(@
), trucos con fragmento (https://trusted.com@attacker.com
), y discrepancias entre encoding simple y doble — el objetivo es un valor que el servidor valide como legítimo pero que el navegador resuelva a otro host.https://attacker.com#@trusted.com - Confirma de extremo a extremo: ¿el authorization code llega de verdad a un origen controlado por el atacante, o solo desapareció el mensaje de validación?
Checklist de hunting
- Dos cuentas para probar roles víctima/atacante
- Probada la reutilización del token de reset y entre cuentas
- Probado cambio de email/contraseña sin la confirmación esperada
- Mapeados todos los endpoints que aceptan redirect_uri
- Probados bypasses de subdominio, @, fragmento y doble encoding
- Confirmado que el code/token llega de verdad a un origen externo, extremo a extremo
Evidencias para el reporte
- Petición OAuth/reset original
- Petición modificada
- Respuesta 302/301 mostrando la redirección
- URL final donde aterriza el navegador
- Prueba de que el code/token llegó al dominio del atacante
Mitigación
- Coincidencia exacta de redirect_uri, sin subdominios comodín
- Normalizar y decodificar la URL una sola vez antes de validar — nunca volver a decodificar después
- Invalidar todas las sesiones al cambiar la contraseña
- Tokens de reset caducables y de un solo uso, ligados a una cuenta