| name | hunt-idor |
| description | Systematic IDOR / BOLA hunting methodology. Use when testing any endpoint that accepts an ID, UUID, slug, filename, or reference to a user-owned object. |
Hunting IDOR / BOLA
The mental model
Authorization is separate from authentication. A logged-in user should only access their objects. IDOR exists where the server trusts that a client would only pass IDs it owns.
Two accounts minimum
Always hunt IDOR with two distinct accounts (call them A and B). For privilege escalation, add an admin/manager account.
Where IDs live (check all of them)
- URL path:
/api/users/123/invoices/456
- Query string:
?user_id=123&doc=456
- Body params: JSON, form-encoded, multipart
- Headers:
X-Account-Id, X-Tenant-Id, Referer
- Cookies:
session_user, tenant
- WebSocket messages
- GraphQL variables
ID formats that hide vulnerabilities
- Sequential ints: obvious, try ±1
- UUIDv4: "unguessable" — look for places they leak (emails, notifications, search, autocomplete, error messages)
- Hashed/encoded: base64 the ID, try modifying inner value; some apps use
base64(user_id:timestamp)
- Composite:
tenant-123-doc-456 — change either half
- Slugs:
/company-name/resource/thing — try another company's slug
Method matrix
For every endpoint, test:
- GET → read other user's object
- PUT/PATCH → modify other user's object
- DELETE → delete other user's object
- POST with parent ID → create in another user's context
Common blind spots (high-yield hunting ground)
- Export / download endpoints — CSV/PDF/ZIP with ID param
- Bulk operations — pass array of IDs including other users'
- Old API versions —
/api/v1/ often less strict than /api/v2/
- Admin panel ID in body — regular user passes
role: admin
- Invitation flows — accept invite meant for another email
- Webhook config — set another tenant's webhook to your URL
- File references —
?file=../other_user/doc.pdf
Response fingerprinting
Authorization check bypassed successfully:
- 200 with full data → confirmed IDOR
- 200 with empty/sanitized data → partial; try other verbs
- 403 with different body lengths depending on ID → oracle; can enumerate
- 500 → misconfigured check, often exploitable
Authorization enforced:
- 403/404 consistently regardless of ID existence → likely safe
Reporting
- Show the request from account A accessing account B's resource
- Include a diff of normal vs. cross-tenant response
- State impact in record terms: "Any authenticated user can read all X records across all tenants."