I had my computer on while watching Sunday Night Football with Caido and Firefox open. I didn’t know what I was looking for, but I needed to check my medical provider’s website to find a FedEx tracking number. Curiosity won, so I jumped into Caido to understand how the site worked. It’s a homegrown vibe coded app.
Spotting the First IDOR
While stepping through requests I noticed an API endpoint called GetUserByID. My response contained my personal information, which made sense. But as I scrolled, more names, emails, and UUIDs appeared—none of them mine. Among the noise, admin details popped out.
What happens if I swap my UUID for the admin’s?
{"id":"REDACTED-REDACTED-REDACTED-REDACTED-REDACTED856"}
The response came back with the admin’s first and last name, date of birth, and address. IDOR confirmed.
Chaining the IDORs
I pivoted to other endpoints and found the same pattern:
/api/getAllUserOrders/api/getMyFiles/api/getAllUserMedications
With these I could enumerate previous orders, medication prescriptions, and administrative contact details. Serious, but I wondered if it could get worse.
Supabase File Exposure
The /api/getMyFiles endpoint triggered a flood of Supabase requests. Filenames like test-results.pdf appeared—belonging to other users. Since I could access my own uploads, I tried swapping the path to point at admin files. That worked, exposing patient lab records.
At this point I recorded a walkthrough video, documented everything, and emailed the admin contact (whose address was leaked by the API).
He replied within five minutes and said he’d look into it. Fifteen minutes later another reply landed and he wanted to chat. That same day we jumped on a Google Meet. He was thankful, asked about my background, and invited me to continue testing the test for any other vulnerabilites. I already had ideas queued up.
Escalating Privileges
Back in Caido I found /api/updateUser. The response showed my role as null, so I modified the request:
{
"payload": {
"first_name": "Travis",
"last_name": "",
"role": "admin"
},
"userId": "a791fe89-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
The response returned:
{"success":true,"data":[{"role":"admin", ...}]}
I was now an admin. Navigating back to the site revealed a new tab worth exploring.
Digging Deeper: Supabase service_role
During login I noticed a lot of Supabase traffic. I’m not a software engineer, but I’m a professional Googler. A quick search for “Supabase exploits” led me to DeepStrike’s article.
The author used GET /rest/v1/users?select=*. I tried GET /rest/v1/ instead and received the entire schema. Inside were tables for admin files, patient notes, and internal messages. A follow-up request to /rest/v1/admin_files?select=* listed every admin file.
With the service_role API key present in the request headers, I could GET, POST, PATCH, and DELETE. I confirmed impact by downloading a file via curl.
Responsible Disclosure
We had a follow-up meeting scheduled for Monday (one full week of testing). I found the service_role issue Sunday night and saved it for the call. He immediately grasped the severity, ended the meeting to remediate, and rescheduled.
At our final meeting he shared some AI features they were building, expressed gratitude, and advised we will continue working together as they launch new features.