Respuesta rápida
Una plataforma de IA conversacional validaba el parámetro redirect_url del login OAuth comprobando que empezara por /, no por // y no contuviera :. El backslash pasaba esos tres checks. El navegador normaliza /\evil.tld a //evil.tld → https://evil.tld, y el servidor emitía la redirección post-login al dominio del atacante con la cookie de sesión ya establecida.
1. Contexto — el flujo de redirect post-login
El endpoint https://main.target.tld/login aceptaba un parámetro redirect_url que indicaba a dónde enviar al usuario tras completar el login. Este parámetro se embebía en el estado OAuth (googleOauthState / appleOauthState) como el campo next_url. Después de que el proveedor OAuth (Google o Apple) completaba la autenticación y redirigía de vuelta, el servidor leía next_url del estado y emitía la redirección final.
El estado OAuth se podía ver en el __NEXT_DATA__ decodificando el base64 de googleOauthState:
{
"csrf": "6d7f9f064956d71d4f89a20b987e589d",
"platform": "web",
"next_url": "/\\evil.tld",
"current_url": "/login"
}
2. El bypass — backslash no filtrado
La validación vulnerable
El código de validación del redirect_url en cliente comprueba tres condiciones:
if (
redirectUrl.startsWith("/") && // debe ser path relativo
!redirectUrl.startsWith("//") && // bloquea protocol-relative URLs
!redirectUrl.includes(":") // bloquea javascript: y esquemas externos
)
Por qué el backslash lo bypassa
El input del atacante es /\evil.tld (URL-encoded como /%5Cevil.tld):
| Check | Resultado | Motivo |
|---|---|---|
startsWith("/") | ✅ pasa | Empieza por / |
!startsWith("//") | ✅ pasa | Es /\, no // |
!includes(":") | ✅ pasa | No hay dos puntos |
| Resultado | ACEPTADO | El validador lo trata como path relativo |
Pero los navegadores normalizan el backslash a slash según la URL spec:
/\evil.tld → //evil.tld → https://evil.tld
El servidor embebe el valor sin normalizar en next_url del estado OAuth. Al emitir la redirección post-login, el navegador recibe /\evil.tld y lo resuelve como URL externa.
3. La URL de ataque
https://main.target.tld/login?redirect_url=/%5Cevil.tld
%5C es la codificación URL del backslash \. Al decodificarse, el parámetro queda como /\evil.tld.
4. Flujo del ataque base
Atacante envía link: target.tld/login?redirect_url=/%5Cevil.tld
↓
Víctima abre el link (página de login real)
↓
redirect_url pasa validación (/\ no es //)
↓
Servidor embebe next_url: "/\evil.tld" en googleOauthState
↓
Víctima hace click "Continue with Google" → autenticación
↓
OAuth callback con código válido
↓
Servidor lee next_url del estado → "/\evil.tld"
↓
Redirect a /\evil.tld (cookie de sesión ya establecida)
↓
Navegador normaliza /\ a // → https://evil.tld
↓
Víctima llega al dominio del atacante con sesión activa
5. Cadena de escalación — Open Redirect vía Canvas + postMessage
En la escalación del reporte se documenta una segunda cadena que combina este open redirect con el handler openUrl del sistema de canvas de la plataforma, convirtiéndolo en un vector stored y zero-click.
Cómo funciona openUrl en el canvas
El canvas de un bot puede enviar un postMessage al parent con type: "openUrl". Normalmente, cuando la URL de destino es externa, la plataforma muestra al usuario un overlay de confirmación antes de abrir la URL. La función shouldAutoOpen decide si mostrar ese overlay o no.
El bypass del overlay
shouldAutoOpen whitelist el dominio principal como origen de confianza y omite el overlay de confirmación para URLs de ese dominio:
openUrl → https://main.target.tld/login?redirect_url=/%5Cevil.tld
↓
shouldAutoOpen: hostname === "main.target.tld" → true → window.open() sin confirmación
↓
La plataforma procesa el login con next_url = /\evil.tld
↓
Redirect a evil.tld
Como la URL que se pasa al handler es del dominio whitelisted, pasa como URL de confianza. El overlay no se muestra. La ventana se abre directamente y la cadena de redirect se ejecuta automáticamente.
Resultado de la cadena completa
- El bot aparece en la homepage y sugerencias de la plataforma de forma orgánica.
- El usuario hace click en el bot (comportamiento normal de la plataforma).
- El canvas ejecuta el postMessage con
openUrl. shouldAutoOpenlo trata como URL de confianza → no hay overlay.- El redirect hacia el dominio externo se dispara sin ninguna confirmación adicional.
6. Comportamiento con usuario ya autenticado
Si el usuario ya tiene sesión activa, el endpoint de login no le pide credenciales — lo redirige directamente a next_url. Esto significa que el open redirect funciona también sobre usuarios ya logueados que abran el link, sin necesidad de pasar por el flujo OAuth.
7. Clasificación técnica
| Campo | Valor |
|---|---|
| Tipo de vulnerabilidad | Open Redirect — Incomplete URL Validation (Backslash Normalization) |
| CWE | CWE-601 — URL Redirection to Untrusted Site |
| Endpoint afectado | https://main.target.tld/login?redirect_url= |
| Parámetro vulnerable | redirect_url → embebido en next_url del estado OAuth |
| Navegadores afectados | Firefox 149, Chrome 146 (normalización estándar) |
| Interacción requerida (base) | Víctima abre el link y completa login |
| Interacción requerida (cadena canvas) | Un click en el bot |
8. Notas técnicas
- La normalización de
\a/en paths de URL es comportamiento estándar del parser de URLs (WHATWG URL spec). No es específico de ningún navegador — todos los navegadores modernos lo hacen. - El validador del cliente comprueba
//pero no\— el fix es añadir!redirectUrl.includes("\\")al conjunto de checks. - La validación solo se aplica en el cliente. El servidor no re-valida
next_urlantes de emitir la redirección post-OAuth, que es donde ocurre el redirect real. - En la cadena de canvas, el bypass del overlay de confirmación es independiente del open redirect — es un fallo de lógica en el whitelist que no contempla que URLs en ese dominio puedan redirigir externamente.
Labs relacionados
Practica chains de open redirect, parser quirks (backslash, dot, @) y bypasses de validación URL: labs de Open Redirect.
Practica esto en un lab
Open Redirect
Sigue aprendiendo · cuenta gratis
Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.
Artículos relacionados
OAuth open redirect — backslash bypass de redirect_uri (POE report walkthrough)
Walkthrough del report POE: open redirect via `\` (backslash) en redirect_uri que el parser de POE no normalizaba correctamente. URL-based XSS encadenado para token theft.
OAuth attacks — state CSRF, redirect_uri bypass, token reuse, IDOR de tokens
Vulnerabilidades en flujos OAuth 2.0: ausencia de state parameter, redirect_uri loose validation, token reuse cross-app, IDOR en endpoints de token refresh.
File Upload — bypasses de extension, content-type y magic bytes
10 bypasses para subir webshells: doble extensión, null byte, content-type spoof, magic bytes, polyglots, race conditions y abuso de path traversal.