Web Cache Poisoning
Web Cache Poisoning
Resources:
Practical Web Cache Poisoning: Redefining 'Unexploitable'
Cache:
Caching is intended to speed up page loads by reducing latency, and also reduce load on the application server.
Some companies host their own cache using software like:
Varnish
Cloudflare(caches scattered across geographical locations.)
Drupal have a built-in cache.
How Cache Works:
Request is being received by Cache
Cache Checks whether it has a copy of this exact resource being requested then it response with it
If not ,Cache forwards the request to the application server.
Methods used by cache to Identify whether two requests are trying to load the same resource:
byte-for-byte compare.(ineffective)
📌 Caches tackle this problem using the concept of cache keys
Typical cache keys:
URL and it’s Get parameters
Host header
Based on those 2 Keys Cache Will handle The next 2 Requests in the same way
📌 As a result, the page will be served in the wrong language to the second visitor.
Solution:
Vary response header is Used to specify additional request headers that should be keyed.But it’s used in a rudimentary way.
Cache Poisoning:
📌 is to send a request that causes a harmful response that gets saved in the cache and served to other users.
Methodology :
Identify unkeyed inputs(Manually Or Using Param miner or any other fuzzing tool)
📌 Cached responses can mask unkeyed inputs - The response is the same thus,you can’t notice the unkeyed inputs.Changing it while testing won’t be effective(it’s already cached) Therefore,using a cache-buster would help If you test manually. or you can ensure every request has a unique cache key by adding a parameter with a value of $randomplz to the query string or in your fuzzing tool.
Identify how much damage you can do with it.
Try and get it stored in the cache
📌 To keep it legal,Cache The request using a get parameter with a fixed value just to affect your requests while testing.
Basic Poisoning:
In most cases this is just a self-xss
dontpoisoneveryone=1 is our cache-buster in this case.
Performing the request to the same endpoint Would case an alert
Discreet poisoning:
After Performing the Basic Poisoning on /en?dontpoisoneveryone=1,We would like to perform it on the homepage now.Which would be normally cached already,Therefore you need to wait to cache Your malicious request when the last cached request is over.
The response headers 'Age' and 'max-age' respectively specify the age of the current response, and the age at which it will expire.the response expires when the age == max-age.
Sometimes you notice the max-age is missing but at some point the server will reset the age.
Find when by automating requests in different times and observing the response.
Or you can just spam your malicious request using burp intruder.
Selective Poisoning:
As seen in the response,The User-Agent is being used as a key for cache.
So you might poison it with the most used user agents.
If you are targeting someone(selective) you can just poison the cache with his user agent
anyone with that user agent will be affected.
DOM Poisoning:
Sometimes it’s not about just an XSS payload,it’s complicated somehow,
an example :
Route Poisoning:
Some applications use headers for internal request routing
📌 HubSpot is giving the X-Forwarded-Server header priority over the Host header
Go to HubSpot,Create a web page, paste its URL into the X-Forwarded-Server,
Your response would be your web page and should be cached and served to all users,it’s sound like an subdomain take over.
Hidden Route Poisoning:
📌 Cloudflare's blog is hosted by Ghost, who are clearly doing something with the X-Forwarded-Host header.
Example Found on [1][”Hidden Route Poisoning:”]
Chaining Unkeyed Inputs:
We might chain 2 headers
A good combination of
Open Graph Hijacking:
Open Graph is a protocol created by Facebook to let website owners dictate what happens when their content is shared on social media. The og:url parameter we've hijacked here effectively overrides the URL that gets shared, so anyone who shares the poisoned page actually ends up sharing content of our choice.
📌 the application sets 'Cache-Control: private', and Cloudflare refuse to cache such responses. Fortunately, other pages on the site explicitly enable caching.📌 The 'CF-Cache-Status' header is an indicator that Cloudflare is considering caching this response.📌 Cloudflare have even more regional caches📌 I suspect that quite a few websites start using a service like Cloudflare for DDoS protection or easy SSL, and end up vulnerable to cache poisoning simply because caching is enabled by default.
Local Route Poisoning:
📌 X-Original-URL and X-Rewrite-URL which override the request's path
📌 Which might be used to convert reflected XSS into stored XSS some times.Something Like that:(**Internal Cache Poisoning)**
Open Redirect + Cache Poisoning:
Leads to :
Nested cache poisoning:
📌 If the site uses an external cache.we can use the internal cache to poison the external cache
Web Cache Poisoning via Fat GET Request:
This web application is using a caching system. By sending a GET request with a request body (a "fat" GET request) it was possible to force the caching system to cache a response that contains user-controlled input.
Notes:
You can identify the cache keys using :
Always poison the javascript entries trying to get a redirect to your exploit.
Some UTM analytics parameters are unkeyed like :
Example OF The Last 2 Notes:
XSS + Cache Poisoning:
Some Useful headers: or Just Use Param Miner.
Activate “Add fcbz cachebuster”.while testing your payloads that you want it to be cached
Activate “Add Dynamic cachebuster” while guessing the used inputs.
Ibrahim Radi
Last updated