OAuth State Lab: Link Your Account to the Victim's Session

✨ AI Summary
ConnectApp lets you link your Twitter account through OAuth. It never sets the state parameter on the way out, and never checks it on the way back. That missing value is exactly what stops an attacker from stitching their login onto your session — and without it, they can.
Lab at a glance: Intermediate · 100 XP · about 20 minutes · runs in your browser, no signup or setup. Try it yourself first; the full solution is behind a spoiler below.

What this lab teaches

The OAuth state parameter is a CSRF token for the OAuth flow. The client generates it, sends it on the authorization request, and verifies it on the callback, so it knows the callback belongs to the same user who started the flow. Drop it and an attacker can feed a victim a callback carrying the attacker's authorization code — linking the attacker's identity to the victim's account.

The lesson: state is not optional. It is the only thing tying the callback to the user who began the flow.

How ConnectApp links a social account

ConnectApp starts the link without a state value and accepts any callback at face value. An attacker begins their own OAuth link, captures the resulting callback (which carries the attacker's code), and gets the victim's browser to visit it. ConnectApp, having nothing to compare, links the attacker's social identity to the victim's session.

The attack approach

  • Confirm the authorization request has no state parameter and the callback is not checked.
  • Begin an account-link flow yourself and capture the callback URL with your own code.
  • Deliver that callback to the victim's authenticated session.
  • The victim's account becomes linked to your social identity — an account takeover primitive.

Full walkthrough (spoiler)

If the callback is rejected, something is validating the flow after all — open the steps.

Show the step-by-step solution
  1. Start the Twitter link in ConnectApp as the attacker and stop at the callback, capturing the URL that carries your authorization code.
  2. Because there is no state, that callback is not bound to any particular user or session.
  3. Have the victim's logged-in session visit your captured callback (the lab simulates the victim following it).
  4. ConnectApp links your social account to the victim's account. The lab detects the cross-linked session and marks you solved.

Why it works

Without state, the callback carries a code but no proof of who started the flow. ConnectApp simply trusts whatever callback arrives in the victim's session and links the identity inside it. The attacker supplies that identity, so the victim's account ends up connected to the attacker — a classic login-CSRF / account-takeover.

Core principleAlways generate a random, single-use state value, bind it to the user's session, and reject any callback whose state does not match.

Where this happens in the real world

Missing or unverified state is one of the most common OAuth implementation flaws and a frequent source of account-takeover reports, which is why the OAuth specification treats state as a required anti-CSRF measure. It falls under Broken Access Control (A01) and Identification and Authentication Failures (A07) in the OWASP Top 10 (2021).

How to defend against it

  • Generate a random state value per flow, bind it to the user's session, and verify it on the callback.
  • Reject any callback with a missing or mismatched state.
  • Use PKCE as an additional protection for the authorization-code flow.
  • Re-authenticate before sensitive linking so a stray callback cannot silently attach an identity.
Mr Elite tip: When testing an OAuth flow you are authorised to assess, remove the state parameter and see whether the callback is still accepted. If it is, the flow is open to login-CSRF and account linking abuse.

Try it, then go further

Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.

❓ Frequently asked questions

What is the OAuth state parameter?
A random, per-flow value the client sends on the authorization request and verifies on the callback. It is a CSRF token that proves the callback belongs to the user who started the flow.
What happens without state?
An attacker can deliver a callback carrying their own authorization code to a victim's session, linking the attacker's identity to the victim's account — an account takeover.
Is state really mandatory?
The OAuth specification treats it as a required anti-CSRF protection. Omitting it is a well-known, high-impact implementation flaw.
Does PKCE replace state?
No. PKCE protects the code exchange; state protects against cross-session callback abuse. Use both.
⚡
Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *