In this modern digital world, especially in the era of Work From Home (WFH), it is essential that the web applications we are being used on daily basis must be available to their users with quick request-response time. There are many ways to achieve this, but we are going to focus on Caching and Web Cache Poisoning. Many web applications make use of Web Server Caches to reduce server load and increase performance.
Now, you might be wondering that How does a Web Server Cache works?
Let’s understand this with an example: Suppose there is a website with the domain www.site.com hosted on web server WS1. When a user sends a request to www.site.com, WS1 will process the user request and send a response back to the user. It’s just a scenario of one normal user’s request-response, think of some giant web applications such as Facebook, YouTube, and LinkedIn with a huge user base. So many users are requesting these sites at the same time (especially during peak time), and many might be sending the same requests. If a server had to process requests and send responses to individual requests, it would be overloaded and users might face delays in getting responses from the server.
Caching reduces such issues by sitting in between the user and Web server. A Caching proxy caches (stores) the response of individual requests for a fixed amount of time, and if any similar type request is received from the user then without having any interaction with the webserver, the cached response is directly served to the user. This mechanism effectively reduces the load on the web server for processing the same requests.
But how would a Cache Proxy know whether it has a cached response for the request or not?
The answer is: Cache Keys
Cache Keys are predefined subsets of components of a request. These subsets could be anything like Headers, Parameter values, etc. The components which are not cache keys are known as “Unkeyed”. If cache proxy identifies cache keys of a request matching the keys of any cached (stored) requests, it will serve a copy of the cached response back to the user if it is not expired. And if the cache keys of a request are not matching with any cached response, the request is forwarded to the webserver for processing it.
Now that we have understood all basic fundamentals, let begin the actual discussion:
What is Web Cache Poisoning?
Web Cache Poisoning is not an attack but is a modern exploit methodology used by attackers to reach more user-base of a vulnerable web application to deliver the attack. In order to make this web-cache attack successful, hackers combine both: Vulnerable website behavior + Website Cache Proxy mechanism.
Below are the stages of Web Cache Poisoning attack:
- The attacker finds a vulnerability on a web application
- The attacker identifies the cache keys of vulnerable requests, checks if the vulnerability can be exploited using unkeyed attacker controllable fields, and tries to get a response from the server which contains malicious content (a.k.a. attack payload).
- The attacker keeps trying systematically to get the vulnerable response cached on the Cache Proxy Server
- The attacker waits for Cache Proxy to serve the cached malicious response to other users of the web application
This cache attack methodology can be used to deliver many web attacks such as XSS, Open Redirects, and so on.
Further, let us understand these stages in depth along with an example:
Stage-1:
Suppose an attacker finds out that a web application www.site.com is vulnerable to XSS. In the search request, the attacker finds the value of the Referer header as a meta tag in the response. Also from the response header: Cache-control: max-age=3600, public,
it was observed that it is using a caching proxy. The most common header to determine the cache is X-cache
from which a user can observe where the response is served. X-cache: HIT
means web cache proxy served the request, and X-cache: MISS
means web server served the request.
GET /?search=hello HTTP/1.1
Host: www.site.com
Referer: https://www.site.com/blogs/1/
HTTP/1.1 200 OK
Cache-Control: max-age=600
<meta property="og:url" content=" https://www.site.com/blogs/1/" />
Stage-2:
Once the attacker identifies the vulnerability, the next task is to determine the cache keys and check if the attack can be performed from unkeyed fields or not. A cache key can be identified manually by adding different values to each user-controllable field and observing if there is any effect in the response or not. The most common scenario would be that unkeyed input will be reflected back in the response but changing its value won’t affect the caching proxy and the cached response will be served to the user.
The agenda over here is to manipulate the unkeyed input to generate an attack request and get the response from the server with malicious attack content. So the same response can be served to other users using a caching proxy when their request’s cache keys are matching.
Note: You can also automate this process if you’re using Burp Professional Edition with the help of Param Miner Extension.
Continuing to our example, the attacker identified that the value passed to the search parameter is used as a cache key. And the Referer header value is unkeyed but still, it is user-controllable. So, the attacker can leverage this to deliver an XSS attack payload.
GET /?search=hello HTTP/1.1
Host: www.site.com
Referer: https://www.site.com/blogs/1/ ”><script>alert(1)</script><a href="
HTTP/1.1 200 OK
Cache-Control: max-age=600
<meta property="og:url" content=" https://www.site.com/blogs/1/" />
<script>alert(1)</script> <a href="" />
In the above example, first, the attacker closed the existing meta tag with ‘ ”>’,
then injected code for XSS to prompt an alert box with value 1: ‘<script>alert(1)</script>’,
and anchor tag ‘<a href="’
is used just to avoid any syntax error.
Stage-3:
In this stage, the attacker will keep trying to cache the response with malicious payload to serve to other users when they search with the same keyword. The attacker has to keep sending malicious requests to the webserver until the response to the request contains X-Cache: hit in the headers.
You might be thinking that with a single search keyword value, the success of the attack is very limited. But an experienced attacker can automate this process to target a huge amount of common search keywords at a time in order to deliver the attack to a huge user mass.
GET /?search=hello HTTP/1.1
Host: www.site.com
Referer: https://www.site.com/blogs/1/ ”><script>alert(1)</script><a href="HTTP/1.1 200 OK
Cache-Control: max-age=600
X-Cache: miss
<meta property="og:url" content=" https://www.site.com/blogs/1/" />
<script>alert(1)</script> <a href="" />
GET /?search=hello HTTP/1.1
Host: www.site.com
Referer: https://www.site.com/blogs/1/ ”><script>alert(1)</script><a href="HTTP/1.1 200 OK
Cache-Control: max-age=600
X-Cache: hit
<meta property="og:url" content=" https://www.site.com/blogs/1/" />
<script>alert(1)</script> <a href="" />
Stage-4:
As the attacker’s work is done, it is then time to wait for some legitimate user to search for the same keyword, and then the malicious cached response will be served to that request. And the user will see an alert box with the value 1.
Just for simplicity, in our example, we used an alert box, but an attacker can also fetch the session token, cookie value, redirect the user to the attacker’s controllable website, download malicious files or software, and many more things.
In real-world scenario the impact of this methodology is highly dependent on several factors:
- Which client-side attack an attacker can perform on the website
- Which attack can be cached by manipulating caching mechanism
- The number of requests from users for the cached response
Are you on the development side?
Here are some ways to prevent Web Cache Poisoning Vulnerabilities
- Disable Web Caching mechanism, according to your business it might be feasible, or it might be not
- In case if it’s not feasible, keeping caching strictly to static resources is the most effective prevention
- Mitigate or Patch all client-side vulnerabilities
- List all the user-controllable inputs which are unkeyed and are reflected back in the response. For Ex: headers, cookies, query strings, etc. Those can either be removed, disabled, or added to the cache key to reduce the attack impact.