Web app attacks increased by 52% in 2019 — here’s what to know about how web cache poisoning moved from being a theoretical vulnerability to an exploitable one
Let’s face it: Nobody likes to wait a million years for a webpage to load. This is why the internet has so many blogs on website speed optimisation and how it influences conversion rates. Web caching is one of those breakthroughs that serves a two-fold purpose — it reduces the load on web servers and also improves the end user experience.
Compromising the cache was only considered a theoretical scenario — one too complex to be pulled off practically — up until a few years ago. In the following sections, before we dive into exploiting secondary vulnerabilities (like XSS) through web cache poisoning, we’ll break down what web caching is and how it works.
Let’s get started!
What Is Web Caching?
Caching, in general, can refer to a page cache, a browser cache, or a server cache — all of which are components that store data for faster retrieval. But for our purposes, we’ll be looking at server-side caching. In this case, a server-side cache is a repository that sits between one or many clients and server(s). Its goal is to improve response time by delivering locally saved copies of frequently requested resources instead of querying the back-end server.
The idea here is that by reducing latency (the time lag between when a client requests a resource and when it actually arrives) and delivery speeds, it makes the site seem more responsive.
How Web Caching Works
Let’s consider the following example to explain how caching works:
Suppose there are five users, numbered 1 to 5, who try to fetch the same resource (an image, a webpage, etc.) consecutively, one after the other. What happens is that when user 1 submits the request for the first time, the server-side cache saves a local copy on it. Upon receiving subsequent requests for the same resource from the other users, the response saved on the cache is served instead of having to communicate again with the back-end server. Caching boosts the speed at which the page loads for users, and results in reduced loads on the server, as there are fewer requests to serve.
This brings us to the question – how does the cache identify whether user 2 or 3 is requesting the same resource as user 1? It’s here that the concept of “cache keys” comes into play. While these cache keys may contain an HTTP host, a query string, or an HTTP scheme, they mostly consist of the request line and the host header
Consider the request below:
The cache key in the example request above comprises only of the “GET” request line and the host header while the rest of the request is considered as the unkeyed input.
But what happens when something goes wrong with the unkeyed input? This is where things get really interesting (and dangerous).
What is Web Cache Poisoning?
Much like what the name implies, web cache poisoning is a type of cyber attack that involves a hacker “poisoning” the data cache that’s used to respond to other users’ requests. Essentially, it’s turning these caches into exploit delivery systems that can be used to send harmful responses.
Web cache poisoning relies on unkeyed inputs that are those parts of a request that do not form the “cache key.” James Kettle, Director of Research at PortSwigger, took advantage of the web caches of several websites to turn them into systems that could deliver a payload to other people visiting the site. He says that although cache poisoning has a bad reputation, it’s actually quite simple to exploit.
A typical web cache poisoning scenario can be broken down, as shown below:
- The attacker identifies the unkeyed input either manually or using a tool (like Param Miner, an extension to Burp).
- The attacker crafts a malicious request to see whether it gets reflected in the response without being properly sanitized, or if it affects the processing of other data.
- After a harmful response is received from the back-end server, it’s time for the next step in the process where the attacker tries to cache the response.
- Once that’s done, your exploit gets delivered to any user who accesses the website.
In the next section, we’ll do a quick review of XSS before we see how to exploit it via web cache poisoning.
What Is Cross-Site Scripting (XSS)?
Cross-site scripting flaws have continued to appear in the OWASP Top 10 vulnerability list since its first publication in 2003. Though it moved down in the list in 2017, as other vulnerabilities became more pronounced, it continues to be one of the most common security concerns across applications.
An XSS vulnerability arises when an attacker manipulates HTTP requests from an otherwise trusted resource in a way that it gets reflected in the response without proper input filtering or sanitization. This leads to the execution of malicious code in the victim’s browser and can be used to steal cookies, hijack sessions, spread malware, etc.
XSS attacks can be classified into three main types of attacks:
- stored, or
To give you an example, consider the following HTTP request/response to see how a malicious script gets reflected in the response:
If the application had proper checks (such as input filtering, output encoding, etc.) in place, the input would’ve been discarded as an unexpected input, or the HTML tags would’ve been treated as data upon being encoded. Encoding involves changing the data format from one form to another. For example, characters like “<,” “>,” etc. should always be HTML encoded as “<,” “>” and so on to prevent them from being executed as code or breaking out of context in other ways.
And now, we move on to an example scenario of web cache poisoning. Shall we begin?
Example Scenario Using the Web Security Academy Lab
The scenario described below is from PortSwigger’s vulnerability labs in their Web Security Academy. You can create an account and try out these labs on your own or attempt the challenge on hackxor. The request/response screenshots are taken from the lab that explains using web cache poisoning to exploit unsafe handling of resource imports.
In the following example, I’ve attempted to show how the requests can be modified to poison the cache without getting into the details of how to solve the lab step by step. Once you access the lab, it takes us to a dummy ecommerce website containing a web cache poisoning vulnerability in its home page. Let’s take a look:
1.Once your browser proxy settings are configured to send traffic via Burp, it can be set to capture both requests and responses. The snip below shows the original lab homepage traffic that we’re trying to poison with our malicious payload.
2. The lab also comes with an exploit server where we can store the payload we intend to deliver.
(Note: Including the X-Forwarded-Host as a cache key when it does not hold the same value as the Host field, could’ve prevented this vulnerability.)
4. Once we get a cache hit, the payload gets delivered to any user visiting the same lab home page link.
How Do You Mitigate Cache Poisoning Attacks?
The most secure way to achieve this is to avoid caching altogether or to include everything in your cache keys. However, since in most cases, that’s either not feasible or it defeats the purpose of caching, we can adopt the following precautions:
- Ensure that secondary vulnerabilities don’t exist within your application. Running web scans, conducting manual tests, etc. to assess your application for any potential security flaws is always a good practice.
- Restrict where caching is done and what is cached. For instance, you might want to limit caching at a single point or only cache static responses. Additionally, certain services (like CDNs) may have caching enabled by default and leave websites vulnerable to cache poisoning attacks.
- Be selective about your unkeyed inputs. Disable unkeyed inputs where feasible. Carefully consider what goes into your unkeyed inputs as they can be manipulated to incorporate harmful payloads.
Hopefully, by now, you have a basic understanding of what web cache poisoning is, why it’s no longer considered a theoretical vulnerability, and how placing a cache in front of web servers can provide entry points to exploit secondary vulnerabilities in the application. Since caching improves performance, disabling it might not be sustainable for the most part. However, taking necessary precautions such as regular vulnerability scans, or auditing every page within the application to understand how unkeyed inputs are handled, is equally crucial for avoiding security risks.