In this blog post, let's learn about the lesser known vulnerability known as HTTP Parameter Pollution or HPP, which affects multiple modern applications. We will go over what's the reasons behind this bug, a real-world experience and possible mitigation.
What is HPP?
First, let us answer the burning question: what is HTTP Parameter Pollution? In simple words, this vulnerability occurs when query parameters in an HTTP request are supplied to an application in such a crafted manner that it processes the request in an unnatural way, leading to disastrous outcomes. But here's the catch: most applications crash or spew out error messages when such unexpected inputs are encountered, but in the case of applications vulnerable to HPP malicious inputs are processed as though nothing unusual has happened. But the results are the ones that cause the damage.
There are no strict standards when it comes to HTTP parameters and how to interpret multiple inputs. RFC 3986 for "Uniform Resource Identifier (URI): Generic Syntax" mentions that a query is just a URI component which is used to fetch or point to a resource to whatever is located at that URI. When an application's intended query format is twisted, the developers might not have taken that into account, resulting in twisted outputs. All of this depends on the parsing of HTTP requests and the related query parameters, about which the developers might be unaware and unintentionally leave their products vulnerable to HPP.
Understanding HPP
Let us take an example of a real-life HPP bug we encountered in a very popular app. It used OTP-based authentication where a user supplied an email and an OTP was sent to the specified email. Upon entering the OTP on the next screen, login was granted.
Say the login endpoint was "/api/v0/send-otp". Following was the POST request body for generating OTP:
{
"email" : "[email protected]",
"gen_otp" : "yes",
"key" : "some_base64_string"
}
Pretty straightforward:
- Email is submitted to the backend logic.
- Corresponding OTP is sent to that email.
- The submission screen is shown.
But from an attacker's perspective, there could be multiple ways to fiddle with this request in hopes of generating unusual outputs. We tried a number of crafted requests, but none of these worked. Two of which are shown below:
{
"email" : "[email protected]",
"gen_otp" : "yes",
"key" : "some_base64_string"
"email" : "[email protected]",
"gen_otp" : "yes",
"key" : "some_base64_string",
} // attempt to make the application parse two emails in a single request
{
"email" : "[email protected]\[email protected]",
"gen_otp" : "yes",
"key" : "some_base64_string"
} // delimiting the email parameters with `\n` so that backend sends same OTP to both.
We had almost given up but tried to use one last delimiter (and felt dumb we hadn't thought of this before). Instead of \n
we used a ,
(a comma). The request looked like this:
{
"email" : "[email protected],[email protected]",
"gen_otp" : "yes",
"key" : "some_base64_string"
}
Lo and behold, we received the same OTP on two different emails! Taking over accounts after this was trivial as there was no 2FA facility. We didn't have access to the application source code, but we can guess that the comma somehow made the application think there are two emails to which the OTP has to be sent. Hope this example gave some clarity on how HPP works.
Types of HPP
Now HPP can be split into categories:
-
Server-side HPP: When the attacker directly targets the application and sends a request with polluted parameters, it is called server-side HPP. Our real-life example in the previous section was an example of server-side HPP.
-
Client-side HPP: While server-side HPP is a straightforward attack on the vulnerable application, client-side HPP also involves a middle stage: the victim user. An attacker crafts a malicious URL which leads to the resulting webpage having polluted parameters. Let's take an example to better understand this.
Let's assume in a banking application, the password reset page is athttp://xyzbank.com/resetPass?username=test
. When visited, the webpage shows your username and one hyperlink to send a reset code to your email. This hyperlink is in form:<a href = "/sendCode?username=test&[email protected]"> Send reset code to your email </a>
.
Since the application is vulnerable to HPP, the attacker forges the following URL:http://xyzbank.com/resetPass?username=test&[email protected]
. This will lead to webpage has the link with injected payload:<a href = "/sendCode?username=test&[email protected]&[email protected]"> Send reset code to your email </a>
. As soon the user clicks this hyperlink, the backend application will take[email protected]
as the valid email and the reset code will be sent to the attacker. This is just one simple scenario to provide better clarity towards client-side HPP (where victim interaction is involved).
There are three types of client-side HPP:- Reflected HPP: The example we took above is a case of reflected HPP, where the victim needs to interact with the malicious URL to reach the webpage with polluted hyperlinks. Reflected attacks fail if the adversary is not able to make the victim click these URLs. This is usually achieved by ways of phishing or social engineering, first making the victim believe that they are interacting with legitimate resources and then making them click it.
- Stored HPP: This happens when the malicious inputs are stored inside an application database (unlike reflected HPP, where things happen on the fly). Let's revisit our previous example. If somehow the attacker is able to make
[email protected]
stored in the web application so that whenever/resetPass
is accessed, the reset password hyperlinks always have attacker mail in the polluted parameter. This will lead to anyone using the password reset functionality being exploited (and the attacker wouldn't have to resort to phishing, social engineering and other less reliable routes!) - DOM-based HPP: When the polluted parameters are injected inside Javascript (instead of the response of a request), it is known as DOM-based HPP. It is called "DOM" based because when a webpage loads, a DOM object for the relevant javascript is created. If the injection point is inside the DOM object, the webpage will create the DOM object with the malicious payload and the resulting javascript will be polluted.
Mitigation Strategies
Now we know what HTTP parameter pollution is and what kind of varieties exists in this vulnerability (quite a few). HPP is the problem, what is the solution? First thing firsts, the developer needs to know how the platform they are working on parses multiple HTTP parameters. That's the key to handling unexpected/polluted parameters. The below image lists a few of the popular web servers with their parsing functionalities:
Fig: Popular webserver and how they parse parameters. Source: Wikipedia
Even if the developers are using custom-made APIs, they should take care of unexpected combinations in which parameters can be input. Further, to prevent any kind of client-side HPP (reflected, stored or DOM-based) proper URL encoding and sanitization of input are extremely necessary in order to perform uniform parsing at the backend. Remember, treat every input as malicious!
Conclusion
Although HTTP parameter pollution is a less popular category of vulnerability compared to other more significant and prevailing bugs like XSS, CSRF, etc, that does not diminish the negative impact a successful HPP exploitation can do. HPP is just the first step of an attack that can lead to sensitive information exposure, account takeovers and many other devastating effects on an organization. This article was meant as an introduction to this bug class which is relatively less known and we advise digging through more research to get an even better understanding.