关于那些web漏洞--csp_残月01的博客-爱代码爱编程
CSP
CSP即content security policy(内容安全策略),通过设置策略指令来规定资源的加载来源,简单地说就是CSP可以规定页面能从哪些域(站点)加载资源,这里的资源包括js脚本、图片和音频等等。如图,可以在响应头中看到它的存在:
MDN文档对CSP解释的非常清楚。链接:MDN_CSP
Low
<?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>
';
CSP设置了脚本的来源(script-src)只能是self(此页面所在的站点)、https://pastebin.com、example.com、code.jquery.com和https://ssl.google-analytics.com。那么,要绕过CSP,只能从这些站点入手,如果这些站点上的某一个页面能插入脚本代码,就把它加载进来。逐个检查站点,可以发现在https://pastebin.com这个站点可以自主编辑页面。
点击raw后,就可以生成一个包含脚本代码的页面。然后再引入到页面中,点击include成功弹框。
那为什么不直接闭合标签注入脚本代码?因为默认情况下,CSP禁止内联脚本(形如就是内联脚本)和事件处理属性中的代码(如οnerrοr=“code)”)。
如果要取消禁止,就设置指令为:script-src unsafe-inline。
Medium
<?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");
// 设置了这个头部,浏览器会检测到XSS,从而采取相应防御措施,但并非所有
// 浏览器都支持,例如Firefox就不支持。设置为 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>
';
仔细分析CSP,设置了"unsafe-inline"说明可以执行内联脚本,而"nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA="说明只有设置了nonce属性,并且属性值为"TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA="的script标签才能执行内联脚本。思路很清晰了,输入:,成功弹框。
这个nonce值可以在响应头中得到,它的作用跟token值有点类似,含有nonce值的script标签才能执行脚本,实际上这个nonce值应该要随机生成的,而不是固定的。
High
// high.php
<?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>
';
// high.js
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();
});
}
// jsonp.php
<?php
header("Content-Type: application/json; charset=UTF-8");
if (array_key_exists ("callback", $_GET)) {
$callback = $_GET['callback'];
} else {
return "";
}
$outp = array ("answer" => "15");
echo $callback . "(".json_encode($outp).")";
?>
首先给"solve the sum"按钮注册一个"click"的监听事件,当点击这个按钮后,就会执行solveSum函数,这个函数的作用是创建一个script元素,其src属性为"…jsonp.php…",说明script元素的内部脚本是从jsonp.php加载进来的,然后在body元素中添加这个script元素,里面的脚本代码就会被执行。接下来看看jsonp.php代码,$callback值是GET方式传递的,它可以被控制,并且还原样返回。测试一下:
跟Low级别一样,这些数据加载进来后,就是script元素内部的脚本,可以被执行。
Impossible
// jsonp_impossible.php
<?php
header("Content-Type: application/json; charset=UTF-8");
$outp = array ("answer" => "15");
echo "solveSum (".json_encode($outp).")";
?>
这级别修复了High级别中jsonp.php的小漏洞,不再用$callback接收参数值了,而是把"solveSum"写死在代码中,因此也就没有可利用的地方了。
总结
这几道题给出了一些绕过CSP的方法:
(1)仔细分析站点的CSP策略,检查它设置的来源是否可以修改其中的页面数据,嵌入脚本后把它加载进来即可;
(2)是否允许内联脚本,是否设置对应的nonce值,nonce值是否固定的。