Abusing HTTP Misconfigurations
Identifying Unkeyed Parameters
In a web cache poisoning attack, we want to force the web cache to serve malicious content to other users. To do so, we need to identify unkeyed parameters that we can use to inject a malicious payload into the response. The parameter to deliver the payload must be unkeyed since keyed parameters need to be the same when the victim accesses the resource. This would mean the victim has to send the malicious payload themselves, effectively resulting in a scenario similar to reflected XSS.
As an example, consider a web application that is vulnerable to reflected XSS via the ref parameter. It also supports multiple languages in a language parameter. Let's also assume that the ref parameter is unkeyed, while the language parameter is keyed. In this case, we can deliver the payload using the URL /index.php?language=en&ref="><script>alert(1)</script>. If the web cache then stores the response, we successfully poisoned the cache, meaning all subsequent users that request the resource /index.php?language=en get served our XSS payload. If the ref parameter was keyed, the victim's request would result in a different cache key, thus the web cache would not serve the poisoned response.
Thus, the first step in identifying web cache poisoning vulnerabilities is identifying unkeyed parameters. Another important takeaway is that web cache poisoning in most cases only helps to facilitate the exploitation of other vulnerabilities that are already present in the underlying web application, such as reflected XSS or host header vulnerabilities. In some cases, however, web cache poisoning can turn un-exploitable issues in a default/plain web server setting into exploitable vulnerabilities.
Unkeyed request parameters can be identified by observing whether we were served a cached or fresh response. As discussed previously, request parameters include the path, GET parameters and HTTP headers. Determining whether a response was cached or not might be hard. In our lab, the server tells us via the X-Cache-Status header, however, in a real-world setting this may not be the case. Instead, we can achieve this manually by changing parameters and carefully observing the response. For instance, if we change a parameter value and the response remains the same, this indicates that the parameter is unkeyed and we were served the same cached response twice. Let's start with a basic example to showcase a basic cache poisoning scenario.
Unkeyed GET Parameters
Our web application is a simple site displaying some text and allowing us to embed our own text:
The web application sets the GET parameter language and our content is embedded via the content parameter. Let's investigate if either of those parameters are unkeyed. To do so, we first send an initial request with only the language parameter. The first request results in a cache miss, while the second response was cached (as indicated by the X-Cache-Status header in the response):

When we now send a different value in the language parameter, we can see that the response differs and we get a cache miss. Therefore, the language parameter has to be keyed:

We can apply the same logic to the content parameter. When we send the following series of requests, we can observe the following behavior:
- Request to
/index.php?language=test&content=HelloWorldresults in a cache miss - The same request to
/index.php?language=test&content=HelloWorldresults in a cache hit - Request to
/index.php?language=test&content=SomethingDifferentresults in a cache miss again
Since the third request triggers a cache miss, we can deduce that the cache key was different from the first two requests. However, since only the content parameter is different, it has to be keyed. Therefore, both the language and content parameters are keyed, so no luck here.
A little more investigation of the web application reveals that attempting to access the admin panel and using the link to go back sets a third parameter called ref. Let's again apply our testing to find out if this parameter is keyed or unkeyed. We can observe the following behavior:
- Request to
/index.php?language=valuewedidnotusebefore&ref=test123results in a cache miss - The same request to
/index.php?language=valuewedidnotusebefore&ref=test123results in a cache hit - Request to
/index.php?language=valuewedidnotusebefore&ref=Helloalso results in a cache hit
This time, the third request also triggers a cache hit. Since the ref parameter is different, however, we know that it has to be unkeyed. Now we need to find out whether we can inject a malicious payload via the ref parameter.
Before we move on, here are a few remarks on this technique of determining keyed and unkeyed parameters. This works well in our test environment, however, in a real engagement where the target site is potentially accessed by a large number of users during our testing, it is close to impossible to determine whether we received a cached response due to an unkeyed parameter or because another user's request resulted in the response being cached. We should therefore always use Cache Busters in a real-world engagement, which we will discuss in a later section.
Now, to determine whether the ref parameter is exploitable or not, we need to determine how this parameter influences the response content. We can see that its value is reflected in the submission form:

Since no sanitization is applied, we can easily break out of the HTML element and trigger a reflected XSS like so:
GET /index.php?language=unusedvalue&ref="><script>alert(1)</script> HTTP/1.1
Host: webcache.htb
Note: Remember to use a value for the language parameter that was never used before to avoid receiving a cached response.
This allows us to poison the cache for any user that browses the page in our targeted language. Our goal is to force an admin user to reveal the flag by requesting /admin.php?reveal_flag=1 which we are unauthorized to do. We are not going into detail about the XSS payload here but we can achieve this with a payload similar to the following. For more details, check out the Cross-Site Scripting (XSS) module here.
<script>var xhr=new XMLHttpRequest();xhr.open('GET','/admin.php?reveal_flag=1',true);xhr.withCredentials=true;xhr.send();</script>
This results in the following cache poisoning request, assuming our victim visits the site with the language=de parameter (after adding "> for XSS injection):
GET /index.php?language=de&ref=%22%3E%3Cscript%3Evar%20xhr%20=%20new%20XMLHttpRequest();xhr.open(%27GET%27,%20%27/admin.php?reveal_flag=1%27,%20true);xhr.withCredentials%20=%20true;xhr.send();%3C/script%3E HTTP/1.1
Host: webcache.htb
After poisoning the cache and waiting for a while, the admin should visit the site, trigger our XSS payload and reveal the flag for us.
Unkeyed Headers
Similarly to unkeyed GET parameters, it is quite common to find unkeyed HTTP headers that influence the response of the web server. In the current web application, assume we found the custom HTTP header X-Backend-Server which seems to be a left-over debug header that influences the location a debug script is loaded from:

We can apply the same methodology from before to determine that this header is unkeyed. Since this header is reflected without sanitization, we can also use it to exploit an XSS vulnerability. We can thus use the header to deliver the same payload as before with the following request:
GET /index.php?language=de HTTP/1.1
Host: webcache.htb
X-Backend-Server: testserver.htb"></script><script>var xhr=new XMLHttpRequest();xhr.open('GET','/admin.php?reveal_flag=1',true);xhr.withCredentials=true;xhr.send();//
/ 1 spawns left
Questions
Answer the question(s) below to complete this Section and earn cubes!
Click here to spawn the target system!
Target:
Click here to spawn the target system!
+10 Streak pts
Table of Contents
Introduction to HTTP Misconfigurations
Introduction to HTTP MisconfigurationsWeb Cache Poisoning
Introduction to Web Cache Poisoning Identifying Unkeyed Parameters Web Cache Poisoning Attacks Advanced Cache Poisoning Techniques Tools & PreventionHost Header Attacks
Introduction to Host Header Attacks Authentication Bypass Password Reset Poisoning Web Cache Poisoning Bypassing Flawed Validation Host Header Attacks PreventionSession Puzzling
Introduction to Session Puzzling Weak Session IDs Common Session Variables (Auth Bypass) Premature Session Population (Auth Bypass) Common Session Variables (Account Takeover) Session Puzzling PreventionSkills Assessment
Skills Assessment - Easy Skills Assessment - HardMy Workstation
OFFLINE
/ 1 spawns left