Web cache poisoning
More info about web cache: Web cache
Two phases:
Find a way to trigger a response that unintentionally includes a dangerous payload.
Ensure the response is cached and served to the intended victims after success.
Exploiting cache design flaws
Web cache poisoning to deliver XSS
X-Forwarded-Host
is unkeyed. Exploit: X-Forwarded-Host: a."><script>alert(1)</script>"
If this response was cached, all users who accessed /en?region=uk
would be served this XSS payload
Note: try also multiple headers
Exploiting responses that expose too much information
Cache-control directives
A challenge in web cache poisoning is ensuring the harmful response gets cached, often requiring manual trial and error. However, sometimes responses reveal information that helps the attacker successfully poison the cache.
Vary header
The Vary
header specifies a list of additional headers that should be treated as part of the cache key even if they are normally unkeyed. For example, it is commonly used to specify that the User-Agent
header is keyed. If the mobile version of a website is cached, this won't be served to non-mobile users by mistake.
Exploiting cache implementation flaws
The methodology involves the following steps:
Identify a suitable cache oracle
A cache oracle is a cacheable page or endpoint that provides feedback on whether a response was cached or served directly from the server.
Identify and evaluate unkeyed inputs
Adding random inputs to requests and observing their effect on the response, whether it's directly reflected or triggers a different response.
Identify an exploitable gadget
These gadgets will often be classic client-side vulnerabilities, such as reflected XSS and open redirects.
Tip: use Param Miner extension to identify unkeyed inputs (Guess headers)
Unkeyed port
In this way you can:
Enable a denial-of-service attack by adding an arbitrary port, redirecting users to a non-functional port.
Enable XSS payload injection.
Unkeyed query string
Like the Host header, the request line is usually keyed, but one of the most common cache-key transformations is the exclusion of the entire query string.
Detecting an unkeyed query string
You can use alternative cache busters, like adding them to a keyed header that doesn’t affect the app’s behavior.
Unkeyed query parameters
Some websites only exclude specific query parameters that are not relevant to the back-end application, such as parameters for analytics or serving targeted advertisements. UTM parameters like utm_content
.
Detection
First time
Second time
Tip: use Param Miner extension to identify unkeyed inputs.
Exploiting parameter parsing quirks
This happen when back-end identifies distinct parameters that the cache does not. The Ruby on Rails framework, for example, interprets both ampersands (&
) and semicolons (;
) as delimiters
As the names suggest, keyed_param
is included in the cache key, but excluded_param
is not. Many caches will only interpret this as two parameters, delimited by the ampersand:
Once the parsing algorithm removes the excluded_param
, the cache key will only contain keyed_param=abc
. On the back-end, however, Ruby on Rails sees the semicolon and splits the query string into three separate parameters:
But now there is a duplicate keyed_param
. This is where the second quirk comes into play. If there are duplicate parameters, each with different values, Ruby on Rails gives precedence to the final occurrence. The end result is that the cache key contains an innocent, expected parameter value, allowing the cached response to be served as normal to other users. On the back-end, however, the same parameter has a completely different value, which is our injected payload. It is this second value that will be passed into the gadget and reflected in the poisoned response.
Exploiting fat GET support
Although this scenario is pretty rare, you can sometimes simply add a body to a GET
request to create a "fat" GET
request. In this case you can "overwrite" the param value
Normalized cache keys
Problem: when you find reflected XSS in a parameter, it is often unexploitable in practice. This is because modern browsers typically URL-encode the necessary characters when sending the request, and the server doesn't decode them.
Example:
You send the follow URL to a victim
His browser send the following request
So, normally this XSS is unexploitable.
Exploitation with normalized cache keys
Some caching implementations normalize keyed input when adding it to the cache key. In this case, both of the following requests would have the same key:
When the victim visits the malicious URL, the payload will still be URL-encoded by their browser; however, once the URL is normalized by the cache, it will have the same cache key as the response containing your unencoded payload.
Last updated