Account takeover vulnerability in Azure’s API Management Developer Portal
How an Account Takeover vulnerability, discovered during a routine customer engagement, became a candidate for responsible disclosure, via the Microsoft Security Research Center Researcher Portal.
In December 2022, when testing a customer’s instance of the Azure API Management (APIM) Developer Portal, I discovered a critical vulnerability, that allowed any user to bypass the intended authorization controls, and log in as the administrator account. The administrator account has full access to all the data stored within the application, and the ability to edit any part of the application’s content. The following is my detailed writeup of how an Account Takeover vulnerability, discovered during a routine customer engagement, became a candidate for responsible disclosure, via the Microsoft Security Research Center researcher portal.
What is APIM?
Azure’s APIM service is, simply put, a place to configure, manage, and limit access to your APIs. It is built to be the gateway to all your APIs, receiving requests, and forwarding them to the respective backend services. APIM additionally allows you to view your API documentation and interact with the imported APIs in the fully customizable Developer Portal. This is the Developer Portal that I was assigned to test.
As per Microsoft’s documentation, developers can:
- Read API documentation
- Call an API via the interactive console
- Create an account and subscribe to get API keys
- Access analytics on their own usage
- Download API definitions
- Manage API keys
The key takeaway here is that the application stores sensitive data, which should not be exposed to anyone without explicit permission. The developer portal, for the most part, handles the protection of its data extremely well, requiring an administrator to assign individual APIs to products, which can then be assigned to individual developers, so that they can manage and use the APIs in the portal.
Exploring the Developer Portal
As previously mentioned, I was testing an instance of the Azure APIM Developer Portal. We were provided with some limited accounts that could see various APIs, and we had the ability to register our own accounts. These self-registered accounts were not assigned anything, and therefore had a very blank view of the developer portal.
Testing began with the reconnaissance phase, where I quite quickly noticed a request towards a “/users” endpoint. This was clearly intended to return a user’s details when visiting the user’s profile page.
In our case, it returned the user details for email@example.com. The interesting part of this request is the user ID in the URL path. This indicated that it might be possible to retrieve other users’ details, if we could guess their user IDs. Unfortunately, the ID used (in this case “63e3c2f746346113f040540d”) is extremely long, and therefore unguessable without a very powerful computer, and a considerable amount of time.
I tried several common techniques to bypass the ID implementation, including simply submitting another known user’s valid user ID, but even then, the application responded with a “403 Forbidden” error code. As a last-ditch effort, I turned to one of my favourite Burp Suite (a very common tool for web application penetration testing) extensions, Param Miner. You can read more about Param Miner here https://github.com/PortSwigger/param-miner. For our purposes however, all you need to know is that Param Miner will add random parameters to a request, and verify if there are any unlinked parameters that might have an impact on the application’s response.
To my surprise, Param Miner came through, and found a query parameter called “uid”. This looked promising, let’s investigate.
Finding the Bug
With our new “uid” parameter in hand, we can start testing various values to see how the application responds.
Interesting, it seems that despite the user ID provided in the URL (“63e3c2f746346113f040540d”) the application is deciding to search for the user whose ID matches the value provided in the hidden “uid” parameter. Since no user has an ID of “randomString”, “User not found” is returned.
At this point, I realized that despite this interesting parameter, nothing could be achieved without knowing the ID of another user. This was a problem because the user IDs were clearly too long to feasibly guess. However, I had one more idea.
The site’s administrator was not assigned a random ID. We can infer from this, that the hidden “uid” parameter does not run proper authorization checks, allowing us to read the administrator’s details despite the fact that we are not meant to be authorized to do so. This rather simple and comical authorization bypass (“&uid=1”) may be abuseable elsewhere in the application. Let’s take a look.
Finding an Exploit
Any penetration tester knows that you don’t stop when you find something interesting, you poke at it until you can build an exploit of real value. That was the next step. I had to ask myself what I could feasibly do with this bug. Fortunately for me, Microsoft provided some excellent documentation (https://learn.microsoft.com/en-us/rest/api/apimanagement/current-ga/user) on the “/users” endpoint. I tested the bypass on several endpoints, noting that I could successfully retrieve any user’s details and even update users’ profiles, provided I had their user ID. Fortunately, Microsoft provided the perfect use for my bypass that required only the administrator’s user ID.
This endpoint generates a one-time-use login token for the provided user ID, that can be used to log in to a user account without providing the user’s credentials (email and password).
Taking Over the Admin Account:
At this point, I was very excited: “am I about to log in to the site’s admin account with a bypass as simple as “&uid=1”? I gave it a shot.
It would seem that I have indeed been provided with a single sign on token for the administrative user. Time to test it out….
As you can see, we have full access to the APIs (which need to be individually assigned to users) and we can even edit the content on the page using the menu on the left. At this point, I had pushed the bug to its limit and found a practical use for the exploit. As part of the assessment, the finding was reported to the customer.
The Road to a Bug Bounty
After the finding had been demonstrated to the customer, they reached out to their local Microsoft team who recreated the issue and started work on a fix the same day. I also received an email from the Microsoft Cloud engineers who had this vulnerability reported to them:
I immediately started work on recreating the vulnerability in my own azure environment to confirm that this was indeed an issue with all live instances of Azure’s API Management solution. This was in fact the case, and I submitted the bug to Microsoft on behalf of Outpost24 following our responsible disclosure policy. A few short weeks later, the bounty team over at Microsoft contacted me:
As of 22nd February 2023, the fix was released, and I was given the go ahead to write this very blogpost. Microsoft has removed support for the hidden “uid” parameter, preventing unauthorized access to any of the “/users” endpoints.
For fellow penetration testers, I highly recommend checking interesting endpoints for hidden parameters using Param Miner. I personally have had great success with it, and my assumption is that unlinked parameters are often left completely unprotected.
I would like to say a huge thank you to the local Microsoft team who prompted Outpost24 to submit the disclosure to Microsoft directly. They have been extremely helpful throughout the entire process, from the initial discovery, all the way through to the bounty and the eventual release of the fix.
Finally, this finding is an excellent example of why Outpost24’s manual testing services are worth investing in. Vulnerability scanners can only take you so far when it comes to web applications as they cannot test for vulnerabilities using the context of the endpoint, and certainly cannot think like a human to build higher impact exploits and proof of concepts.