BOLA In The Wild: Mapping PII Storage Through API Calls - 2026
First bug found in the wild!!!
Background
This bug was found while testing a volunteer management web application, disclosing much more than phone numbers and emails but also personal documents such as ID's, passports, and birth certificates. The issue was responsibly disclosed to the organization, who remediated it prior to this publication. All sensitive details, endpoints, and identifiers have been redacted.
In the following sections, we'll go through my thought process when it came to discovery, client communication, reporting, and remediation.
Initial Recon: Watching the app talk
Like any other site, i could have first fuzzed for directories, files, vhosts, subdomains etc. But i like to keep things simple, so i began to scope out the basic functionality of the site by clicking buttons and analyzing requests in the Console/Network tab of Dev Tools to see what requests the app was making to the server(while also intercepting requests in Burp Suite).
At first i noticed some interesting javascript files, but what i was mostly interested in were the API calls being made.
Being a volunteer management web app for an organization, a volunteer has the ability to create their profile, or a "draft", detailing different information about themselves. I had created a test account, so most of the values shown below are null.
GET /api/web/user/draft/example@gmail.com 200{"_id":"<REDACTED>","createdAt":"","email":"<REDACTED>","key":"<REDACTED>","name":"","firstName":"","lastName":null,"contact":{"email":"<REDACTED>","phone":"<REDACTED>","homePhone":null,"socialNetworks":[]},"birthday":0,"gender":null,"profileUrl":null,"coverUrl":null,"signatureUrl":null,"status":{"status":"Preregister","reason":null,"date":0,"dateEnd":null,"dsc":null,"caseId":null,"updatedAt":null,"updatedBy":null,"statusHistory":[],"registerDate":0,"residentOtherCountry":null,"eventsHistory":null,"general":{"fullAddress":null,"street":null,"streetNumber":null,"neighborhood":null,"zipCode":null,"stateDepartment":null,"city":null,"country":null}
When first creating a draft, you're able to search for a location in the search bar, which made another API call. On the front-end client side, all that showed up was the location of the place being searched. However, the response to this particular request revealed personal information about all volunteers in all locations.
At this point i knew something was off, why was i able to see all of this information?
At this point the following information for any user was disclosed:
first and last name
phone number
email
birthday, gender
Following the API Trail
I wanted to see if there was any documentation for the API used by this organization. It wasn't /api/web/docs, but the error message down below gave it away.
I tried /api/v1/openapi.json, but it didn't work, so i tried /api/v2/openapi.json, and yes, it did work. I now had all of the api endpoints and their use cases.
There are so many things i tried using the api calls that were documented, but i simply didn't have enough permissions. But there were some api calls that allowed me to slowly but surely gather and chain all of the information needed to map PII storage.
The API call below lists all of the volunteers in a specific state in a specific city. Revealing information such as their:
name
email, phone number
address
path where sensitive documents were stored/uploaded
The cityId in the query could be changed to CT-USCA-3, CT-USCA-4, listing other volunteers' information. This also applied to other states (e.g. /api/web/city/CT-USTX-1)
Identifying Broken Object-Level Authorization
Now that i had email addresses, and the exact path where sensitive information was stored, i uploaded my personal birth certificate and id as a normal user would.
But i wanted to test if the appropriate measures were in place protecting others from viewing other volunteers documents. I really wanted to make sure that i was able to demonstrate impact without overstepping, so i created another test account with a different email address, and i wanted to see if my second test account was able to see my first test account's documents.
It turns out i could! A BOLA vulnerability existed where any authenticated user was able to access data they are not authorized to view.
This meant the authorization checks failed to properly ensure that an authenticated user had the sufficient permissions or privileges to request and view specific data or perform certain operations
Responsible Disclosure & Retesting
I quickly took screenshots, blurred sensitive information, and created a basic web application assessment report using a template i have, sent it and waited...
In just a few hours i got a response from their security/development team asking when it would be a good time to schedule a video call. I said whenever was fine, so they called me about 30 minutes later.
I was really nervous, they all seemed so serious, a moment of silence, no one spoke, until one of them thanked me for finding the vulnerabilities, then the other also thanked me, each of them asked me how i found them and how did i stumble upon them π. We had some laughs and some interesting conversations, towards the end they asked me if i could finish up the report and send it later tonight or the next morning, so i did.
I sent the report in, and waited a week. Just this monday i got a call asking if i could restest to see if the vulnerabilities existed, and just yesterday we had another call outlining that the vulnerabilities no longer existed in the web app π
I had no idea my first bug would turn into a full web app pentest including client communication, reporting and documentation, and restesting π