Bypassing CSRF Protection by Injecting a Forged Token via Cookie Injection

42 views·4 min read·
Bypassing CSRF Protection by Injecting a Forged Token via Cookie Injection

Most CSRF protections tie a token to the user’s session. If the token and the session don’t match, the request gets rejected. Simple, effective – usually.

But what if the token is only tied to a second cookie, not the session itself? In this lab from PortSwigger’s Web Security Academy, that small design flaw is enough to fully bypass CSRF protection – no XSS required. All it takes is a cookie injection bug hiding in an unrelated feature: the search function.

This post breaks down the vulnerability, explains why each step works, and walks through the full exploit.

The Core Flaw

The app uses two separate cookies:

  • session – identifies who you are
  • csrfKey – a secret value the server uses to validate the CSRF token

When you submit a form, the server checks:

Does the csrf parameter match what’s expected from the csrfKey cookie?

That’s it. It never checks whether csrfKey actually belongs to the same user as session. These two cookies are independent. That’s the bug.

This means an attacker can generate their own valid csrfKey / csrf pair (just by logging in normally), then trick a victim’s browser into using the attacker’s csrfKey instead of their own – while the victim’s session cookie stays untouched.

Result: the request passes CSRF validation (because the token matches the csrfKey), but runs under the victim’s session. The attacker just changed the victim’s email address, entirely from a third-party site.

Why You Can’t Just Set the Cookie Directly

Browsers enforce same-origin cookie rules. A page hosted on evil.com cannot set a cookie for target-app.com. Only the target’s own domain can do that, via a Set-Cookie response header.

So the attacker needs the target site itself to hand out the malicious cookie. That’s where the second bug comes in.

The Second Flaw: Header Injection via Search

The site’s search feature reflects the search term into a Set-Cookie header, without sanitizing it. That means you can inject a CRLF sequence (%0d%0a) into the search parameter and smuggle your own header into the response:

/?search=test%0d%0aSet-Cookie:%20csrfKey=YOUR-KEY;%20SameSite=None

Since this request goes to the target’s own domain, the browser accepts the Set-Cookie header as legitimate, same-origin rules are satisfied. The victim’s browser now stores the attacker’s csrfKey.

Putting It Together

Step 1: Log in as yourself. Capture the “update email” request. Confirm that changing the session cookie logs you out, but changing csrfKey only breaks CSRF validation – proving the two cookies aren’t linked.

Step 2: In an incognito window, log in as a second test account. Confirm that swapping in the first account’s csrfKey and csrf value makes the second account’s request valid. This confirms the token is not session-bound.

Bypassing CSRF Protection by Injecting a Forged Token via Cookie Injection

Now we know, CSRF is not bound to the session, but for the attack to work, we need a method to set the CSRFKey into the victim’s browser. So we will head towards the search functionality on the website.

Step 3: Capture the search request. Confirm the search term is reflected in a Set-Cookie header.

Step 4: So now we need to build an exploit HTML page that auto-submits the form for the “update email” request, using your own csrf token, with the email, using the Set-Cookie Header injection vulnerability, that will inject the CSRFKey into the victim’s browser.

    
    <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="hidden" name="csrf" value="test">
      <input type="submit" value="Submit request">
    </form>
<img src="https://YOUR-LAB-ID.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrfKey=YOUR-KEY%3b%20SameSite=None" onerror="document.forms[0].submit()">

So it works like this: Instead of auto-submitting on load, first load an <img> tag pointing at the malicious search URL. Its onerror handler triggers the form submission, but only after the cookie-injection request has completed.

Then, host the exploit and deliver it to the victim.

Why This Works

Two independently unremarkable bugs combine into a full CSRF bypass:

  1. CSRF tokens validated against a cookie that isn’t cryptographically bound to the session.
  2. A header injection bug in an unrelated feature (search) that lets an attacker set arbitrary cookies for the target domain.

Neither bug alone breaks CSRF protection. Together, they defeat it completely.

Takeaway

CSRF tokens must be tied directly to the authenticated session – not to a separate cookie that can be swapped independently. And any user input reflected into response headers must be strictly sanitized, or it becomes a cookie injection vector on its own.

Leave a Reply

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