Receber por email
← Voltar para o catálogo

hunt-graphql

Habilidade para caçar vulnerabilidades GraphQL, construída a partir de 12 relatórios públicos de bug bounty que cobrem IDORs (via node()/GID, mutações, cross-tenant), SSRF via argumento, DoS por batching, bypass de custo de query, SQLi via argumento, autorização em nível de objeto quebrada, bypass de autenticação via mutações não escopadas e exposição de PII por falta de autorização em nível de campo. Utilize ao caçar GraphQL em qualquer alvo.

2.6kestrelas
Atualizado há 4 meses

Ver no GitHub ↗ Licença: NOASSERTION

Como adicionar

/plugin marketplace add elementalsouls/Claude-BugHunter

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/hunt-graphql/svg)](https://www.skillteca.com.br/skills/hunt-graphql)

Skills relacionadas

Ver todas de Dados e Análise →

Alerta por categoria

Receba novas skills de Dados e Análise toda segunda

1 email curto, só com as skills novas de Dados e Análise. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Crown Jewel Targets

GraphQL vulnerabilities are high-value because the attack surface is both broad and deep — a single endpoint can expose entire data models, privilege escalation paths, and cross-API state confusion. Highest payouts occur in:

  • Platform APIs (GitHub, Shopify, Stripe-tier targets) where GraphQL mutations interact with REST APIs managing the same resources
  • Race conditions between GraphQL mutations and REST endpoints where state synchronization is non-atomic — these hit medium-to-high severity reliably
  • Authorization persistence bugs where team/org/repo membership state is controlled by one API but readable/writable by another
  • B2B SaaS platforms where one tenant affecting another via schema traversal = critical
  • Internal admin GraphQL endpoints accidentally exposed to lower-privilege users

The GitHub reports demonstrate the crown jewel pattern: privilege that should be revoked persists because two APIs disagree on ground truth.

Attack Surface Signals

URL Patterns:

/graphql
/api/graphql
/v1/graphql
/query
/gql
/graph
/api/v2/graphql
/internal/graphql

Response Headers:

Content-Type: application/json  (with query body)
X-Request-Id + no REST-style path params = likely GraphQL

JavaScript Source Patterns:

// grep for these in JS bundles
"query {"
"mutation {"
"__typename"
"apollo"
"ApolloClient"
"graphql-tag"
"gql`"
"operationName"
"GRAPHQL_URI"

Tech Stack Signals:

  • Apollo Server/Client in JS bundles
  • Relay in React apps
  • graphene or strawberry (Python), graphql-ruby, gqlgen (Go), Lighthouse (Laravel)
  • POST requests with {"query": "..."} body shape in Burp history
  • __schema or __type in any response = confirmed GraphQL

Recon Sources:

  • github.com search: "graphql" site:target.com
  • Wayback Machine for /graphql paths
  • JS bundle scanning with LinkFinder or getallurls

Step-by-Step Hunting Methodology

  1. Discover the endpoint — spider JS bundles, check /graphql, /api/graphql, review Burp passive scan hits for application/json POST with query fields
  1. Test introspection — send the full introspection query. Even if blocked, try field-level enumeration:

``graphql { __typename } `` If that returns, introspection may be partially blocked but the schema is discoverable

  1. Map the full schema — use InQL (Burp extension) or graphql-voyager to visualize relationships. Specifically look for:
  2. Mutations that modify ownership, permissions, or membership
  3. Mutations that mirror REST API functionality
  1. Identify REST/GraphQL overlap — document every resource that can be modified via BOTH REST and GraphQL. These dual-write surfaces are your RC targets.
  1. Test authorization boundaries per mutation — replay mutations as lower-privilege users. Does the server enforce the same authz as the equivalent REST call?
  1. Hunt cross-API state desync — find sequences where:
  2. REST action should revoke access
  3. GraphQL mutation re-grants or preserves it
  4. Test the ordering: REST first → GraphQL → check state; then GraphQL first → REST → check state
  1. Test for persistent privilege after role/membership changes — remove a user via REST, then call the corresponding GraphQL mutation for that resource. Query current state via both APIs and compare.
  1. Probe for IDOR in node IDs — GraphQL global IDs often encode object type + ID. Swap IDs across object boundaries and across account contexts.
  1. Check batch query abuse — send arrays of operations to bypass rate limiting or amplify enumeration.
  1. Document the exact reproduction chain — for RC bugs, time-based steps must be reproducible deterministically.

Payload & Detection Patterns

Full Introspection Query:

{
  __schema {
    types {
      name
      fields {
        name
        type {
          name
          kind
        }
      }
    }
  }
}

Minimal Introspection Probe (bypass attempt):

{ __typename }

curl introspection test:

curl -s -X POST https://target.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"query":"{ __schema { queryType { name } } }"}' | jq .

Field suggestion probe (bypass blind introspection blocks):

{ unknownField }

If response returns "Did you mean: [realFieldName]?" — schema is enumerable despite introspection being disabled.

Batch query amplification:

[
  {"query": "{ user(id: 1) { email } }"},
  {"query": "{ user(id: 2) { email } }"},
  {"query": "{ user(id: 3) { email } }"}
]

RC desync test pattern (pseudo-sequence):

# Step 1: Grant access via REST
curl -X PUT https://api.target.com/repos/ORG/REPO/teams/TEAM \
  -H "Authorization: token ADMIN_TOKEN" \
  -d '{"permission":"admin"}'

# Step 2: Revoke via REST  
curl -X DELETE https://api.target.com/repos/ORG/REPO/teams/TEAM \
  -H "Authorization: token ADMIN_TOKEN"

# Step 3: Re-assert via GraphQL mutation
curl -X POST https://api.target.com/graphql \
  -H "Authorization: bearer ATTACKER_TOKEN" \
  -d '{"query":"mutation { updateTeamsRepository(input: {repositoryId: \"REPO_ID\", teamId: \"TEAM_ID\", permission: ADMIN}) { clientMutationId } }"}'

# Step 4: Verify persistent access
curl https://api.target.com/repos/ORG/REPO/teams \
  -H "Authorization: token ADMIN_TOKEN"

Grep for GraphQL in JS bundles:

grep -Eo '(query|mutation|subscription)\s+\w+\s*[\({]' bundle.js
grep -Eo '"(/[a-z0-9/_-]*graphql[a-z0-9/_-]*)"' bundle.js

InQL / clairvoyance for blind schema enumeration:

python3 clairvoyance.py -u https://target.com/graphql \
  -H "Authorization: Bearer TOKEN" \
  -w wordlist.txt -o schema.json

Common Root Causes

  1. Dual-write without atomic locking — developers implement the same resource modification in both REST and GraphQL independently. Neither system is aware the other exists for that resource. State updates aren't serialized or compared.
  1. Inconsistent authorization middleware — REST endpoints go through one auth layer (e.g., middleware chain), GraphQL resolvers go through a different resolver-level check. The same action, different enforcement.
  1. GraphQL as "new REST" migration — teams add GraphQL mutations that mirror REST functionality without auditing the permission model. The GraphQL version is less mature and skips checks the REST version accumulated over time.
  1. Introspection left on in production — default framework settings (Apollo, Graphene) enable introspection in all environments. Developers forget to disable it, treating it as "just documentation."
  1. Node ID trust without re-authorization — GraphQL global IDs (base64("ObjectType:123")) are decoded and trusted without verifying the requesting user has access to that specific object.
  1. Mutation side effects not mirrored — when a REST action triggers cascading effects (e.g., team removal cascades to permission revocation), the GraphQL equivalent mutation doesn't trigger the same cascades.

Bypass Techniques

Defense: Introspection disabled

  • Bypass via field suggestion errors — send invalid field names and parse "did you mean X?" responses
  • Use clairvoyance to brute-force field names against a wordlist
  • Check JS bundles for hardcoded query strings that reveal the schema

Defense: Depth limiting

  • Fragment spread to increase effective depth without hitting the limiter:
fragment F on User { repos { teams { members { ...F } } } }

Defense: Rate limiting per IP

  • Use batch operations (array of queries in one POST)
  • Distribute across authenticated sessions

Defense: Auth checks on mutations

  • Test with tokens at different privilege tiers (viewer, member, admin)
  • Test unauthenticated — some mutations don't check session at all
  • Test with tokens from *different organiz
Compartilhar X LinkedIn