Web Cache Poisoning

Web cache poisoning is an advanced technique whereby an attacker exploits the behavior of a web server and cache so that a harmful HTTP response is served to other users.
Types of web cache
private storing responses for a single user
shared storing responses that serve multiple users.
Private cache control
When the Cache-Control header is set to private, this directive ensures the response stays in a private cache (like the browser) and doesn’t get stored in shared caches.
For extra safety, you can also instruct proxies not to cache certain responses (especially for sensitive URIs) by using headers like Cache-Control: no-store or Cache-Control: no-cache.
Managing shared cache behavior
Shared caches fall into two categories:
Public, shared proxy caches serve multiple users and aim to improve performance by delivering cached content from the edge (e.g. Cloudflare)
Managed, shared proxy caches within corporate networks. Admins can fine-tune caching behavior and even override standard HTTP caching rules. (e.g. a company proxy server)

Core HTTP Proxy jobs
The proxy:
decides whether to cache a response or not.
checks if it already stored a response for a specific request.
selects the right version of a stored response to serve.
evaluates the age of a stored response and either reuse it or request a fresh one.
Cached response
it includes any explicit cache-related response header (e.g.
Cache-Control,Expires,ETag,Vary, etc.)it does not include
Cache-Control: no-storeit has a final or heuristically cacheable status codes (e.g.
200,203,204,206,300,301,404,410,414,501)it uses a cacheable HTTP method (
GETandHEADby default -POSTonly if explicitly allowed via headers likeCache-Control).
Cache key & Unkeyed components
cache keys request's components used by caches to identify requests and their equivalent in the cache. They are typically based on the path of the requested resources (request line) and the HOST header
unkeyed components components of the request that are not included in the cache key
Web cache poisoning - Attack phases
Identify unkeyed input
Identify how the server process the unkeyed input check if an input is reflected in the response from the server without being properly sanitized, or is used to dynamically generate other data
Identify a suitable cache oracle a cache oracle is simply a page or endpoint that provides feedback about the cache’s behavior:
An HTTP header that explicitly tells you whether you got a cache hit
Observable changes to dynamic content
Distinct response times
Probe key handling
Identify an exploitable gadget Instead of having to induce a victim to visit a specially crafted URL, your payload will automatically be served to anybody who visits the ordinary, perfectly legitimate URL
Get the response cached
Cache buster
Randomic element for each request that triggers the injection of the request in the remote cache
URL string
User-Agent
Accept
Accept-Encoding
Cookie
Origin
Headers to test
X-Http-Method-Override: GET/POST/DELETEForwarded-Server: your-site.comX-Forwarded-Scheme: https/https/nothttpsX-Forwarded-Port: 1337/dudPortX-Host: example.comX-Forwarded-Host: evil.com/hacker.hrX-Http-Host-Override: yourownhost.comOrigin: https://evil.comX-Original-Url: /pathcan be converted to:
Main attack vectors
Unkeyed port
Some caching systems will parse the Host header and exclude the port from the cache key.
In the case where a redirect URL was dynamically generated based on the Host header, by using a fake port to Host header value, the user will be redirected to a not existing page, causing a Denial of Service.
Other way of adding the PORT is by using X-Forwarded-Port: 1337 and also you can add Origin: randomValue
Unkeyed query string
The request line is typically keyed like the Host header.
If the query string in unkeyed, use param miner to detect other keyed Headers and add cache buster in the identified header.
Another approach is to operate on the request line, identifing any discrepancies between how the cache and the back-end normalize the path of the request. For example, the following paths are considered different entries by the web cache but the backend always sees the requests as GET /:
Apache:
GET //Nginx:
GET /%2FPHP:
GET /index.php/xyz.NET
GET /(A(xyz)/
Unkeyed query parameters
Some websites only exclude specific query parameters that are not relevant to the back-end application, like UTM parameter utm_content.
Some pages handle the entire URL in a vulnerable manner, making it possible to exploit arbitrary parameters.
Unkeyed cookies
Check if any cookie parameter is reflecting in a response, enabling JavaScript execution that leads to stored XSS.
Cache parameter cloaking
Fat GET Request
Unkeyed component: HTTP method.
Poison the cache with a POST request containing a malicious payload in the body. The payload would then even be served in response to users' GET requests.
Sometimes you need to override the HTTP method:
Normalized (url decoded) cache keys
Modern browsers typically URL-encode the necessary characters when sending the request, and the server doesn’t decode them, generating a harmless URL-encoded string.
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:
URI normalization
X-Forwarded-Scheme and X-Forwarded-Host
Illegal Header Fields
Drupal Open Redirect
Persistent redirect hijacking
Akamai-based request headers
akamai-x-get-cache-key
Returns the exact cache key (X-Cache-Key, X-True-Cache-Key) used for the request
akamai-x-cache-on
Forces Akamai to attempt to cache the response at the edge
akamai-x-cache-remote-on
Forces caching on remote / parent (mid-tier) caches
akamai-x-check-cacheable
Explains whether and why a response is cacheable or not
akamai-x-get-extracted-values
Returns values Akamai extracted and normalized (headers, params, geo, device)
Last updated