Intermediate levelWith account

Open Redirect via Backslash Bypass in the OAuth Login's redirect_url

The client-side validator checks //, : and /. The backslash slips through. Browsers normalize it to a slash and the victim ends up on evil.tld with an active session cookie.

Gorka El BochiMay 9, 202612 min

Quick answer

A conversational AI platform validated the OAuth login's redirect_url parameter by checking that it started with /, not with //, and did not contain :. The backslash passed those three checks. The browser normalizes /\evil.tld to //evil.tld → https://evil.tld, and the server issued the post-login redirect to the attacker's domain with the session cookie already established.


1. Context — the post-login redirect flow

The https://main.target.tld/login endpoint accepted a redirect_url parameter indicating where to send the user after completing login. This parameter was embedded in the OAuth state (googleOauthState / appleOauthState) as the next_url field. After the OAuth provider (Google or Apple) completed authentication and redirected back, the server read next_url from the state and issued the final redirect.

The OAuth state could be seen in __NEXT_DATA__ by decoding the base64 of googleOauthState:

json
{
  "csrf": "6d7f9f064956d71d4f89a20b987e589d",
  "platform": "web",
  "next_url": "/\\evil.tld",
  "current_url": "/login"
}

2. The bypass — unfiltered backslash

The vulnerable validation

The client-side redirect_url validation code checks three conditions:

typescript
if (
  redirectUrl.startsWith("/") &&     // debe ser path relativo
  !redirectUrl.startsWith("//") &&   // bloquea protocol-relative URLs
  !redirectUrl.includes(":")         // bloquea javascript: y esquemas externos
)

Why the backslash bypasses it

The attacker's input is /\evil.tld (URL-encoded as /%5Cevil.tld):

CheckResultReason
startsWith("/")✅ passesStarts with /
!startsWith("//")✅ passesIt's /\, not //
!includes(":")✅ passesThere's no colon
ResultACCEPTEDThe validator treats it as a relative path

But browsers normalize the backslash to a slash per the URL spec:

bash
/\evil.tld  →  //evil.tld  →  https://evil.tld

The server embeds the value unchanged in the OAuth state's next_url. When issuing the post-login redirect, the browser receives /\evil.tld and resolves it as an external URL.


3. The attack URL

ini
https://main.target.tld/login?redirect_url=/%5Cevil.tld

%5C is the URL encoding of the backslash \. When decoded, the parameter becomes /\evil.tld.


4. Base attack flow

bash
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
<!-- PAYWALL -->

5. Escalation chain — Open Redirect via Canvas + postMessage

In the report's escalation, a second chain is documented that combines this open redirect with the platform's canvas system openUrl handler, turning it into a stored and zero-click vector.

How openUrl works in the canvas

A bot's canvas can send a postMessage to the parent with type: "openUrl". Normally, when the destination URL is external, the platform shows the user a confirmation overlay before opening the URL. The shouldAutoOpen function decides whether to show that overlay or not.

The overlay bypass

shouldAutoOpen whitelists the main domain as a trusted origin and skips the confirmation overlay for URLs on that domain:

ini
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

Since the URL passed to the handler is on the whitelisted domain, it passes as a trusted URL. The overlay is not shown. The window opens directly and the redirect chain runs automatically.

Result of the full chain

  • The bot appears on the platform's homepage and suggestions organically.
  • The user clicks the bot (normal platform behavior).
  • The canvas executes the postMessage with openUrl.
  • shouldAutoOpen treats it as a trusted URL → no overlay.
  • The redirect to the external domain fires without any additional confirmation.

6. Behavior with an already-authenticated user

If the user already has an active session, the login endpoint doesn't ask for credentials — it redirects them directly to next_url. This means the open redirect also works on already-logged-in users who open the link, without needing to go through the OAuth flow.


7. Technical classification

FieldValue
Vulnerability typeOpen Redirect — Incomplete URL Validation (Backslash Normalization)
CWECWE-601 — URL Redirection to Untrusted Site
Affected endpointhttps://main.target.tld/login?redirect_url=
Vulnerable parameterredirect_url → embedded in the OAuth state's next_url
Affected browsersFirefox 149, Chrome 146 (standard normalization)
Required interaction (base)Victim opens the link and completes login
Required interaction (canvas chain)A single click on the bot

8. Technical notes

  • The normalization of \ to / in URL paths is standard behavior of the URL parser (WHATWG URL spec). It's not specific to any browser — all modern browsers do it.
  • The client validator checks // but not \ — the fix is to add !redirectUrl.includes("\\") to the set of checks.
  • The validation is applied only on the client. The server does not re-validate next_url before issuing the post-OAuth redirect, which is where the actual redirect happens.
  • In the canvas chain, the confirmation overlay bypass is independent of the open redirect — it's a logic flaw in the whitelist that doesn't account for URLs on that domain being able to redirect externally.

Practice open redirect chains, parser quirks (backslash, dot, @) and URL validation bypasses: Open Redirect labs.

Practice this in a lab

Open Redirect

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account

Related articles

hunters training
711

hunters training

labs from real reports
55

labs from real reports

completions
1,205

completions

in bounties practiced
$213,970

in bounties practiced

46 flags captured this week·Real reports from HackerOne · Bugcrowd · Intigriti·No commitment·Free Academy