在本文中,我向大家分享一个我在去年发现的Edge浏览器的漏洞。这个漏洞利用了浏览器XSS过滤器的缺陷,来绕过另一种XSS防御措施:CSP(Content Security Policy,内容安全策略)。注意这个漏洞并不是在绕过XSS过滤器,而是利用它让一些本来没有XSS的页面,强行制造出可利用的XSS漏洞。

MS-Edge-Thumb-1600x600.jpg

0×01 背景

浏览器XSS过滤器诞生于IE 8,它用于防范反射型XSS攻击。其基本原理是[参1,2,3]:既然是反射型,那么URL中的某个参数的值必然会在页面的某个位置出现。当然不是每个反射到HTML页面中的参数都是XSS,比如example.com/index.php?id=12345,如果页面中含有12345,这显然是无所谓的。但是如果example.com/index.php?id=<script>alert(1)</script>中的script元素被反射回来,则有可能是XSS攻击。过滤器的判断逻辑大概是这样:首先判断GET或者POST的数据中有没有参数可能含有XSS代码,这一步浏览器内置了一个比较复杂的正则来匹配。如果匹配成功,则搜索这个参数值有没有出现在服务器返回的HTML中。如果出现了,则浏览器认为这是一个XSS攻击。Edge和IE的XSS过滤器有两种模式,一种是发现攻击后屏蔽整个页面,另一种是尝试修复XSS。默认是第二种模式,服务器可以通过设置HTTP头字段X-XSS-Protection: 1; mode=block调成第一种模式[参4]。

下面说一下微软浏览器是如何“尝试修复XSS”的。举例来说,假设URL是example.com/index.php?id=<script>alert(1)</script>,并且HTML代码中含有<script>alert(1)</script>,那么浏览器就把HTML中的这个元素修改为<sc#ipt>alert(1)</script>。修改之后再交给HTML解析器。由于它把r改成了#,破坏掉了script元素,那么之后这段JavaScript代码就不会被执行。embed、iframe、object、meta等标签同理,都是用#替换一个字母来破坏这些标签。

上述的这个修复方法虽然有效地修复了一部分反射XSS,但是它也有潜在危险。自IE 8引入XSS过滤器以来就一直有研究者通过滥用这个修复逻辑,向本来没有XSS漏洞的页面注入XSS。一个简单的利用方法是:比如页面中本来就有<script src="jquery.js" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></script>,我们构造这个URL:example.com/index.php?<script src="jquery.js" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></script>。IE和Edge都会误报这是一个XSS攻击,并按上述方式修复,修复之后的代码成了<sc#ipt src="jquery.js" rel="external nofollow" rel="external nofollow" rel="external nofollow" ></script>,于是jquery.js就没法加载了。当然这个例子不是漏洞,因为这就跟jquery.js文件不存在一样,虽然功能可能受影响,但没有安全问题。

在参考资料[5]中作者提到了另一个利用方法,比如有这样一个img标签:<img alt="x onload=alert(0) x" src="x.png" rel="external nofollow" rel="external nofollow" >,其中alt的值是注入点。本来这个标签不会执行代码,但是IE弄巧成拙地修复成<img alt#"x onload=alert(0) x" src="x.png" rel="external nofollow" rel="external nofollow" >。onload就执行了。

0×02 CVE-2017-0135

上文我们说了利用过滤器的误报来干掉jquery.js。既然它能干掉script标签,那么能不能干掉其他标签呢?特别是能不能干掉和安全相关的标签。我想到了Content Security Policy有两种设置方法,既可以在HTTP头字段中设置,也能在HTML中用<meta http-equiv="Content-Security-Policy" content="...">来设置。于是我就试了下,如果网站用的是meta标签设置CSP,我能不能利用过滤器的误报来干掉这个meta标签,使得CSP失效。结论是可以。我构造了这样一个HTML页面,假设其URL是http://example.com/xss.html

