XSS跨站脚本攻击是指,黑客向一个页面中注入Javascript脚本,其它的用户访问该页面时会执行这个脚本。为了防止这一攻击,一些软件尝试从输入中移除Javascript代码。这很难正确实现。在这篇文章中我会展示一些试图移除输入里的Javascript脚本的代码,并演示几中绕过它的方法。
以网上商城软件 Magento 中的类 Mage_Core_Model_Input_Filter_MaliciousCode 为例。这个类的作用是过滤“恶意代码”,包括以任何形式插入的Javascript。
代码如下所示:
protected $_expressions = array( '/(///*.*/*//)/Us', '/(/t)/', '/(javascript/s*:)/Usi', '/(@import)/Usi', '/style=[^<]*((expression/s*?/([^<]*?/))|(behavior/s*:))[^<]*(?=/>)/Uis', '/(ondblclick|onclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onload|onunload|onerror)=[^<]*(?=/>)/Uis', '/<//?(script|meta|link|frame|iframe).*>/Uis', '/src=[^<]*base64[^<]*(?=/>)/Uis', ); function filter($value) { return preg_replace($this->_expressions, '', $value); }
$_expressions变量包含一个正则表达式列表,preg_replace函数会移除文本中匹配该正则表达式的内容。所以如果你输入<script>foo</script>,两个标签都会被移除,只保留foo。
让我们看一些绕过这个filter的方法。我们的目标是绕过它传递一些执行Javascript的HTML代码。这个filter有若干表达式,它们的含义是阻止下面的内容:
可以在URL中使用javascript:…让该链接执行javascript:
<a href="javascript:alert('test')">link</a>
filter移除了代码中的javascript:,所以我们不能直接用上面的代码。我们可以试着改变javascript:部分,让浏览器任然执行它并且正则表达式匹配不上。我们试试URL-encode:
<a href="javascript:alert('xss')">link</a>
正则表达式匹配不上了,但浏览器在用它之前对该链接做了URL-decode操作,所以依然可以执行它。
除了Javascript之外还有VBScript。它在IE11中被弃用并且禁用了,但是在Internet Explorer的老版本中依然可以使用,如果你把IE 11设置为IE 10模拟模式的话也是可用的。我们可以像Javascript链接那样执行相同的代码。
<a href='vbscript:MsgBox("XSS")'>link</a>
Internet Explorer在CSS中支持Javascript表达式,称为动态属性 https://msdn.microsoft.com/en-us/library/ms537634 (v=vs.85).aspx。允许攻击者加载外部的CSS样式表是非常危险的,因为攻击者可以在原始页面的上下文中执行Javascript。
在恶意的css中:
body { color: expression(alert('XSS')); }
我们可以在CSS中使用反斜杠转义字符来规避@import过滤。
Internet Explorer允许反斜杠,现在它绕过了我们的filter。
我们也可以使用Inernet Explorer支持的内嵌样式中的动态属性:
<div style="color: expression(alert('XSS'))">
filter会检查这样的字符串,style后面跟的不是<并且后面包含expression:
/style=[^<]*((expression/s*?/([^<]*?/))|(behavior/s*:))[^<]*(?=/>)/Uis
因此,我们在那里增加一个<.
<div style="color: '<'; color: expression(alert('XSS'))">
这可以绕过filter,因为[^<]匹配不上<,并且这还是一个有效的CSS,虽然“<”不是一个有效的color,但是其余部分仍然被使用了。
可以在一个元素上定义事件句柄,如下:
<div onclick="alert('xss')">
这样,这个Javascript会在有人点击它的时候执行,但是也有一些事件是在页面加载后或者用户移动鼠标的时候触发。filter移除了许多这样的事件,但是它并没有包含所有的事件句柄。比如,漏了onmouseenter:
<div onmouseenter="alert('xss')">
我们的代码会在用户把鼠标移动到div上面时执行。
另一个绕过的方法是在属性和”=”之间加上一个空格。Magento在最新版本的恶意代码filter中已经修复了它。
<div onclick ="alert('xss')">
Script标签可以用来定义行内脚本,或者从其它位置加载脚本文件:
我们的filter移除了<script>标签。然而,它只是做一次,因此我们可以让我们想要的内容在移除标签之后出现:
<scr<script>ipt>alert("XSS")</scr<script>ipt>
filter移除了两个发现的<script>,最后我们还是执行了想要的代码。实际上,这个嵌套标签的办法可以用来规避任意的filter表达式。
尽管filter试图阻止了几种脚本注入的办法,我们都找到了规避的方法。创建一个filter来规避XSS攻击是不容易的。你必须考虑各种编码类型以及不同的浏览器行为。这让开发者做起来很难,却方便了攻击者。
我展示了几种绕过filter的办法。这是一个安全威胁,除非没有对不可信输入使用这个filter。在 Magento 2.0.1 release notes 中有这样一段话:
用户在输入HTML代码的时候可以轻易绕过MaliciousCode filter函数。但是,Magento几乎不用这个filter,并且当前的用法都不允许未授权用户输入。
1月23日,我进一步联系了Magento,他们不认为这是一个安全问题,所以我发布了这篇文章。
*本文译者:felix,翻译自: http://www.sjoerdlangkemper.nl/2016/01/29/circumventing-xss-filters/ ,转载须注明来自FreeBuf黑客与极客(FreeBuf.COM)