From IDOR to proven Critical
A read IDOR is a Medium until you demonstrate impact; the discipline of mass enumeration, PII at scale, and cross-tenant write that earns the severity.
The bug is the easy part
You change id=1001 to id=1002 and you get someone else’s invoice. Good, that
is an insecure direct object reference. But a triager reads that and asks the
question you have to answer first: so what? One record, one user, maybe your own
test account in disguise. That is a Medium on a good day, and it is the version
of the report that sits in the queue.
The severity does not come from the access-control flaw. It comes from what the flaw lets you reach, at what scale, and whether you can prove it. The discipline is turning “I can read object 1002” into “I can read every object, here is the shape of the exposure, and I never touched a real customer to show you.”
The two-account diff comes first
Before scale, before anything, prove it is actually a cross-user bug and not your own data. Two accounts, change only the token.
That establishes the bug. Now you escalate it from a Medium to its real severity, and there are three ways the impact actually lands.
Escalation one: mass enumeration
A single leaked record is an anecdote. The same endpoint iterated across the ID space is a breach. If the references are sequential or guessable, the question is no longer “can I read one” but “what fraction of the dataset is reachable.”
GET /api/v2/invoices/100001 HTTP/2
Host: example.com
Authorization: Bearer <attacker_token>{"id":100001,"account":"victim-corp","email":"a.buyer@victim.example","total":4900,...}Increment the ID and the structure is identical for the next account. The demonstration is not “I dumped the database.” It is showing that IDs are sequential, that the authorization check is per-request and absent, and crucially the count of distinct accounts reachable. I prove the range exists by sampling its boundaries, not by pulling every record. A handful of requests across a wide span establishes “the whole space is open” without exfiltrating anyone’s data, and that is the number that moves severity.
Escalation two: PII at scale
What comes back matters as much as how much. An endpoint that returns more fields than the UI shows is its own problem. If each enumerable object carries email, full name, address, government ID, or payment fragments, you are not describing an access-control bug anymore, you are describing a PII exposure of N people. The severity argument writes itself: identifiers x records reachable x sensitivity of fields. I document the field shape from a couple of records (my own victim account, plus the boundary samples that prove the range) and let the arithmetic carry the rest. I do not need to read a thousand real people to prove a thousand real people are exposed.
Escalation three: cross-tenant write
Read is the floor. If the same broken reference works on a PUT, PATCH, or
DELETE, the finding becomes an integrity and availability bug across the tenant
boundary, which is almost always Critical.
PATCH /api/v2/invoices/100002 HTTP/2
Host: example.com
Authorization: Bearer <attacker_token>
{"status":"void"}If an attacker in one tenant can mutate an object owned by another, you can void invoices, change account settings, or reassign ownership for users you have no relationship with. To prove a write safely I act only on my own victim account, never on a stranger’s record, and I show the state before and after. That single controlled mutation, between two accounts I own, proves the class without harming anyone.
The discipline is the point
The thing that separates a Medium from a Critical is not a cleverer payload, it is the refusal to stop at “the API returned data.” Prove it with two accounts. Establish scale by sampling the range, not draining it. Quantify the PII by its shape, not by hoarding it. Prove write with one controlled mutation on an account you own. You end up with a report a triager cannot argue down, and you never put a real user’s data through your tooling to get there. That restraint is not a courtesy. It is what makes the impact claim credible.