Web Cache Poisoning Unkeyed Header
Param Miner -https://github.com/PortSwigger/param-miner
Burp Suite Pro
Burp Suite Comparer
My goal in this blog is to refine my understanding of web cache poisoning concepts and spread awareness of the amazing yet simple tools available for anyone on the internet. The labs I will be using throughout this series are provided by the company Port Swigger. Port Swigger is most known for their Burp Suite application - A popular website penetration testing tool.
Web Cache poisoning is an advanced attack where a bad actor takes advantage of a web servers cache behavior to route a harmful HTTP response to other people visiting the website.
At a fundamental level Cache poisoning is due to the architecture of cache handling, more than specific servers or programs.
When hosting a website to support large amounts of traffic, the costs and energy associated can ramp up so quickly as user traffic increases it can cause scalability and availability issues.
A solution for this is Cached Pages, which allows the server to send a cached response instead of fetching its webpage from scratch. This reduces the need for sockets and network traffic.
The server has to decide when a cached response is and is not appropriate and in order to make this differentiation the server looks for Keyed input in the HTTP Request headers. Keyed input is simply flagged header parameters that are used to determine if a request with these parameters has been called before. Typical the GET and Host are the evaluated parameters, and if the Get and Host request match, then a cached page will be served up.
The issue here is with parameters that are not reviewed to determine the cache state of a specific page. What we can do, is actually change the unchecked parameters such as cookies or X-Forwarded-Host, and insert our own requests. When the server evaluates if a page needs to be cached, it only checks the Keyed input and ignores the rest. This is the point of exploitation whereby an attacker can manipulate Unkeyed Input, and the server will re-cache the web page with the malicious parameters found in Unkeyed Inputs.
Kill Chain Cycle
There are two stages involved in the kill chain life cycle. Firstly the attacker has to generate a response from the back end server, that will ultimately contain a dangerous payload. The testing for this is done with standard alert script payloads.
Secondly the attacker has to actually cache their response on the server so it can be delivered to victims.
Hands on Lab
Lets look at the portswigger Lab titled: Web Cache Poisoningwith an unkeyed header
Launching the Lab brings us to a shopping website. The content of the page is not important to us, but rather the HTTP response when we navigate to, or request this home page.
In order to read and review the HTTP response, we utilize Burp Suite and set up our proxy with intercept enabled.
Making the request to the home page of our target website we receive and catch the response.
In this response we cannot tell by default what inputs are keyed for evaluating cached responses. The initial challenge description informs us that this server uses the X-Forwarded-Host Header.
Testing Vulnerable Parameters
We have a request and the knowledge of which HTTP header is vulnerable, in this case it is the X-Forwarded-Host Parameter. If we are working on a CTF, bug bounty or real world environment we would have to enumerate for vulnerable header parameters and one tool that can help with that is the Burp Extension Param Miner.
You can also identify unkeyed headers by manually adding random headers into the request and looking for any effect on the response. Sometimes the output can be difficult to detect and using Burp Comparer can be very helpful for this investigative work.
Before we modify the request, it is important to understand the use of a cache buster. If you are testing a website and accidentally inject a redirect or malicious code this will trigger for everyone and you could be in trouble. So to avoid this we add a Cache Buster to ensure our requests have a unique cache key and are only served to us. The best place to insert a Cache Buster is in the keyed parameter "Get". If we are successful in getting the response we want, we can then remove our cache buster and have confidence the same result will happen as we cache our input with the server.
Modify the Request
lets start by adding our cache buster to the intercepted response, so our respones are unique to us.
From here we added the parameter X-Forwarded-Host: apples.com
Sending this request to our target server generates a favorable response where we actually see that our apples.com string was referenced, right before /resources/js/tracking.js. This could be due to old code, or active code that has another purpose with the X-Forwarded-Host.
In theory we would want to spin up our own server and create a malicious payload with a custom URL address to inject into this X-Forwarded-Host parameter. This could be done with something like Heroku and Apache.
But for the purposes of this lab and being practical, Port Swigger has provided us with a mock server to configure a malicious response.
We see below the option to add a file and file path. We will match this to the referenced file path in the servers response to our Injected X-Forwarded-Host parameter. /resources/js/tracking.js.
In the body of this Java Script file we will place alert(document.cookie) and then copy our servers address.
We inject our servers address into the X-Forwarded-Host: and sending this request provides an OK response. So we remove our cache buster and send the request again. Something important to note is that because this is a Web Cache Poisening attack we have to actually cache our record. There is fortunately a header parameter that can tell if our request is from the server, or from the cached pool and this parameter is known as X-Cache: miss/hit.
If X-Cache displays miss, it means our request went all the way to the server, if it returns hit, it means our request came from the cache pool. So it is common to first send the request once, to place the existing keyed cache, with our new cache containing unkeyed input which goes totally ignored.
Below we send our malicious request for the first time and receive a miss.
The second time we send the request, we can see we received a hit, which means all requests moving forwards will load our X-Forwarded-Host param.
Here is our final payload below where we have removed the cache buster and provided our URL in the X-Forwarded-Host.
We then browse to the web page and receive our Java Script Alert Popup.