hunt-graphql – Security skill for AI agents
Hunting skill for graphql vulnerabilities.
Install the hunt-graphql 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-graphql-61288.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /hunt-graphql – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-61288.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $hunt-graphql – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-61288.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /hunt-graphql – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-61288.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /hunt-graphql – 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.
Crown Jewel Targets
GraphQL vulnerabilities are high-value because the attack surface is both broad and deep — a poorly authorized endpoint may expose data models, privilege escalation paths, and cross-API state confusion. Potentially important surfaces include (no payout guarantee):
- Platform APIs (GitHub, Shopify, Stripe-tier targets) where GraphQL mutations interact with REST APIs managing the same resources
- Race conditions between GraphQL mutations and REST endpoints where state synchronization is non-atomic — severity depends on demonstrated impact and program policy
- Authorization persistence bugs where team/org/repo membership state is controlled by one API but readable/writable by another
- B2B SaaS platforms where one tenant affecting another via schema traversal = critical
- Internal admin GraphQL endpoints accidentally exposed to lower-privilege users
The GitHub reports demonstrate the crown jewel pattern: privilege that should be revoked persists because two APIs disagree on ground truth.
---
Attack Surface Signals
URL Patterns:
/graphql
/api/graphql
/v1/graphql
/query
/gql
/graph
/api/v2/graphql
/internal/graphqlResponse Headers:
Content-Type: application/json (with query body)
X-Request-Id + no REST-style path params = likely GraphQLJavaScript Source Patterns:
// grep for these in JS bundles
"query {"
"mutation {"
"__typename"
"apollo"
"ApolloClient"
"graphql-tag"
"gql`"
"operationName"
"GRAPHQL_URI"Tech Stack Signals:
- Apollo Server/Client in JS bundles
- Relay in React apps
grapheneorstrawberry(Python),graphql-ruby,gqlgen(Go),Lighthouse(Laravel)- POST requests with
{"query": "..."}body shape in Burp history __schemaor__typein any response = confirmed GraphQL
Recon Sources:
github.comsearch:"graphql" site:target.com- Wayback Machine for
/graphqlpaths - JS bundle scanning with
LinkFinderorgetallurls
---
Step-by-Step Hunting Methodology
1. Discover the endpoint — spider JS bundles, check /graphql, /api/graphql, review Burp passive scan hits for application/json POST with query fields
2. Test introspection — send the full introspection query. Even if blocked, try field-level enumeration:
{ __typename }
A valid __typename response identifies the runtime object type; it does not demonstrate applicable schema introspection or discoverability.
3. Map the applicable schema — use InQL (Burp extension) or graphql-voyager to visualize relationships. Specifically look for:
- Mutations that modify ownership, permissions, or membership
- Mutations that mirror REST API functionality
4. Identify REST/GraphQL overlap — document every resource that can be modified via BOTH REST and GraphQL. These dual-write surfaces are your RC targets.
5. Test authorization boundaries per mutation — replay mutations as lower-privilege users. Does the server enforce the same authz as the equivalent REST call?
6. Hunt cross-API state desync — find sequences where:
- REST action should revoke access
- GraphQL mutation re-grants or preserves it
- Test the ordering: REST first → GraphQL → check state; then GraphQL first → REST → check state
7. Test for persistent privilege after role/membership changes — remove a user via REST, then call the corresponding GraphQL mutation for that resource. Query current state via both APIs and compare.
8. Probe for IDOR in node IDs — GraphQL global IDs often encode object type + ID. Swap IDs across object boundaries and across account contexts.
9. Check batch query abuse — send arrays of operations to bypass rate limiting or amplify enumeration.
10. Document the exact reproduction chain — for RC bugs, time-based steps must be reproducible deterministically.
---
Payload & Detection Patterns
Partial Introspection Query (not a complete schema export):
{
__schema {
types {
name
fields {
name
type {
name
kind
}
}
}
}
}Minimal Introspection Probe (bypass attempt):
{ __typename }curl introspection test:
curl -s -X POST https://target.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"query":"{ __schema { queryType { name } } }"}' | jq .Field suggestion probe (bypass blind introspection blocks):
{ unknownField }If response returns "Did you mean: [realFieldName]?" — schema is enumerable despite introspection being disabled.
Batch query amplification:
[
{"query": "{ user(id: 1) { email } }"},
{"query": "{ user(id: 2) { email } }"},
{"query": "{ user(id: 3) { email } }"}
]Subscription hijacking (cross-user channel access):
subscription { messageAdded(channelId: "OTHER_USERS_CHANNEL") { content sender { email } } }If subscriptions lack per-user scoping, an attacker can receive real-time events from another user's channel or conversation.
Multi-code OTP/2FA brute-force via alias batching:
mutation {
v1: verifyOtp(code:"000001"){token}
v2: verifyOtp(code:"000002"){token}
v3: verifyOtp(code:"000003"){token}
}Aliases can invoke distinct attempts within one operation if allowed; evaluate per-account/per-operation limits with a small approved test budget. Do not brute-force real OTPs or assert a bypass merely from syntax.
RC desync test pattern (pseudo-sequence):
# Step 1: Grant access via REST
curl -X PUT https://api.target.com/repos/ORG/REPO/teams/TEAM \
-H "Authorization: token ADMIN_TOKEN" \
-d '{"permission":"admin"}'
# Step 2: Revoke via REST
curl -X DELETE https://api.target.com/repos/ORG/REPO/teams/TEAM \
-H "Authorization: token ADMIN_TOKEN"
# Step 3: Re-assert via GraphQL mutation
curl -X POST https://api.target.com/graphql \
-H "Authorization: bearer ATTACKER_TOKEN" \
-d '{"query":"mutation { updateTeamsRepository(input: {repositoryId: \"REPO_ID\", teamId: \"TEAM_ID\", permission: ADMIN}) { clientMutationId } }"}'
# Step 4: Verify persistent access
curl https://api.target.com/repos/ORG/REPO/teams \
-H "Authorization: token ADMIN_TOKEN"Grep for GraphQL in JS bundles:
grep -Eo '(query|mutation|subscription)\s+\w+\s*[\({]' bundle.js
grep -Eo '"(/[a-z0-9/_-]*graphql[a-z0-9/_-]*)"' bundle.jsGraphQL introspection & audit tooling:
# InQL (Burp extension) — visualize GraphQL schema and relationships
inql -t https://target/graphql --generate-queries
# Clairvoyance — brute-force field names when introspection is disabled
python3 clairvoyance.py -u https://target/graphql -H "Authorization: Bearer TOKEN" -w wordlist.txt -o schema.json
# GraphQL Cop — scan for common misconfigurations (introspection enabled, no depth limits, etc.)
graphql-cop -t https://target/graphql---
Common Root Causes
1. Dual-write without atomic locking — developers implement the same resource modification in both REST and GraphQL independently. Neither system is aware the other exists for that resource. State updates aren't serialized or compared.
2. Inconsistent authorization middleware — REST endpoints go through one auth layer (e.g., middleware chain), GraphQL resolvers go through a different resolver-level check. The same action, different enforcement.
3. GraphQL as "new REST" migration — teams add GraphQL mutations that mirror REST functionality without auditing the permission model. The GraphQL version is less mature and skips checks the REST version accumulated over time.
4. Introspection left on in production — default framework settings (Apollo, Graphene) enable introspection in all environments. Developers forget to disable it, treating it as "just documentation."
5. Node ID trust without re-authorization — GraphQL global IDs (base64("ObjectType:123")) are decoded and trusted without verifying the requesting user has access to that specific object.
6. **Mutation side effects not
… (full content is in the download package)
Related skills
- 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
- hunt-nodejs – Hunt Node.js specific vulnerabilities
- hunt-oauth – Hunting skill for oauth vulnerabilities. Built from 19 public bug bounty reports. Use…
- hunt-race-condition – Hunting skill for race condition vulnerabilities.