BOOK THIS SPACE FOR AD
ARTICLE ADIn the next installment of the WWWWWH series, we’ll be discussing the Who, What, When, Where, Why, and How of Web Cache Poisoning. I started this series to cover security vulnerabilities from an end-to-end perspective, so make sure to check out the rest of the series here. This holistic view is meant to look past the How of exploiting vulnerabilities that so many people (myself included) dwell on, and makes sure to think about vulnerabilities as a real-life scenario.
Web Cache Poisoning is a vulnerability that allows an attacker to inject a payload into the cache of a web application to deliver a malicious response to any user who accesses that cache while visiting the site. This attack only lasts for the duration of the life of the cache. A cache works by sitting between the origin server and the user, saving particular responses to requests for a fixed amount of time. This is in an attempt to not overload the server and to decrease latency for the user. By poisoning the cache, you can effectively serve a malicious payload to a (potentially) massive amount of users.
Who?
When poisoning the cache of an application, you’ll be looking to target the users of said application. This attack is difficult to target a single, specific user unless you know exactly when the user will be accessing the system. If this is intended to be a targeted attack, depending on the traffic to the site, you may catch other users in the crossfire. Generally, a busy application will be the target with the intention to serve the payload to as many users as possible.
What?
This attack will be targeting, well, the web cache. Web cache poisoning is highly dependent on what can get cached, taking advantage of flaws in implementation to serve the harmful HTTP response to other users. A poisoned cache is more a means of distribution than a standalone vulnerability and will often be chained with other attacks depending on the context.
When?
Since web caches have a Time-to-Live (TTL), this attack yields the best results when performed while a heavy influx of users are visiting/using the site. TTL refers to the period a cached copy of a resource is considered valid and can be used without checking back with the origin server. Once the TTL, expires, the cache is considered stale and a new request to the origin server is typically made to fetch fresh content. TTL is not universally fixed and can vary based on different factors, such as the max-age in the Cache-Control header, or a server configuration. For maximum impact, you’ll want to study peak hours the application is in use.
Where?
To perform this attack, you’ll need to look for parameters that can manipulate the web cache and store a payload. You’ll do this by identifying keyed and unkeyed inputs. Keyed inputs are how a cache identifies an equivalent request by comparing a subset of the components, determining if it can serve the user back a cached response. But, cache poisoning relies on manipulation of the unkeyed inputs. These are request components that are excluded and, crucially, ignored completely. You’ll generally find unkeyed inputs in headers, but can also be found in ports, query strings, query parameters, and parameter parsing quirks.
Why?
As mentioned above, web cache poisoning is more of a means of distribution rather than a vulnerability in itself. You will be looking to chain this vulnerability with other attacks to leverage the wide distribution in an attempt to steal credentials, exploit cookie handling vulnerabilities, deliver XSS attacks, exploit unsafe handling of resource imports, deface the application, or to serve users faulty information.
How?
The first step of performing a web cache poisoning vulnerability as an attacker is to find an entry point. After identifying a web application utilizing a cache, evaluate requests for unkeyed inputs. For this example, lets say the Cybersec Cafe has a web application hosted at app.cyberseccafe.com. When you first enter the application, you send the following GET request to load the homepage:
GET / HTTP/2Host: app.cyberseccafe.com
...
From an initial glance, you may not necessarily notice anything out of the ordinary here. There are no parameters, no queries, no cookies. You are just simply retrieving the home page of the app.cyberseccafe.com application. However, you receive the following response, and suddenly notice things of interest:
HTTP/2 200 OKContent-Type: text/html; charset=utf-8
Set-Cookie: session=xxRedactedxx; Secure; HttpOnly; SameSite=None
Set-Cookie: vulnerableCacheCookie=na-prod-cache; Secure; HttpOnly
Cache-Control: max-age=30
Age: 0
X-Cache: miss
Content-Length: 509
...
Analyzing this request, we find there are some new pieces that we may fancy:
Set-Cookie: session=xxRedactedxx
You notice that in the response, a session cookie is set. However, this is going to be standard behavior for many applications.Set-Cookie: vulnerableCacheCookie=na-prod-cache;
While I’ve intentionally notated this header to point out the vulnerability, this should begin to peak our interest in general. It’s notating that there is a cache that is being used for this site — specifically the Northern America production environment cache. Could we potentially poison the cache for all of North America?Cache-Control: max-age=30
It looks like the cache lasts for 30 seconds before making a request back to the origin server. This means if we are able to cache an attack, it will be delivered to whomever accesses that cache while visiting the application for the following 30 seconds.Age: 0
This is the current age (elapsed time) of the cacheX-Cache: miss
This portion notates that our request has not been cached. It will be notated with a “hit” if our request is cached.Now that you’ve refreshed the page, you notice that the two set cookies appear in your new request along with the Cache-Control header notating the max age.
GET / HTTP/2Host: app.cyberseccafe.com
Cookie: session=xxRedactedxx; vulnerableCacheCookie=na-prod-cache
Cache-Control: max-age=0
...
You send the request again to see what happens. Looking through the response, you search and notice that the vulnerableCacheCookie is reflected inside of the HTML of the page.
HTTP/2 200 OKContent-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=30
Age: 4
X-Cache: hit
Content-Length: 10899
...
<html>
...
<script>
item = {
"vulnerable":"na-prod-cache"
}
</script>
...
</html>
How perfect! It’s not only reflected, but reflected inside of a <script></script> tag. This is a perfect opportunity to use this reflected input to deliver an XSS payload. So, you craft up an XSS payload to alert the user that they have been hacked:
GET / HTTP/2Host: app.cyberseccafe.com
Cookie: session=xxRedactedxx; vulnerableCacheCookie=test"-alert('Learn more about cybersecurity at the CybersecCafe!!')-"test
Cache-Control: max-age=0
...
Send this request until you notice the X-Cache header in the response now says hit, alerting us that our payload is now cached. You notice now that your payload is reflected in the body of the response. You’ve successfully poisoned the cache!
HTTP/2 200 OKContent-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=30
Age: 1
X-Cache: hit
Content-Length: 10906
...
<html>
...
<script>
item = {
"vulnerable":"test"-alert('Learn more about cybersecurity at the CybersecCafe!!')"test"
}
</script>
...
</html>
With this attack, the following alert will pop up for anyone that visits the application while the payload is still cached, letting everyone know they can learn more about cybersecurity at the Cybersec Cafe!
While this is certainly a very simplified scenario of a web cache vulnerability, it illustrates the potential danger that can be unveiled with it. If app.cyberseccafe.com were say, an extremely popular online shopping application, and you were able to deliver this XSS payload during busy hours on Black Friday — it has the potential to be an absolutely devastating attack. You’d have the opportunity to deliver the payload to everyone who visits the site!
As you can see, using web cache poisoning as a means of distribution can have some very disastrous repercussions for the web application and its users. So, from a developer’s perspective, how can you fight off web cache poisoning attacks? Well, the obvious answer would be to disable the cache altogether, but his isn’t necessarily always plausible. Instead, you could implement static responses only, ensuring the correct configurations are in place so that an attacker can’t trick the back-end into retrieving something malicious. It’s also important to be weary about third party integrations, as now you are also relying on them to be security conscious as well. And, as always: patch, patch, patch!
If you enjoyed this deep dive into the Web Cache Poisoning vulnerability, you can find more cybersecurity content at the Cybersec Cafe, or you can find me on X/Twitter!
This material is meant to be used for educational purposes and not for malicious practice.