hunt-csrf – Security skill for AI agents
Hunting skill for csrf vulnerabilities.
Install the hunt-csrf skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-csrf-347f9.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /hunt-csrf – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-csrf-347f9.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $hunt-csrf – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-csrf-347f9.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /hunt-csrf – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-csrf-347f9.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /hunt-csrf – then describe the task you want done.
SKILL.md content
> ⚠️ AUTHORIZED USE ONLY
> This skill is for educational purposes or authorized security assessments only.
> You must have explicit, written permission from the system owner before using this tool.
> Misuse of this tool is illegal and strictly prohibited.
> Mandatory confirmation gate
> Before running any command that probes, exploits, changes, persists on, extracts data from, or attempts credential access against a target:
> 1. Ask the user to state the exact target URL, IP, account, or resource.
> 2. Ask the user to confirm written authorization and the permitted scope.
> 3. Show the exact command(s) and explain their expected effect.
> 4. Wait for explicit confirmation in the current conversation.
>
> Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab.
Browser delivery is required for confirmation
Raw clients can vary headers to diagnose checks, but can spoof values a browser cannot send. Success with an authenticated cookie and forged Origin is not CSRF evidence. Confirm a harmless reversible state change using a real supported browser, separate controlled origin and test accounts, documenting cookies, SameSite, preflight, redirects and actual before/after state. Never transfer money or alter real users as a demonstration.
Autonomous Testing Priority
CSRF only matters on state-changing actions that a browser could be tricked into making cross-site.
Testing flow:
1. GET the form endpoint to establish a baseline and check what fields exist (look for hidden csrf_token, authenticity_token, _token, csrfmiddlewaretoken fields).
2. POST the state-changing action without any CSRF token field. Send only the functional parameters (email, amount, etc.).
3. Use a "simple-request" Content-Type — application/x-www-form-urlencoded, multipart/form-data, OR text/plain are the three CORS "simple" content-types a cross-origin form can send with no preflight. A JSON endpoint is CSRF-resistant only if the server rejects those — if it also accepts a text/plain body (common), craft a text/plain payload that parses as valid JSON (see the JSON-CSRF-via-text/plain section). Don't skip a JSON endpoint on the assumption that application/json alone is protective.
4. A successful raw replay is only a candidate. Confirm browser-deliverable credentials and the actual state change.
High-value targets (in order of impact):
- Email/password change → account takeover
- Money transfer or payment → financial fraud
- Admin actions (role assignment, user deletion)
- OAuth social-account linking → persistent ATO
Token bypass techniques when a token IS present:
- Omit the token field entirely — some frameworks only validate if the field exists, not if it's absent
- Send an empty value (
_token=) — some validate format, not presence - Copy a token from another session — some tokens aren't tied to the session
Scope: Login CSRF can have impact and belongs in an authorized assessment; evaluate logout by concrete harm. GET endpoints can also mutate state. Exclude only genuinely read-only behavior.
---
Crown Jewel Targets
CSRF becomes high-value when it touches state-changing actions with account-level or financial consequences. The highest-paying targets are:
- Account takeover vectors: OAuth/SSO flows (RelayState manipulation), social account linking/unlinking (Oculus-Facebook, SocialClub), import-friends features that expose OAuth tokens
- Authentication infrastructure: Login CSRF, session fixation via CSRF, forced account association
- API endpoints accepting cross-origin POST: JSON APIs, heartbeat/activity APIs, anything that skips Content-Type enforcement
- Third-party integrations: Grafana, monitoring dashboards, embedded analytics — often lag on CSRF protections
- Social platforms: Twitter/X collections, friend imports, social graph mutations — high-volume, authenticated actions with real user impact
Asset types that pay most: Core product auth flows > API gateways > third-party integrations running on subdomains > admin panels.
---
Attack Surface Signals
URL Patterns
/oauth/authorize?RelayState=
/accounts/link
/import/friends
/api/v*/heartbeat
/api/v*/collect
/monitoring/* (Grafana, Prow, Prometheus)
/auth/saml/callback
/connect/* (social integrations)Response Header Signals
# Missing or weak SameSite cookie attributes
Set-Cookie: session=abc123; HttpOnly # inspect browser default and delivery; absence alone is not proof
Set-Cookie: session=abc123; SameSite=None # explicitly allows cross-site
# Missing CSRF headers
# No X-Frame-Options or permissive CORS
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true # wildcard origin cannot authorize credentialed CORS in browsersJS / DOM Patterns
// Static or predictable CSRF tokens
meta[name="csrf-token"] // grep if value changes across sessions
authenticity_token // Rails — check if reused across page loads
// JSON endpoints without Content-Type enforcement
fetch('/api/heartbeat', {method: 'POST', body: JSON.stringify(data)})
// No CSRF token in form at all
<form method="POST" action="/accounts/link"> // no hidden token fieldTech Stack Signals
- Rails apps: Look for
authenticity_token— test if it's static per session - Django apps: Check
csrfmiddlewaretoken— test cross-user/session reuse - Grafana instances: CVE-2022-21703 — check version via
/api/health - SAMLv2/OIDC flows:
RelayStateparameter rarely validated - Express/Node APIs: Often skip CSRF middleware on
/api/*routes
---
Step-by-Step Hunting Methodology
1. Map all state-changing endpoints — Spider authenticated session, filter for POST/PUT/DELETE/PATCH. Note every form and AJAX call.
2. Check cookie SameSite attributes — In DevTools → Application → Cookies. Flag any session cookie without SameSite=Strict or Lax.
3. Test token staticness — Log in twice (different sessions or incognito). Compare authenticity_token / csrfmiddlewaretoken / csrf-token values across:
- Same session, different page loads (stable per-session tokens may be valid)
- Different sessions for same user
- Different users entirely
4. Test token omission — Remove the CSRF token field entirely from a POST request. A 200 response alone does not prove CSRF; verify browser delivery and state change.
5. Test token substitution — Replace the token with one from a different session. Server accepting it = broken validation.
6. Test JSON endpoints for form-POST CSRF — Check if Content-Type is enforced:
- Send
application/x-www-form-urlencodedto a JSON endpoint - Send
text/plainwith a JSON body - If accepted, HTML form can trigger it cross-origin
7. Hunt OAuth/SSO RelayState — Intercept SAML/OIDC flows. Test if RelayState is validated for same-origin. Inject external URLs.
8. Check social linking flows — Every "connect your X account" feature. These often use redirect-based OAuth where CSRF on the callback can associate an attacker's social account.
9. Test third-party dashboards on subdomains — Grafana, Kibana, Prometheus. Check version, apply known CVEs, test default CSRF posture.
10. Build PoC HTML page — Host on a different origin, fire the request, confirm cookies are sent and action executes.
---
Payload & Detection Patterns
Basic CSRF PoC (Form POST)
<html>
<body onload="document.forms[0].submit()">
<form method="POST" action="https://target.com/api/v1/account/link">
<input type="hidden" name="provider" value="attacker_account_id" />
<input type="hidden" name="token" value="oauth_token_here" />
</form>
</body>
</html>JSON CSRF via text/plain (bypasses Content-Type check)
<html>
<body onload="document.forms[0].submit()">
<form method="POST" action="https://target.com/api/heartbeat"
enctype="text/plain">
<!-- browser sends: {"status":"ok","x":"=padding"} -->
<input type="hidden" name='{"status":"ok","x":"' value='padding"}' />
</form>
</body>
</html>curl: Test CSRF token omission
# Capture a valid request, then replay without token
curl -s -X POST https://target.com/settings/email \
-H "Cookie: session=YOUR_SESSION" \
-d "[email protected]" \
-v 2>&1 | grep -E "HTTP|location|error"curl: Test token reuse across sessions
# Get token from session A
TOKEN_A=$(curl -s https://target.com/settings -H "Cookie: session=SESSION_A" \
| grep -oP 'authenticity_token[^"]*value="\K[^"]+')
# Use token A in session B's request
curl -s -X POST https://target.com/settings/update \
-H "Cookie: session=SESSION_B" \
-d "authenticity_token=$TOKEN_A&[email protected]" \
-vGrep patterns for recon
# Find CSRF token fields in HTML responses
grep -Eo 'name="(csrf|… (full content is in the download package)
Related skills
- hunt-dom – Hunt client-side DOM vulnerabilities
- hunt-fintech-graphql – Hunt fintech-specific GraphQL vulnerabilities
- hunt-graphql – Hunting skill for graphql vulnerabilities.
- hunt-grpc – Hunt gRPC vulnerabilities
- hunt-idor – Hunting skill for idor vulnerabilities. Built from 26 public bug bounty reports. Use…
- hunt-laravel – Hunt Laravel specific vulnerabilities
- hunt-misc – Hunting skill for misc vulnerabilities. Reference for authorized role, token and…
- hunt-nextjs – Hunt Next.js specific vulnerabilities