CtrlK
BlogDocsLog inGet started
Tessl Logo

oauth

OAuth 2.0 / OIDC attacks — redirect_uri bypass, state CSRF, code leak via Referer, response_type confusion, PKCE downgrade, scope creep, ATO chains.

63

Quality

75%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

Fix and improve this skill with Tessl

tessl review fix ./packages/decepticon/decepticon/skills/standard/exploit/web/oauth/SKILL.md
SKILL.md
Quality
Evals
Security

OAuth / OIDC Attack Playbook

OAuth bugs are the single highest-paying ATO vector in modern bug bounty. Every B2B SaaS has an OAuth surface; many implement it wrong.

1. Map the flow

# Discovery
curl -s https://target/.well-known/openid-configuration | jq

# Key endpoints to identify:
# - /authorize  (where redirect_uri/client_id/scope arrive)
# - /token      (where code exchanges for tokens)
# - /userinfo   (claims about user)
# - /jwks.json  (signing keys, see jwt/SKILL.md)
# - /revoke     (sometimes overpermissive)

Capture a normal flow in Burp. Note: client_id, redirect_uri, state, scope, response_type, code_challenge (PKCE).

2. Attack surface

2.1 redirect_uri bypass

The #1 OAuth bug class. Server's allowlist regex is too loose:

BypassExample
Path appendhttps://target.com/callback/../../evil
Subdomain wildcardhttps://target.com.evil.com/cb (server matches *.target.com)
Userinfo trickhttps://target.com@evil.com/cb
Path-traversal in fragmenthttps://target.com/cb#@evil.com
Open-redirect chainredirect_uri=https://target.com/known-redirect?to=evil.com
Localhost / loopbackredirect_uri=http://localhost:1337 (often allowlisted)
data: URIredirect_uri=data:text/html,<script>... (rare but devastating)
Mixed-protocolhttp:// accepted where https:// required
URL-encoded slashhttps://target.com%2Fcallback%2F@evil.com
Different fragment behaviorredirect_uri=https://evil.com#https://target.com/cb

Test ALL of these. paramspider + Burp Intruder w/ payload list = systematic.

2.2 State parameter missing / weak (CSRF)

state should bind the auth request to the user's session. Missing or predictable state → attacker initiates OAuth in own browser, sends victim the URL, victim clicks → attacker's account now linked to victim's identity.

GET /authorize?response_type=code
    &client_id=...
    &redirect_uri=https://target.com/cb
    &state=                          ← empty or predictable

2.3 PKCE downgrade

Public clients (mobile, SPA) SHOULD use PKCE. If server accepts code_verifier omission for a flow that should require it:

POST /token
client_id=mobile-app
code=<stolen_code>
# Note: no code_verifier sent

Some servers fall back to non-PKCE flow → stolen code exchanges fine.

2.4 Code reuse / replay

Some servers don't invalidate codes after first exchange. Capture the code (via referer leak, IDOR, log scrape) → exchange in attacker's session for victim's tokens.

2.5 Referer leak of code

Some apps render auth-callback as a regular page that fetches resources from third-party CDNs. The full URL (including ?code=...) is sent in Referer header to those CDNs. Attacker who owns / can compromise a CDN link extracts the code.

Test: visit the callback URL, observe Referer to all third-party hosts.

2.6 Scope creep / mass-assignment in token request

POST /token
grant_type=authorization_code
code=...
scope=read write admin             ← inject elevated scope

Some servers honor a scope parameter at token-exchange time and don't re-validate against original /authorize scope.

2.7 response_type confusion

The hybrid flow (response_type=code id_token) may behave differently:

  • Server returns id_token in URL fragment (visible client-side)
  • Then code exchange for access_token
  • Sometimes the id_token validation is weak/skipped on response_type variations

2.8 client_id confusion

Some servers trust client_id as the only identifier. If two clients share a redirect_uri pattern, you can re-use one's code as another:

client_id=trusted-internal-app
redirect_uri=https://attacker.com/cb (also allowlisted for trusted app!)

2.9 OAuth → Account Takeover chain

The classic ATO via OAuth:

  1. Victim signs in to target via OAuth (Google/Microsoft)
  2. Attacker finds open-redirect or XSS on target
  3. Attacker crafts OAuth init URL w/ redirect_uri pointing to attacker via open-redirect
  4. Victim clicks → server issues code → redirected to attacker's host w/ code in URL
  5. Attacker exchanges code for victim's tokens → full ATO

2.10 Account-linking vulnerabilities

"Sign in with Google" can link a NEW Google account to an EXISTING email-password account if the server matches solely by email. Attacker registers victim@target-mail.com (a typosquat or sub-add), starts OAuth, links to victim's existing account.

3. Tools

  • oauthtoolkit — Tom Hudson's automation
  • Burp Pro OAuth flow scanner
  • oauth2-test Python lib for fuzz
  • ZAP Scripting (OAuth modules)
  • Manual w/ Repeater (most flaws need careful semantic work)

4. PoC pattern

  1. Capture normal flow
  2. For each parameter (redirect_uri, state, scope, response_type, client_id, code_challenge), fuzz w/ Burp Intruder using the bypass list above
  3. On any deviation (302 to attacker host, token issued with elevated scope, etc) — capture as evidence
  4. Construct ATO chain: combine OAuth bug w/ open-redirect OR XSS for victim-clicks-link → tokens delivered to attacker

5. Severity calibration

BugTypical
redirect_uri bypass on real client → code to attackerCritical 9.8 (full ATO)
Missing state on social-loginHigh 8.0 (one-click account hijack)
Scope creep acceptedHigh 8.0
PKCE downgrade on public clientHigh 7.5
Code reuse acceptedHigh 8.0
Open-redirect chain extension onlyMedium 6.0
Account-linking via emailHigh-Critical depending on impact

6. Defender remediation

  • Exact-match redirect_uri (case-sensitive, full URL, no path manipulation)
  • Mandatory non-empty unpredictable state bound to session
  • Invalidate codes on first use; short TTL (≤ 60s)
  • PKCE required for ALL public clients
  • Server-side scope validation: reject scope in token request that exceeds the original /authorize scope
  • Never link OAuth identity to existing account by email alone — require explicit user confirmation

Cross-references

  • Upstream catalog: skills/_corpus/payloads/OAuth Misconfiguration/
  • ATO chaining: skills/exploit/web/ato-methodology/SKILL.md
  • Open-redirect chains: skills/exploit/web/open-redirect/SKILL.md

Known exemplars

  • Microsoft 2020: $50k OAuth ATO chain on Teams via redirect_uri
  • Slack 2017: $4-8k bounties on multiple redirect_uri bypasses
  • Github 2019: OAuth scope creep bug for $5k
  • Frans Rosén's writeup on OAuth bugs (one of the best resources)
  • Hackerone disclosure #341876 — Asana account-linking via email
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.