July 16, 2026
Workflow Bypass Leading to Unauthorized Access to Sensitive Records (IDOR / Broken Object-Level…
Summary

By Kaisec
2 min read
While testing a multi-step prescription/order workflow on a healthcare-adjacent platform, I found that intercepting and manipulating requests at a specific step allowed me to bypass a client-side validation error, trigger backend state changes out of sequence, and ultimately access other users' records by manipulating a single numeric identifier (_recordId_) in a GET request.
This was a Broken Object Level Authorization (BOLA/IDOR) vulnerability. Because the backend trusted a client-supplied ID without checking that the requesting user actually owned that record, an attacker could:
- Access other users' generated PDF documents (in this case, prescriptions)
- Retrieve sensitive personal information tied to those records (owner contact details, pet name/condition, location data)
- Trigger the platform to send that document to an arbitrary phone number of the attacker's choosing
Discovery
I was walking through a multi-step order/prescription flow as a normal user would, when a step in the process threw a backend validation error ("feeding volume abnormal please retry"). Rather than retrying as intended, I intercepted the request in Burp and froze the flow to see what was actually happening under the hood before the error surfaced.
Continuing through the flow with the intercepted requests held open, the app made a sequence of backend calls:
POST updateRecordStatus
POST updateRecordStatus
POST dispatchDocument
GET getRecordById?recordId=<numeric_id>POST updateRecordStatus
POST updateRecordStatus
POST dispatchDocument
GET getRecordById?recordId=<numeric_id>The final GET request stood out a plain sequential numeric ID, fetched with no visible authorization check tying it back to my session.
The Vulnerability
I changed the _recordId_ value to a nearby sequential number someone else's record and replayed the request.
The app happily redirected me to a PDF rendering view for a completely different user's record. No ownership check, no session-to-record binding just "does this ID exist," full stop.
From there, the workflow allowed the resulting document to be sent to a phone number of my choosing, meaning this wasn't just a read primitive an attacker could exfiltrate someone else's sensitive document to their own device without ever needing to view it in-browser.
Root Cause
- The backend trusted the
_recordId_parameter supplied by the client with no server-side ownership validation - Workflow "steps" were enforced only by the frontend UI flow, not by backend state intercepting and replaying requests let me skip past validation that was supposed to gate progression
- IDs were sequential and predictable, making enumeration trivial once the pattern was identified
Impact
Given the sequential nature of the IDs, this wasn't a one-off it was systematically enumerable. An attacker could have walked the ID space to harvest an unknown number of other users' sensitive records, including personal contact information and health-adjacent data about their pets, and could redirect that data to phone numbers they controlled.
Affected Endpoints
POST /api/updateRecordStatus
POST /api/dispatchDocument
GET /api/getRecordById?recordId=POST /api/updateRecordStatus
POST /api/dispatchDocument
GET /api/getRecordById?recordId=Recommendations
- Enforce object-level authorization: every record fetch should verify the requesting user actually owns or is authorized to view that specific record, not just that the record exists
- Treat workflow steps as server-side state, not client-side sequence a client skipping steps or replaying requests out of order shouldn't be able to reach an end state it hasn't earned
- Move away from sequential, guessable identifiers toward UUIDs or similarly unpredictable IDs, as a defense-in-depth measure (not a substitute for authorization checks)
- Rate-limit and monitor for enumeration-style access patterns on ID-based endpoints
Takeaways
This is a good example of why IDOR/BOLA hunting is often less about finding some exotic bug and more about paying attention to what happens when something breaks. The vulnerability wasn't hiding in normal usage it surfaced because I stopped and looked closely at what the app was doing in the background when I hit an error, instead of just retrying like a normal user would. Freezing the request flow at that moment, rather than letting the error resolve itself, was the actual key that opened the door.