This writeup details a series of vulnerabilities I encountered a few months ago on a single private program. The company did specify that they would like to read the writeup before publication to approve it first. Unfortunately, the private program has since been shut down, and the email account that I was in correspondence with has been disabled, so I have decided to publish my writeup anyway in good faith without any identifiable information, for lack of other options. So here we go.

For the sake of obscurity, I will be referring to the vulnerable entities as Grandparent, Parent and Child. A Grandparent entity oversees several Parent entities, and the Parent entity manages the Child entity (end users). Each entity has an ID value.

Chaining API Vulnerabilities

In this case, the Parent entity’s ID value was a very powerful piece of information to have. Although it was a UUID, that doesn’t mean anything if the value can be obtained through means other than guessing. In this case it was leaked within the API requests sent by a pre-auth user.

There was also a leak of the Grandparent ID in a request fetching content from an AWS bucket, but this is useless pre-auth. The next vulnerability was that there was an IDOR in the account creation system that first allowed the user to assign arbitrary roles (such as Admin) in the Parent entity, and secondly allowed the user to create accounts in any Parent if the Parent ID was known.

At this point, the attacker could escalate to become an administrator in the Parent entity. Next, there was an endpoint for GetParentById that took the Parent ID as a parameter. This endpoint actually required no authentication, and leaked API keys, administrator details, the Grandparent ID, and more sensitive data.

Next, the Grandparent ID could be used alongside the API keys to list all of the Parent entities for that Grandparent, and the corresponding Parent IDs. The privilege escalation issue could then be reused to create arbitrary administrator Child accounts in any Parent of the Grandparent. The end user could effectively take over other Parent entities preauth. The data at play here was incredibly sensitive.

Account Takeover

This issue arose from the endpoint that generated links that could be sent to Child entities for them to authenticate with. However, a weak JWT secret meant that the token could be compromised. Account takeover was possible by modifying the Child ID within the token and re-signing.

Grandparent Entity Creation

There was an issue in the account creation process that allowed the user to create Grandparent entities without the company’s prior approval.

Information Disclosure

The private program’s Grandparent ID was leaking in both the company’s official Github within the commit history, and in a Javascript file used in the main web application. Chained with the earlier issues, they could be used to create admin accounts in the private program’s own Parent entities.

Default Endpoint Behaviour

Removing the Parent ID value from a specific endpoint caused the API to return an arbitrary (production) customer’s Parent ID. I was able to leverage this Parent ID to get both the API key and critical PII from the Parent through various API endpoints.

Cookie Scope

The Parent entity had a dashboard that was used to manage the Parent. Within this dashboard, it was possible to send a link to someone externally, who would have limited access and only be able to access certain features. This is a common role-based access control method in SaaS. However, by reusing the cookies of the limited-access user with the API rather than the dashboard, it was possible to retrieve the API key to perform almost any high-privilege action.


APIs do not always behave consistently. By understanding exactly how a web application works, and understanding the significance of each component in relation to other components, it is possible to uncover a wide variety of issues.

Part 2: Update, Jan 2023

Secondary context path traversal, pioneered by Sam Curry, is another uncommmon issue that occurs in APIs. It can be found when user input is insufficiently sanitised, and sent to a proxy. The proxy appends this value to a path, such as an internal API route, and the user is able to traverse this to perform unintended actions. Due to insufficient obscurity (in my opinion), I have taken down Part 2. Instead I will outline what was possible on that target in general terms.

All in all, it was technically fantastic to hack on and while I wish I could describe it properly, this is all I can do for now.