CSP Bypass

CSP 绕过

内容安全策略(Content Security Policy, CSP)

Low

源码解析

self 信任来自站点的同一个源 (不包括其子域名),还信任其他几个站点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, jquery and google analytics.

header($headerCSP);

# https://pastebin.com/raw/R570EE00

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
<script src='" . $_POST['include'] . "'></script>
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:</p>
<input size="50" type="text" name="include" value="" id="include" />
<input type="submit" value="Include" />
</form>
';

漏洞利用

打开浏览器的开发者工具可以看到响应返回的 Content-Security-Policy ;

PASTEBIN 上生成 代码链接 ,内容如下:

1
alert("Bypass CSP!")

然后将链接输入并提交,成功加载并执行。

参阅

Medium

源码解析

unsafe-inline 允许使用内联 JavaScript 和 CSS;nonce-source 使用随机数源仅允许特定的内联脚本块。

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
 <?php

$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";

header($headerCSP);

// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");

# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.</p>
<input size="50" type="text" name="include" value="" id="include" />
<input type="submit" value="Include" />
</form>
';

漏洞利用

同样使用开发者工具查看,unsafe-inline 表示允许使用内联资源nonce-source 表示仅允许特定内联;

直接注入 JavaScript 代码,成功执行。

1
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert("Bypass CSP!")</script>

参阅

High

源码解析

只允许本页面加载的 JavaScript 执行,不过这里还接收 include 参数;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>
<p>1+2+3+4+5=<span id="answer"></span></p>
<input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/high.js"></script>
';

点击按钮后生成 script 标签指向 source/jsonp.php?callback=solveSum,并加入 DOM 中;页面执行 JavaScript 代码中的 solveSum 函数,在页面中写入答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp.php?callback=solveSum";
document.body.appendChild(s);
}

function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}

漏洞利用

点击 Solve the sum 后发现利用了一个参数为 callback 的 .php 脚本执行函数 solveSum ,这个函数刚好在 high.js 中;

这里使用 Burp Suite 抓包修改参数 callback ,绕过成功。

Impossioble

源码解析

self 信任来自站点的同一个源 (不包括其子域名),并且未设置信任内联元素;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>Unlike the high level, this does a JSONP call but does not use a callback, instead it hardcodes the function to call.</p><p>The CSP settings only allow external JavaScript on the local server and no inline code.</p>
<p>1+2+3+4+5=<span id="answer"></span></p>
<input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/impossible.js"></script>
';

不使用 callback 函数,直接硬编码,避免了注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp_impossible.php";
document.body.appendChild(s);
}

function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
}

var solve_button = document.getElementById ("solve");

if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}