The penetration testing team was tasked with mobile and web application security assessments for an undisclosed banking institution. The scope was limited and only included the API provided to the Client’s customers.
We were able to construct an attack allowing us to conduct unsolicited transactions on behalf of other customers, leading to a complete security compromise. The case described below stands out because the attack relied on flaws in business logic and session management of the application, not injection or remote code execution vulnerabilities.
At the beginning of the penetration test it was discovered that the web application shares its API with the mobile app. The API functionality was mapped, and several helper requests were discovered to provide the penetration testing team with additional information about the application. The most useful of these was a request returning session attribute information, which allowed us to create a behavior model of the authentication mechanism.
During the testing, we found out that a session token is provided not only for authenticated users but also to conduct password resets. The application provided a so-called “anonymous” session, which allowed access to a restricted subset of API endpoints. After enumerating the session attributes, the team discovered that the username field of the session object was filled before the password reset was completed. Then, we decided to test all requests that accepted an anonymous token, and a mobile-only request was found that handled repeated authentication from a mobile device. To our surprise, it was found out that the androidID required to authenticate is not validated. Additionally, as the request is a part of an authentication chain and is meant to be processed after proper authentication, it regenerated the session object and provided the caller with an updated token, no longer an anonymous one. (It should also be noted that even if the androidID was properly validated, the vulnerability would still be considered critical as it is possible to gather the ID by an attacker).
The vulnerability allowed access to any other account in the system in case the attacker knew relevant details to start the password reset procedure.
To further exploit gained access to other accounts, the payment protection mechanism also would need to be bypassed. All payment requests in the application required an OTP password to be entered, but the implementation of the mechanism had a critical vulnerability, allowing us to complete any transaction. The request for payment authorization did not only include the OTP password, but also the UUID returned by the OTP request to the API gateway. It was discovered that while the OTP is properly validated, the application does not store any information on the UUID ownership. This allowed us to validate any transaction requests by issuing OTP requests from our own accounts, receiving the OTP normally, and the using the UUID/OTP pair to validate transactions from other accounts.
In the end, chaining the two vulnerabilities allowed the penetration testing team to conduct any transactions from other accounts without proper authentication.
This case is a very good example of why manual penetration tests are valuable — the team achieved compromise without administrator access to the application, not using any known exploits or discovering injection/deserialization/other RCE flaws. The vulnerabilities used could not be discovered by any Web application scanner, as the requests used presented well-documented behavior. We understand the importance of manual testing, and that is why most of our web penetration test project time is dedicated to it. To learn more about our web penetration testing process and methodology, please visit the Security Testing Services page.