How me and Omar found a secondary context that allowed us to take over the whole company. I will list down what we found within our approach in the target. This research with all the vulnerabilities took us about 3 weeks.
1- Accessed The Admin Dashboard that led us to takeover over 3,000 companies listed down for our target.
2- All The Employees Real Finger Prints, Classified Employees Documents With Their PII.
3- Bypassed The KYC Check Allowed Us To Takeover Phone Numbers.
4- Bypassed Previous Vulnerabilities via Secondary Context.
At first, we are going to explain how we found the vulnerability.
While hunting the target we found an endpoint that returned a weird response when manipulating the request. This error was caused when we added special characters after the path.
As you could see In the response it returned a path that seemed interesting. When you observe the path in the request and response it seems somehow similar to each other, but the path in the response seemed longer which at first we indicated that the path in the response is the Backend API. This is an Image to explain it well.
The first thing we thought, and you might be thinking the same, is can we access the backend API somehow?
We instantly tried to access it via path traversal to access the backend API.
As usual, when we tried to path traversal a WAF is blocking us.
However, we didn't stop there. Checked the JS files and found a production domain that doesn’t use a WAF. This domain was a lifesaver for us.
After we checked, luckily enough it holds the same API and DB of the main domain we were testing.
As soon as we realised it had the same API we tried path traversal to achieve secondary context on the target but we stumbled upon a 404 request that was interesting.
You will probably ask why is it interesting it's just a 404 Error response, but if we check a 404 request in our target without the path traversal.
This clearly means that we achieved the backend API when the 404 Error was different from the Frontend API.
Now, we made sure that we got what we wanted, but there is something else it feels like we are lost at this point because there is a 404 Response and we don’t know how to access the internal endpoints.
Of course, we tried fuzzing and funny enough it worked!
We found an application.wadl
endpoint internally that holds all the internal endpoints for this microservice, which is responsible for the payment system documentation in the Backend API.
An important detail is that these microservices operate on different internal domains, and their mappings are determined by the Frontend API. For example, the path in the frontend API /b2cSRV_NewSim/
maps to the backend API path /company/new/sim
. However, the internal domain for this microservice is different, which is an internal domain [http://uat-satellite.redacted.com/]
. Keep in mind that this is still in the production domain we found.
I explained this because of the application.wadl
we found has only been found on one microservice which is one that is responsible for the payment system.
There is an interesting endpoint which is /extractOrderDocument/{id]/.
We obviously tried requesting the PDF file and it returned a huge file.
Let’s look at what the PDF file holds?
As you could see we reached documents of internal employees leaking PII and real fingerprints.
While digging into the application.wadl
there is a path we found that retrieves all the customers invoices via mobile number through the backend API.
It retrieves PII for customers via their mobile numbers.
How we get accessed to over 3000 companies that are under the company’s control?
The second week of our research we tried to reach more endpoints but found nothing, and as I explained above the front end path of /soa_b2b_billGen/v1 that only had the listing of all the endpoints of the microservices.
We were completely lost at this, but we kept on digging and fuzzing more!
These resources helped us to find out a super admin panel.
But we had a problem the response responded back with 405 Method Not Allowed. We changed it to the POST method but the frontend responds with 405 method not allowed either.
Meaning the frontend API only aceepts it as a GET method to be processed through the backend API. It also means that superadmin login will only accept the POST method through the backend.
So, we need to find a POST request that is vulnerable to secondary context path traversal.
After a long search for a vulnerable POST request, we found it!
We reached the internal admin login page. It was a moment of both joy and frustration because it required an account.
However, in the response, there was a JavaScript file that we checked, and it contained internal endpoints.
But there was validation in place, meaning we needed to obtain the login credentials; otherwise, we couldn’t do anything.
We thought, why not try some default credentials? Maybe we’d get lucky? Unfortunately, it didn’t work.
Later, we realised that we could perform username enumeration because the error message revealed whether the username was incorrect or if it was just the password.
When we reached this, we were hoping to get access somehow. Tried brute-forcing with a regular password wordlist and it didn't work.
We started losing hope and were about to give up and move on to something else. But then, we decided to give it one last try.
We thought, why not create a custom wordlist? That’s when we went back to our shared notion, continued working on it, and tried to gather any words related to the company that could help us. We used the GAP extension in Burp Suite to help us gather all the words/parameters.
With the help of ChatGPT, we gathered around 70,000 costume password lists and deduplicated words.
We tried brute forcing again with the costume list we made and it worked!
At that moment we were super excited we obtained the access token of the superAdmin.
Now we have full control of the company’s super admin panel meaning we control more than 3,000 companies. Including all of the company’s branches
We can change passwords, National IDs, and any sensitive information.
Bypassing KYC Checks For Number Transfer Leading To Account And Number Takeover
This is a telecom company meaning if we take over the number it means the user has no longer access to the number.
It involves verifying a customer’s identity to ensure the legitimacy of their request to prevent fraudulent activities such as unauthorized SIM swaps or identity theft.
A key aspect of this process is document verification, where the customer is required to provide official identification to prove their identity.
This may include government-issued IDs such as a passport, national ID card, or driver’s license.
During our testing, when we attempted the original request without interacting with the backend, the system responded with a 417 Expectation Failed error.
This indicates that the request did not meet the system’s KYC check, preventing us from transferring the victim’s number to our document and another telecom operator.
After trying to request it via the backend API, it went through and transferred the number with my document to another operator!
We were able to bypass the KYC verification process and successfully transfer any phone number to an attacker’s document and telecom operator of choice.
This is a diagram to explain it perfectly
How were we able to access all user and company data and bypass all previous vulnerabilities?
When we were testing the company we reported a lot of Broken Access Control vulnerabilities and it has been fixed.
However, We thought why not try to reach the backend API and bypass our previous reports?
We requested it via the backend API, and It worked!
With this technique, we bypassed over 20 reports we submitted previously, for anyone wondering the path of the Backend API was obtained by generating an error in the path.
Let’s get into depth why this is happening.
Authentication and authorization checks were only enforced in the Frontend API, allowing us to bypass them entirely by making direct requests to the Backend API.
Instead of sending a normal request, we used path traversal [../../] to break out of the expected request flow and directly access a sensitive backend endpoint.
The Frontend API saw a normal request and forwarded it without blocking it. However, the Backend API interpreted it differently and returned user data without requiring authentication.
This worked because the Frontend API didn’t perform proper request sanitization, while the Backend API normalized paths differently, leading to a mismatch in how the request was interpreted.
Why this works?
The request we sent looked normal to the Frontend API, so it didn’t block it. But once it reached the Backend API, it got resolved differently.