⚠️ Authorised Testing Only: OAuth vulnerability testing must only be performed against applications within your authorised bug bounty scope. OAuth CSRF attacks that link accounts or redirect tokens could affect real users if performed outside a controlled test environment. Always use your own test accounts when demonstrating OAuth vulnerabilities.
OAuth Bug Bounty 2026 :— the “Login with Google” button is on nearly every application you will test in a bug bounty programme. And behind that button is an authorization flow with multiple parameters that developers frequently misconfigure, skip CSRF protection on, or validate too loosely. A missing state parameter enables account takeover. A loose redirect_uri validation sends authorization codes directly to an attacker’s server. A token in a URL that the application shares with third-party scripts leaks via Referer headers to analytics providers. Each of these is a real, documented, commonly found vulnerability class that pays Critical in most programmes. Today you learn all of them.
🎯 What You’ll Master in Day 18
Understand the OAuth 2.0 authorization code flow and where each attack vector lives
Test for missing state parameter and exploit it as an OAuth CSRF
Enumerate and test redirect_uri validation bypasses systematically
Identify token leakage via Referer headers in post-OAuth page loads
Chain multiple OAuth weaknesses into a complete account takeover PoC
The OAuth 2.0 Authorization Code Flow — What You Are Actually Attacking
The OAuth 2.0 authorization code flow involves four entities: the user, the client application (the site with the “Login with Google” button), the authorization server (Google/GitHub/Facebook), and the resource server (the API). The flow produces an authorization code, which the client application exchanges for an access token behind the scenes. Every step involves parameters passed through browser redirects — and browser redirects are trivially interceptable and modifiable.
POST /token {code, client_id, client_secret, redirect_uri}
🎯 Test: redirect_uri must match Step 1 exactly — mismatch = bypass
📸 OAuth 2.0 authorization code flow with attack surface annotations — three test points: redirect_uri and state in the authorization request, authorization code in the redirect URL, and redirect_uri validation in the token exchange. Each is a distinct vulnerability class.
🛠️ EXERCISE 1 — BROWSER (12 MIN · NO INSTALL)
Map the OAuth Flow on a Bug Bounty Target and Document Every Parameter
⏱️ Time: 12 minutes · Browser DevTools · any programme in scope with OAuth login
Step 1: Choose a bug bounty programme target with a
“Login with Google”, “Login with GitHub”, or similar
OAuth login button. Only use targets within programme scope.
Step 4: In the Network tab, find the first redirect to the
authorization server (accounts.google.com, github.com/login/oauth, etc.)
Right-click → Copy → Copy URL
Step 5: In a text editor, parse the authorization URL:
– What is the client_id?
– What is the redirect_uri value?
– Is there a state parameter? If yes, does it change each time?
– What scopes are requested?
– What is the response_type? (code vs token)
Step 6: Complete the login flow
Step 7: In Network tab, find the callback request to the
redirect_uri. Does the URL contain the authorization code?
Step 8: Check what third-party scripts load on the page
after OAuth completes (analytics, CDN, fonts, ads).
If the code was in the URL at callback, it may have leaked
via Referer to all of these.
Document: full authorization URL, all parameters, any concerns.
✅ What you just learned: Mapping the OAuth flow before testing is essential — you cannot test redirect_uri if you do not know what the legitimate value is, and you cannot test state if you do not know whether it exists. The most common immediate finding from this mapping exercise is a missing state parameter — surprisingly common even in 2026. The third-party script check in Step 8 catches token leakage without any interception tool — if code= appears in the callback URL and the page then loads third-party scripts, those scripts receive the code in the Referer header.
📸 Share your mapped OAuth parameter list (redact any sensitive values) in #day-18-oauth on Discord.
Missing State Parameter — OAuth CSRF and Account Takeover
The state parameter is OAuth’s CSRF token. Without it, an attacker can initiate an OAuth authorization with their own account, intercept the resulting redirect URL (before completing authorization), craft a CSRF payload from it, and trick a victim into visiting it. When the victim’s browser follows the link, it completes the OAuth flow — linking the attacker’s OAuth identity to the victim’s application account. The attacker can now log in as the victim using their own OAuth credentials.
TESTING FOR MISSING STATE PARAMETER
# Step 1: Start an OAuth flow — intercept with Burp
GET /authorize?client_id=CLIENT&redirect_uri=https://app.com/callback&response_type=code
# No ‘state’ parameter present → potential OAuth CSRF
# Step 2: If state IS present — check if it is validated
# In Burp Repeater, modify the state value to a random string
GET /callback?code=AUTH_CODE&state=TAMPERED_VALUE
# If the application accepts the modified state → no validation
# Step 3: Build the CSRF PoC (for missing state)
# Attacker initiates OAuth flow with attacker’s account
# Gets the callback URL: ?code=ATTACKER_CODE
# Sends victim this URL via CSRF/phishing
# Victim’s browser completes: attacker account linked to victim
The redirect_uri determines where the authorization server sends the authorization code after user consent. Strict validation accepts only the exact registered URI. Loose validation accepts any URI under the registered domain, or fails to check specific bypass patterns. If any modification is accepted, the authorization code is sent to the wrong destination — enabling code theft if that destination is attacker-controlled.
🌐 EXERCISE 2 — PORTSWIGGER (25 MIN)
Exploit OAuth Vulnerabilities Using PortSwigger Web Academy Labs
⏱️ Time: 25 minutes · Free PortSwigger account
Step 1: Go to portswigger.net/web-security/oauth
Step 2: Open lab: “Authentication bypass via OAuth implicit flow”
Step 3: Complete the lab — note how the implicit flow exposes
tokens directly in the URL fragment
Step 4: Open lab: “Forced OAuth profile linking”
This demonstrates account takeover via missing state
Step 5: Follow the lab — the attack flow:
a) Intercept the OAuth link request
b) Drop the request before the state is validated
c) Send the captured URL to the “victim” (same browser
in the lab’s victim simulation)
d) The victim’s account becomes linked to your OAuth identity
Step 6: Open lab: “OAuth account takeover via redirect_uri bypass”
Test redirect_uri modifications:
– Path traversal: https://legit.com/callback/../
– Query string: https://legit.com/callback?x=
– Subdomain: https://attacker.legit.com
Observe which modifications the auth server accepts
For each lab, document:
– The specific parameter that was vulnerable
– The exact payload used
– The impact achieved (account linkage, code theft, etc.)
✅ What you just learned: The PortSwigger OAuth labs demonstrate that each OAuth vulnerability class requires a different attack approach. The implicit flow lab shows why response_type=token (exposing tokens in URLs) is more dangerous than response_type=code. The forced linking lab shows the exact CSRF chain for missing state. The redirect_uri lab shows that authorization servers often validate domain but not path — allowing path traversal attacks that redirect codes to arbitrary paths on the legitimate domain. Each of these has appeared in real bug bounty reports at Critical severity.
📸 Screenshot completed lab confirmations and share in #day-18-oauth on Discord.
Token Leakage via Referer — Silent Credential Theft
When an authorization code or access token appears in a URL — which happens in the implicit flow and in some authorization code flow implementations — and the page then loads any third-party resources (Google Analytics, CDN scripts, fonts), the browser automatically includes the full current URL in the Referer header sent to those third parties. The authorization code is in that URL. Every analytics platform, CDN provider, and ad network that the page loads will receive it in their access logs.
TOKEN LEAKAGE VIA REFERER — DETECTION
# Step 1: In Burp — set up intercept on OAuth callback
# After OAuth redirects to: /callback?code=AUTH_CODE_HERE
# Step 2: Check if token/code is in the URL at callback
https://app.com/callback?code=4/0AfJohXnV… ← code in URL
# Step 3: Check what third-party requests fire after callback
# Burp → HTTP History → filter by Referer containing ‘code=’
# For each — check HTTP response: does auth server redirect to modified URI?
# Success = code appears in redirect to attacker-controlled domain
✅ What you just learned: The seven redirect_uri bypass patterns cover the full range of validation flaws — from pure path-based validation (bypassed with path traversal) through hostname-only validation (bypassed with subdomain or @ patterns) to wildcard domain matching (bypassed with subdomain registration). In Burp Repeater, work through each systematically and observe the response. Any 302 redirect to a modified URI is a finding. The impact level depends on whether you can register the destination — if you can receive the redirected code, you have a code theft vulnerability chainable to account takeover.
📸 Screenshot a Burp Repeater request showing a modified redirect_uri test and response in #day-18-oauth on Discord. Tag #oauth2026
🧠 QUICK CHECK — Day 18
An OAuth authorization request does not include a state parameter. You initiate an OAuth flow with your own test account, capture the callback URL containing the authorization code before completing the flow, and send that URL to a victim via a crafted link. The victim clicks the link and their account becomes linked to your OAuth identity. What vulnerability class is this and what is the severity?
📋 OAuth Bug Bounty Checklist — Day 18
Check for state parameterMissing state = OAuth CSRF → account takeover via forced account linking
Test state validationModify state value in callback — accepted = not validated = still vulnerable
Check for Referer leakageCode/token in callback URL + third-party scripts = credentials leak via Referer header
Test scope manipulationAdd email/admin/write scopes — does auth server grant beyond what app registered?
🏆 Mark Day 18 as Complete
OAuth is on the attack surface of nearly every modern web application with social login. The four vulnerability classes you learned today — missing state, redirect_uri bypass, token leakage, and account takeover chains — are consistently the highest-paying findings in OAuth testing because they directly enable account takeover.
❓ Frequently Asked Questions
What is OAuth and why is it vulnerable?
OAuth 2.0 is the “Login with Google/GitHub” framework — it lets apps access resources without sharing passwords. Vulnerable because multiple parties communicate through interceptable browser redirects. Key vulns: missing state (CSRF), loose redirect_uri validation (code theft), tokens in URLs (Referer leakage).
What is the OAuth state parameter?
A random token generated by the client, included in the authorization request, and verified on return. It prevents CSRF on the OAuth flow. Without it, an attacker can trick a victim into linking their account to the attacker’s OAuth identity — account takeover without needing the victim’s password.
How do I test redirect_uri validation?
Modify redirect_uri in Burp Repeater with 7 patterns: path traversal, query string injection, subdomain prefix, @ confusion, URL encoding, extra slashes, and localhost. Any accepted modification that redirects the authorization code to a non-registered URI is a finding.
What is an OAuth account takeover chain?
Combining redirect_uri bypass (capture the code) + missing state CSRF (force victim to initiate flow) + code exchange = attacker logs in as victim. Each step amplifies the others into full account takeover without credentials.
What comes after Day 18 in the Bug Bounty course?
Day 19 covers CSRF — Cross-Site Request Forgery. OAuth CSRF is a specific application of CSRF principles; Day 19 covers the broader vulnerability class including token validation bypasses, SameSite cookie exploitation, and CSRF in API endpoints.
← Previous
Day 17: JWT Attacks Bug Bounty 2026
Next →
Day 19: CSRF Bug Bounty 2026
📚 Further Reading
JWT Attacks Bug Bounty 2026— Day 17 covers JWT algorithm confusion and alg:none attacks — the token-level counterpart to OAuth’s flow-level vulnerabilities, often combined into complete authentication bypass chains.
60-Day Bug Bounty Mastery Course— The complete course hub — Day 18 OAuth attacks are part of the authentication vulnerability phase covering Days 16–22, building from rate limiting through OAuth, CSRF, and HTTP request smuggling.
Authentication Bypass Hub— The complete authentication bypass category covering OAuth, JWT, CSRF, SAML, and session management attacks with real-world examples and PortSwigger lab references.
PortSwigger OAuth Attack Labs— Six interactive OAuth labs covering implicit flow bypass, forced account linking, redirect_uri bypass, and SSRF via OpenID dynamic client registration — the most comprehensive practical OAuth training available.
OAuth 2.0 Official Specification— The OAuth 2.0 RFC specification — understanding what the standard requires versus what implementations often skip is the foundation for finding every vulnerability class covered in this guide.
ME
Mr Elite
Owner, SecurityElites.com
The OAuth finding that gave me the most confidence in this vulnerability class was a medium-sized SaaS platform with a “Login with GitHub” button. The authorization request had a state parameter — so the naive check would have moved on. But the state was a static string, not randomly generated. The same value appeared in every single authorization request regardless of user or session. That meant any attacker who had ever clicked the login button knew the state value. They could initiate an OAuth flow, capture the callback URL, and send it to any victim — the static state would validate on the server side. Account takeover via a “validated” but completely predictable state parameter. The fix was two lines of code: generate a cryptographically random state value and tie it to the current session. The finding paid Critical. The lesson: check that the state is present, but also check that it is random, and also check that it is session-bound. All three conditions must be true for the CSRF protection to actually work.
Leave a Reply