hunt-graphql – skill Bảo mật cho agent AI
Skill săn lỗ hổng GraphQL. Được xây dựng từ 12 báo cáo bug bounty công khai về IDOR qua node() / GID, IDOR ở mutation kể cả các tính năng AI/LLM, IDOR xuyên tenant, SSRF qua argument, batching-DoS, bypass query-cost, SQLi qua argument, broken-object-level-authz, bypass xác thực qua mutation không giới hạn phạm vi, và lộ PII
Cài skill hunt-graphql bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-8426c.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /hunt-graphql – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-8426c.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $hunt-graphql – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-8426c.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /hunt-graphql – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-graphql-8426c.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /hunt-graphql – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
Crown Jewel Targets
GraphQL vulnerabilities are high-value because the attack surface is both broad and deep — a single endpoint can expose entire data models, privilege escalation paths, and cross-API state confusion. Highest payouts occur in:
- 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 — these hit medium-to-high severity reliably
- 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 }
If that returns, introspection may be partially blocked but the schema is discoverable
3. Map the full 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
Full Introspection Query:
{
__schema {
types {
name
fields {
name
type {
name
kind
}
}
}
}
}Minimal Introspection Probe (bypass attempt):
{ __typename }curl introspection test:
curl --max-time 30 --connect-timeout 10 -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 } }"}
]RC desync test pattern (pseudo-sequence):
# Step 1: Grant access via REST
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 -X DELETE https://api.target.com/repos/ORG/REPO/teams/TEAM \
-H "Authorization: token ADMIN_TOKEN"
# Step 3: Re-assert via GraphQL mutation
curl --max-time 30 --connect-timeout 10 -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 --max-time 30 --connect-timeout 10 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.jsInQL / clairvoyance for blind schema enumeration:
python3 clairvoyance.py -u https://target.com/graphql \
-H "Authorization: Bearer TOKEN" \
-w wordlist.txt -o schema.json---
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 mirrored — when a REST action triggers cascading effects (e.g., team removal cascades to permission revocation), the GraphQL equivalent mutation doesn't trigger the same cascades.
---
Bypass Techniques
Defense: Introspection disabled
- Bypass via field suggestion errors — send invalid field names and parse "did you mean X?" responses
- Use
clairvoyanceto brute-force field names against a wordlist - Check JS bundles for hardcoded query strings that reveal the schema
Defense: Depth limiting
- Fragment spread to increase effective depth without hitting the limiter:
fragment F on User { repos { teams { members { ...F } } } }Defense: Rate limiting per IP
- Use batch operations (array of queries in one POST)
- Distribute across authenticated sessions
Defense: Auth checks on mutations
- Test with tokens at different privilege tiers (viewer, member, admin)
- Test unauthenticated — some mutations don't check session at all
- Test with tokens from *different organizations* — multi-tenant IDOR
Defense: WAF blocking __schema
- Alias the introspection field:
{ s: __schema { t: types { n: name } } }- Use HTTP parameter pollution or alternate content-type headers
Defense: Operation whitelisting (persisted queries)
- Check if the server falls back to ad-hoc queries when the
extensions.persistedQueryhash mismatches - Look for a non-whitelisted endpoint (dev, staging, internal proxy)
Alias batching: when it wins races vs when it doesn't
A common claim is "alias batching defeats per-user rate limits and double-spend protections." Whether this actually wins depends on the resolver execution model:
| Resolver type | Behavior on aliased mutations | Alias batching wins races? |
|---|---|---|
| Multi-threaded / DataLoader-batched async | Aliases
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- hunt-grpc – Săn lỗ hổng gRPC: server reflection được bật (liệt kê tất cả service/method), thiếu xác…
- domain-intel – Trinh sát thụ động các subdomain, chứng chỉ SSL, WHOIS và DNS.
- security-ownership-map – Phân tích repository git để xây dựng bản đồ quyền sở hữu bảo mật (người–file), tính bus…
- hunt-host-header – Săn Host Header Injection: đầu độc password reset dẫn đến ATO, web cache poisoning qua…
- attack-tree-construction – Xây dựng cây tấn công toàn diện để trực quan hóa các đường đi của mối đe dọa. Dùng khi…
- ciso-advisor – Lãnh đạo bảo mật cho các công ty đang tăng trưởng. Định lượng rủi ro bằng tiền, lộ trình…
- security-threat-model – Mô hình hóa mối đe dọa dựa trên kho mã, liệt kê ranh giới tin cậy, tài sản, năng lực kẻ…
- hunt-idor – Skill săn lỗ hổng IDOR. Được xây dựng từ 26 báo cáo bug bounty công khai. Dùng khi săn…