DEV Community

Cover image for How one unchecked redirect param let me into an internal OAuth page
Oopssec Store
Oopssec Store

Posted on • Originally published at koadt.github.io on

How one unchecked redirect param let me into an internal OAuth page

Exploit an unvalidated redirect parameter on OopsSec Store's login page to reach a restricted internal OAuth callback endpoint.

OopsSec Store's login page accepts a redirect query parameter that isn't validated after authentication. We'll abuse this to navigate to an internal endpoint that's otherwise inaccessible.

Lab setup

From an empty directory:

npx create-oss-store oss-store
cd oss-store
npm start
Enter fullscreen mode Exit fullscreen mode

Or with Docker (no Node.js required):

docker run -p 3000:3000 leogra/oss-oopssec-store
Enter fullscreen mode Exit fullscreen mode

The application runs at http://localhost:3000.

Target identification

The app has a login page at http://localhost:3000/login. Try visiting /profile while logged out and you'll get bounced to:

http://localhost:3000/login?redirect=%2Fprofile
Enter fullscreen mode Exit fullscreen mode

That redirect parameter tells the app where to send you after login.

Exploitation

Step 1: Confirming the redirect behavior

First, verify the redirect actually works:

  1. Open an incognito window
  2. Go to http://localhost:3000/profile
  3. Notice you land on /login?redirect=%2Fprofile
  4. Log in with alice@example.com / iloveduck
  5. You end up on /profile

So the parameter does control post-login navigation.

Step 2: Testing for open redirect

Now try an external URL:

  1. Go to http://localhost:3000/login?redirect=https://github.com/kOaDT/oss-oopssec-store
  2. Log in
  3. You land on GitHub

GitHub

No validation at all. The app redirects to an external domain.

Step 3: Discovering internal endpoints

With the open redirect confirmed, let's look for internal resources worth targeting:

gobuster dir -u http://localhost:3000/internal -w /usr/share/wordlists/dirb/common.txt
Enter fullscreen mode Exit fullscreen mode

This turns up /internal/oauth/callback. Visiting it directly while authenticated just kicks you back to the homepage. The page exists, but it won't render through normal navigation.

Step 4: Exploiting the redirect to access the internal page

Here's the trick: the internal callback page needs a specific cookie that only gets set during login when a redirect parameter is present.

  1. Go to http://localhost:3000/login?redirect=/internal/oauth/callback
  2. Log in with alice@example.com / iloveduck
  3. You land on /internal/oauth/callback

Step 5: Retrieving the flag

This time the page renders. It shows an internal debug interface with OAuth provider status and the flag:

OSS{0p3n_r3d1r3ct_l0g1n_byp4ss}
Enter fullscreen mode Exit fullscreen mode

Flag

Vulnerable code analysis

In the login form component, the code grabs the redirect parameter and navigates to it with zero checks:

const redirect = searchParams.get("redirect");

// After successful login:
if (redirect) {
  window.location.href = redirect;
} else if (data.user?.role === "ADMIN") {
  router.push("/admin");
} else {
  router.push("/");
}
Enter fullscreen mode Exit fullscreen mode

window.location.href will go anywhere. Relative paths, same-origin, external domains. Nothing stops it.

The login API also sets a short-lived oauth_callback cookie whenever a redirect parameter is present. The internal callback page checks for this cookie, which is why you can't reach it by just typing the URL in your browser. You have to go through the login flow.

Remediation

The fix: validate the redirect target before navigating.

Relative path validation

The simplest approach. Only allow paths starting with a single /:

function isSafeRedirect(url: string): boolean {
  return url.startsWith("/") && !url.startsWith("//");
}

if (redirect && isSafeRedirect(redirect)) {
  router.push(redirect);
}
Enter fullscreen mode Exit fullscreen mode

URL parsing and origin check

More robust. Parse the URL and compare origins:

function isSameOrigin(url: string): boolean {
  try {
    const parsed = new URL(url, window.location.origin);
    return parsed.origin === window.location.origin;
  } catch {
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

Allowlist approach

If you only have a handful of valid destinations, just list them:

const ALLOWED_REDIRECTS = ["/", "/profile", "/admin", "/orders", "/cart"];

if (redirect && ALLOWED_REDIRECTS.includes(redirect)) {
  router.push(redirect);
}
Enter fullscreen mode Exit fullscreen mode

Whichever approach you pick, using router.push() instead of window.location.href also helps since Next.js router only handles same-origin routes.

Lab

GitHub logo kOaDT / oss-oopssec-store

Security training for the apps you actually ship. Open your browser and start hacking.

OSS - OopsSec Store

An intentionally vulnerable e-commerce app for learning web security.
Master real-world attack vectors through a realistic CTF platform.
Hunt for flags, exploit vulnerabilities, and level up your security skills

Docker Hub · npm · Roadmap · Walkthroughs · Contributing · Good first issues

GitHub license PRs Welcome Good first issues Intentionally Vulnerable
GitHub stars GitHub forks

   ____  ____ ____     ____                  ____            ____  _
  / __ \/ __// __/    / __ \ ___   ___  ___ / __/ ___  ____ / __/ / /_ ___   ____ ___
 / /_/ /\ \ _\ \     / /_/ // _ \ / _ \(_-<_\ \  / -_)/ __/_\ \  / __// _ \ / __// -_)
 \____/___//___/     \____/ \___// .__/___/___/  \__/ \__//___/  \__/ \___//_/   \__/
                                /_/
# Node.js
npx create-oss-store my-ctf-lab && cd my-ctf-lab && npm start

# Docker
docker run -p 3000:3000 leogra/oss-oopssec-store

# Then open http://localhost:3000 and
Enter fullscreen mode Exit fullscreen mode

Disclaimers

Do not deploy OopsSec Store on a production server. This application is intentionally vulnerable and should only be used in isolated, local environments for educational purposes.

Do not exploit vulnerabilities on systems you don’t have explicit authorization to test. Unauthorized access to computer systems is illegal. Always obtain proper permission before performing security testing.

Feedback & Support

Having trouble following this writeup? Found a typo or have suggestions for improvement?

Feel free to open an issue or start a discussion on GitHub.

Top comments (0)