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.

Free · no account

The checklist I run on every new target

47 checks ordered by cost: first what can get you in trouble, then the cheap stuff, and finally the expensive stuff — which is where the big bounties are. I'll send it to your inbox right now.

Unsubscribe in one click, from any email.

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

2,482

hunters training

62

labs from real hacks

1,629

completions

$14,790

paid out for these bugs

11 flags captured this week·Real hacks from HackerOne · YesWeHack · Bugcrowd·No commitment·Free Academy