| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | OAuth 2.0 Authentication |
| Difficulty | Apprentice |
| Objective | Log in to Carlos's account ([email protected]) without his password by abusing flawed validation in the client app's OAuth implicit flow |
Authentication Bypass via OAuth Implicit Flow¶
Navigating to /my-account redirected through /social-login into the OAuth provider:
The authorization request included response_type=token — the implicit grant. The token comes straight through the browser with no server-to-server exchange. I logged in as wiener:peter and approved access:
Looking through proxy history for the request that completes the login:
POST /authenticate HTTP/2
{"email":"[email protected]","username":"wiener","token":"CdRElPgRBJF4aY6XeY0gnqEHPBcnNmcse8tHHuiIUbO"}
This is the client application's own login endpoint — not the OAuth provider's. The client received email, username, and the access token from the OAuth flow and is POSTing them to itself to establish a session. The vulnerability isn't in the OAuth provider; it did its job correctly and issued a valid token for wiener. The question is whether the client's backend verifies that the submitted email and username actually correspond to the account the token was issued for.
Sending to Repeater and swapping the identity fields while keeping the valid token:
POST /authenticate HTTP/2
{"email":"[email protected]","username":"carlos","token":"CdRElPgRBJF4aY6XeY0gnqEHPBcnNmcse8tHHuiIUbO"}
HTTP/2 302 Found
Set-Cookie: session=Szy0J6aH6cI1w6Y3GwGY3qiy9IXGO2CJ; Secure;
The server never cross-checked the token against the submitted identity — it accepted whatever identity was asserted in the POST and issued a session for carlos. A working token for any account is enough to pivot to any other account; you just swap the accompanying identity fields. No password, no carlos's token, no interaction from him at all.
Dropping the session cookie into the browser's storage:
And Lab solved