IDOR: The Beginner’s Goldmine

Three days of recon and you have a ranked attack surface map: forgotten hosts, sensitive endpoints, parameter rich APIs. Now we hunt. We start with IDOR, Insecure Direct Object Reference, because it is the single best bug class for a beginner to learn first. It is the highest frequency, highest impact class on bug bounty platforms, it needs no exotic tooling, and it teaches the one skill that separates real hunters from scanner runners: thinking about authorization.
This is the first vulnerability class in the series, and everything you do today points directly at the parameter rich endpoints your recon surfaced. The map was not busywork. This is what it was for.
As always, inside scope only, on programs that authorize you or systems you own. IDOR testing touches other users’ data by nature, so you test with your own two accounts, never real victims.
What IDOR actually is
An IDOR happens when an application uses an identifier you can see and change to fetch an object, but forgets to check whether you are allowed to access that object.
Picture an invoice page at a URL like /invoice?id=1001. You view your invoice, 1001. Out of curiosity you change it to /invoice?id=1002. If the server returns someone else’s invoice, that is an IDOR. The application correctly identified which object you asked for, and completely failed to check whether it was yours.
That is the whole bug, and it is deceptively deep. The server answered the wrong question. It asked which invoice did you request, got 1002, and returned it. It never asked is invoice 1002 yours to see. The identifier was a direct reference to an object, and access control on that object was missing. Hence Insecure Direct Object Reference.
The reason this is the beginner’s goldmine: it requires no payload crafting, no encoding, no deep protocol knowledge. It requires noticing an identifier and asking a question. The skill is entirely in the mindset, and that mindset, who is allowed to access this, is the foundation of almost all web security.
Why IDOR is everywhere
Developers build the happy path. A user logs in, sees their own data, and the code fetches by ID. In testing it always works, because the test user only ever asks for their own IDs. The missing check, is this object yours, is invisible until someone deliberately asks for an ID that is not theirs. Nobody does that in normal use, so the bug ships.
It scales with every object the app exposes: invoices, messages, orders, profiles, files, API keys, settings, reports. Each is a potential IDOR if access control was skipped. Modern apps expose thousands of objects by ID, and the check is easy to forget on any one of them, especially on new features and internal endpoints, which is exactly where your recon pointed you.
The five patterns that repeat
Across disclosed IDOR reports, the same shapes recur. Learn these and you know where to look.
Sequential IDs. The identifier is a number that increments: 1001, 1002, 1003. Trivial to enumerate, just add or subtract. The GitLab Model Registry IDOR is a clean example: the model ID was sequential, so decrementing it walked through other users’ private models. Report: https://hackerone.com/reports/2528293
GraphQL node substitution. GraphQL exposes objects by a node ID. Swapping one node ID for another often fetches an object you should not see, because the resolver fetched by ID without an ownership check.
Session object misbinding. The action is tied to an object ID in the request rather than to your session. Change the ID and you act on someone else’s object, delete their item, change their setting, expire their session.
Cross tenant boundary failure. In multi tenant apps, an ID from your tenant works against another tenant’s data because the query filtered by ID but not by tenant.
Error response leakage. Even when access is denied, the error response leaks the object’s data. This is the recent HackerOne case: an endpoint returned HTTP 422 access denied, but the error body contained private report details anyway. The check existed on the status code and not on the response body. $15,000 bounty. Report: https://hackerone.com/reports/3862641
That last one is worth sitting with, because it connects to the whole mindset. The developer thought they had access control, they returned an error, but the data leaked through the error path. Access control is not a status code, it is about what data actually crosses the boundary.
How to hunt IDOR, step by step
Point this at the parameter rich endpoints from your recon.
Step 1: set up two accounts. You need account A and account B, both yours. A is the attacker, B is the victim. This is how you test safely, your own B instead of a real user. Note B’s object IDs: its invoice IDs, message IDs, profile ID, whatever the app exposes.
Step 2: find the identifiers. Browse the app as account A with a proxy like Burp capturing traffic. Every request that fetches or acts on an object by ID is an IDOR candidate. Look in:
URL paths: /api/user/4021/profile
Query params: /invoice?id=1001
POST bodies: {“order_id”: 88213}
Headers: X-Account-Id: 4021
GraphQL: node(id: “…”)
Your recon params.txt and js_endpoints.txt from Part 3 are exactly the hunting ground here.
Step 3: swap the ID. As account A, take a request for A’s object and replace the ID with B’s object ID. Send it. Three outcomes:
- You get B’s data. IDOR confirmed.
- You get access denied, clean, no data. Good, access control works here.
- You get an error, but B’s data leaks in the error. IDOR via error leakage, the $15k pattern.
Step 4: test every verb. Access control often exists on read but not on write, or on one action but not another. For each object, test viewing, editing, deleting, and any state change, with B’s ID from A’s session.
Step 5: confirm impact. A real IDOR report shows clear cross account access. The cleanest proof uses two accounts: log A’s request reaching B’s data, and show B’s account to prove the data is really B’s. That two account proof is what makes the report undeniable.
Tools, kept minimal
IDOR needs almost nothing:
A proxy: Burp Suite (community edition is enough) or mitmproxy, to see and modify requests
Two test accounts on the target
Burp’s Repeater to replay a request with a swapped ID
Burp’s Intruder or a script to enumerate sequential IDs
The tooling is trivial on purpose. IDOR is a thinking bug, not a tooling bug.
Writing the IDOR report
Since this series teaches reporting too, here is the shape that gets IDORs paid, drawn from how the strong disclosed reports are written:
Title: IDOR in [endpoint] allows accessing other users’ [object]
Summary:
One sentence: which endpoint, which object, what unauthorized access.
Steps to Reproduce:
Create account A and account B.
As B, note object ID (e.g. invoice 1002).
As A, send [request] with B’s ID.
Observe A receives B’s data.
(Include the exact requests and screenshots.)
Impact:
What an attacker gains: read/modify/delete other users’ data,
scale (sequential IDs = all users), sensitivity of the data.
Remediation:
Enforce an ownership check: verify the authenticated user owns
the requested object before returning or modifying it.
The GitLab report earned its bounty partly on clarity: it provided two accounts, exact GraphQL queries, and showed decrementing the ID to reach other users’ models. Clear reproduction with two accounts is the template.
The mindset to carry forward
IDOR teaches the question that underlies most web bugs: not can I reach this object, but am I allowed to, and does the server actually check. Once you are asking that on every request, you are hunting like a real researcher, and the same authorization lens carries into access control bugs, privilege escalation, and multi tenant failures in the weeks ahead.
Your recon found the endpoints. IDOR is the first thing to test on them, because it is common, high impact, and exactly the kind of bug that lives on the forgotten, parameter rich endpoints you surfaced.
Homework for Day 4
- Pick a parameter rich endpoint from your recon
attack_surface.md. Confirm it is in scope.
- Set up two accounts, A and B, both yours. Record B’s object IDs.
- With a proxy, find every request that references an object by ID. List them.
- For each, swap A’s ID for B’s and test all verbs: view, edit, delete, state change. Watch for direct data, and for data leaking through error responses.
- Read both disclosed reports end to end: GitLab sequential ID IDOR (https://hackerone.com/reports/2528293) and HackerOne error leakage IDOR (https://hackerone.com/reports/3862641). For each, note the exact missing check.
If you find a real cross account access on an in scope target, document it with the two account proof and write it up in the report shape above. That is potentially your first valid finding, and it came straight out of the recon you spent three days building.
Day 5 takes the authorization mindset deeper into broken access control and privilege escalation: not just reading another user’s object, but gaining powers you were never meant to have, with a real disclosed report broken down end to end.