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-store

  • it 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 (GET and HEAD by default - POST only if explicitly allowed via headers like Cache-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

  1. Identify unkeyed input

  2. 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

    1. 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

    1. Probe key handling

    2. 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

  3. 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/DELETE

  • Forwarded-Server: your-site.com

  • X-Forwarded-Scheme: https/https/nothttps

  • X-Forwarded-Port: 1337/dudPort

  • X-Host: example.com

  • X-Forwarded-Host: evil.com/hacker.hr

  • X-Http-Host-Override: yourownhost.com

  • Origin: https://evil.com

  • X-Original-Url: /path

    can 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 /%2F

  • PHP: 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

Pragma directive
Purpose

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