UUID IDOR Exploitation: When Leaked GUIDs Become Access Keys
“We use UUIDs, so IDOR is impossible.” You will hear that in triage tickets. UUIDv4 is hard to brute force. It is not an access-control check. If the API returns another user’s object whenever you present their GUID, you still have BOLA — you only changed the difficulty of discovery.
Discovery beats guessing
You rarely need to brute a 122-bit space. You need leaks:
- JSON lists that include other users’ UUIDs (“share,” “activity,” “autocomplete”)
- Analytics beacons and frontend state dumps
- CSV/PDF exports, invoice PDFs, email links
- Referer headers sending
https://app/files/<uuid>to third parties - Mobile API responses that embed related-object IDs the UI never shows
- Support tickets and error messages echoing internal identifiers
I keep a scratchpad of every UUID the target emits. Cross-check them later against GET /api/resource/{uuid} with a second account.
The two-account proof
Account A creates an object; note id=11111111-.... Account B’s session calls the same route with A’s UUID. Success means missing ownership checks. Also try:
- HEAD/GET on download endpoints
- GraphQL
node(id:)or*_by_pkfields - Batch APIs that accept arrays of UUIDs (one foreign ID in a mixed list)
- Secondary keys:
publicIdvs internaluuidconfusion
UUIDv1/v6 can leak timestamps and MAC-ish entropy; that is a side story. Focus on authorization first.
“Obscurity” responses from triagers
If they claim unpredictability as mitigation, ask whether any feature discloses IDs. Paste the leak source in the report. Severity tracks data sensitivity, not the ID format. A private medical attachment reachable by UUID is critical even if the UUID never appears in /users.
Sequential integers are easier to spray; UUIDs just force you to hunt disclosure. Same CWE family — CWE-639.
Testing shortcuts
Burp match/replace can swap a path segment UUID while you browse as B. Autorize helps once you have a victim UUID inventory. For GraphQL, try global ID decoding (Relay-style base64) — sometimes “opaque” IDs are TypeName:uuid in a thin wrapper.
Fix story
Server-side ownership checks on every direct object reference. Prefer capability URLs only when they are unguessable and treated as secrets (rare, easy to log-leak). Short-lived signed URLs for downloads beat forever-valid GUIDs in path segments.
Writeup tip
Title with the object type (“UUID IDOR on /api/documents/{id} returns another tenant’s PDF”). Put the leak and the replay next to each other. That kills the “not predictable” debate in one screenshot pair.
GUIDs buy you entropy. They do not buy you authorization.