<!DOCTYPE html> <html> <head> <title>CSP Test</title> <meta http-equiv="Content-Security-Policy" content="script-src 'self'"> </head> <body> <script>alert(document.domain);</script> </body> </html> 

在一个支持CSP的浏览器中访问xss.html,由于script-src ‘self’这条CSP策略,我们应该是看不见弹窗的。但是在Edge中访问http://example.com/xss.html?%3Cmeta%20http-equiv=%22Content-Security-Policy%22%20content=%22script-src%20'self'%22%3E,你会发现Edge提醒你过滤器发现了一个XSS,然后弹窗就出现了。

success screenshot.png

这里Edge把meta标签当成了反射XSS,因为它既出现在URL中,又出现在了HTML head中。然后它就把meta标签修复成了<me#a http-equiv="Content-Security-Policy" content="script-src 'self'">。于是这个标签就不再起作用。如果页面有存储型或反射型XSS,我们就可以利用这个漏洞来绕过CSP,进而执行XSS。

发现了这个漏洞之后我立即提交给了微软,2016年底提交,2017年3月在MS17-007中修复了这个漏洞,漏洞编号是CVE-2017-0135。因为这个漏洞符合“Windows预览版Edge浏览器赏金计划”要求,微软给了我1500美元奖金。

微软的修复这个漏洞之后,再打开上述PoC,Edge依然会误报它为反射XSS,但是不再尝试自动修复,而是强制屏蔽掉整个页面,不论服务器选择的是不是屏蔽模式。

0×03 宽字节编码XSS

修复之后我在想,其实上述meta标签绕过,应该还有一种利用场景,同理我们可以利用XSS过滤器干掉<meta charset>标签。这样Edge只能猜页面的编码类型。如果猜错了,比如把本来是GBK编码的网页猜成UTF-8,也许也能导致XSS。这类漏洞原理参见[6]。比如这个HTML:

<!DOCTYPE html> <html> <head> <meta charset="gb2312"> <title>CSP Test</title> </head> <body> <script> var a = "峔\";alert(1);//"; </script> </body> </html> 

其中var a的值,如果服务器按照GB2312编码过滤,浏览器也按照GB2312解析,上述代码不会弹窗。但是我们通过访问xss.html?<meta charset="gb2312">,让Edge去掉<meta charset="gb2312">之后,它就有可能认为是ASCII编码的。而“峔”的GB2312编码是8D 5C。按ASCII来说5C恰好是反斜线,与后面转义双引号的反斜线构成\\。下一个双引号则闭合了这个字符串。这样alert(1)就执行了。(在浏览器编码下,相当于var a = "?\\"; alert(1); //";

当然因为这个利用方式是漏洞修复之后好久我才想到的,所以没办法测试了。仅供大家参考思路。

0×04 总结

这个漏洞说明网站尽量不要用默认模式的Edge过滤器。建议用X-XSS-Protection: 1; mode=block模式。或者X-XSS-Protection: 0关掉XSS过滤器。即使关掉都比Edge的自动修复安全(如果自信你的网站几乎没有反射XSS漏洞的话)。另外建议尽量使用HTTP头字段的方式设置CSP策略,如果不能控制HTTP响应头再考虑用meta标签。

我估计XSS过滤器今后还能发现其他的恶意利用情景。因为它的判断规则比较复杂,而且HTML标准不仅复杂而且随时变化。难免有些之前无所谓的自动修改,之后发现问题。另外XSS过滤器既要保证尽可能全的识别XSS,又要尽量避免误报,这个平衡点把握不好就容易出bug。所以我认为XSS过滤器是一个值得继续研究的攻击面。

0×05 参考资料

  1. IE8 Security Part IV: The XSS Filter
  2. IE8 XSS Filter design philosophy in-depth
  3. IE 8 XSS Filter Architecture / Implementation
  4. Controlling the XSS Filter
  5. Abusing Internet Explorer 8′s XSS Filters
  6. 宽字节编码类型的XSS

*本文原创作者:tocttou