Cache poisoning is a type of attack where a threat actors exploits the vulnerable server and cache, so that a harmful HTTP response is served to other legitimate users. Essentially, cache poisoning is possible because of HTTP Response Splitting and flaws in the web application.
What is Cache and How Cache Works ?
Cache is storage location that is reserved to collect temporary data to help websites, browsers, and applications to load faster for the same information being requested to reduce the response time from application server. Cache is typically situated in between the user and server.
Whenever a cache receives a request for a resource in server, it has to check whether it has saved the exact copy of data to serve or it has to reach the server to fetch it.
Most large organizations host their own cache using software’s or opt to Content Delivery Network (CDN) like Cloudfare in order to give their users fast response. Smaller organizations that do not have the bandwidth to support their own caching opt into using services like Cloudflare as well.
Now, cache techniques seems simple. But, it brings in some risks along with it. Which needs to be detected at the earliest to being the victim of such attacks.
Challenges of HTTP Protocol with cache handling
The risk comes from identifying whether the two requests are trying to load the same resource or not, by request match for each and every byte would be ineffective. HTTP request are inconsequential due the requestor browsers.
GET /story/post.php?story=1 HTTP/1.1 Host: comics.com User-Agent: Mozilla/5.0 … Firefox/57.0 Accept: */*; q=0.01 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate
Caches use the concept of cache keys, where few specific components of HTTP request are considered to identify the resource requested.
Attack use case 1:
GET /story/post.php?story=1 HTTP/1.1 Host: comics.com User-Agent: Mozilla/5.0 … Firefox/57.0 Cookie: language=en; Connection: close
GET /story/post.php?story=1 HTTP/1.1 Host: comics.com User-Agent: Mozilla/5.0 … Firefox/57.0 Cookie: language=de; Connection: close
The example above changes the Cookie value from en to de, which is English to Dutch. This request would end up submitting wrong language to the second client/visitor. So, the risk occurs when unkeyed input may be stored and served to other users. Unkeyed inputs refer to parts of a request that a cache ignores, such as the type of browser a user is using, for example. An open source Burp Suite extension called Param Miner automates the process of identifying unkeyed inputs. Developers can use the Vary HTTP header to specify additional request headers should be keyed. The Vary header tells any HTTP cache which parts of the request header, other than the path and the Host header, to take into account when trying to find the right object. It does this by listing the names of the relevant headers, which in this case is Accept-Encoding. If there are multiple headers that influence the response, they would all be listed in a single header, separated by a comma. The real trouble starts when an application is deployed without realizing that sites support any header based input in cache techniques.
Attack use case 2:
Identify the parameter which can be used to dilute the request(unkeyed input) For this, We can use Param Miner extension in burp suite to detect web cache poisoning vulnerabilities. Below figures illustrates the example of Cache poisoned server.
GET /en?story=1 HTTP/1.1 Host: www.example.com X-Forwarded-Host: blogs
HTTP/1.1 200 OK Cache-Control: public, no-cache … X-Forwarded-Host :<meta property="og:image" content="https://blogs/cms/welcome.gif" />
In the response , we can see that X-Forwarded-Host header is used by the application to serve suspicious gif content inside a meta tag to the users.
Attack use case 3:
GET /en?poisoning=1 HTTP/1.1 Host: www.example.com X-Forwarded-Host: a."><script>alert(1)</script>
HTTP/1.1 200 OK Cache-Control: public, no-cache … <meta property="og:image" content="https://a."><script>alert(1)</script>"/>
We can confirm that now we can cause a response that will execute arbitrary JS whoever requests it. Finally, we need to confirm that this response is stored in cache and served as common response the request comes to server. The header controls for cache are for safety but we don’t want to come to a conclusion that attack won’t work unless we attempt it first. Just submit the request without malicious payload and directly fetching it from different browser on different device.
GET /en?poisoning=1 HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK … <meta property="og:image" content="https://a."><script>alert(1)</script>"/>
This ends up that our attack was success and response is served from the cache. Even though there is no header to confirm cache is present. We have seen just a simple poisoning were there are numerous types of poisoning available in wild to explore.
How to Prevent Cache Poisoning Attack
- First make sure you’re using user headers from request to use a cache key.
- Always make the application free from browser side attacks.
- Have strong cache techniques and restrict the contents.
- As a defense use the cache defenses and test it before deploying the application.