DVWA靶机学习——CSP Bypass

这个系列是学习DVWA靶机的。今天学习CSP Bypass,即绕过内容安全策略的Low、Medium、High、Impossible级别。

Posted by K4ys0n on November 23, 2020

0x00 CSP Bypass

CSP(Content-Security-Policy),即内容安全策略,通俗地说就是开发者告诉客户端,只能执行来自哪里的外部资源,主要是为了缓解XSS和数据注入攻击用的。

开发者可以配置一定的策略,限制客户端浏览器只能执行一定范围内的脚本等资源。

如果设置了CSP但有的浏览器不支持,那浏览器就会直接忽略。

DVWA中的CSP Bypass主要是针对一些开发者对CSP的配置错误,导致攻击者依旧可以绕过防御策略。

0x01 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>

浏览器看到页面是一个可以输入链接并提交包含。 可以看到源码在HTTP头中设置了CSP,如下:

"Content-Security-Policy: script-src 'self' https://pastebin.com  example.com code.jquery.com https://ssl.google-analytics.com ;"

这表示只有当前网站、pastebin.com网站、jquery和google analytics这些来源的js脚本才可以执行。

我们可以查下https://pastebin.com 网站,发现是一个快捷发步信息的网站,可以写一个alert(1),然后点击Create New Paste按钮,就会生成并跳转到一个URL,链接返回的页面是我们所填的内容alert(1)。

可以看到源码中会将这个链接用前端script标签加载外部js脚本的方式,去把链接内容包含进来,所以我们将上面在网站https://pastebin.com(https://pastebin.com) 处生成的链接填写到Low级别的CSP Bypass页面输入框里,然后点击include即可弹窗。

解题思路

可以按照上面所分析的步骤:

  • 在https://pastebin.com(https://pastebin.com) 网站上生成一个内容为alert(1)的消息。
  • 复制生成的链接。
  • 在DVWA CSP Bypass输入框中输入,点击Include按钮提交。

注:理论上如此做是可以弹窗的,但是实验的时候并没有。打开控制台提示”因为 MIME 类型(text/plain)不匹配(X-Content-Type-Options: nosniff)“,查了一下是pastebin.com那个网站返回的HTTP头中设置了X-Content-Type-Options: nosniff,直接限制客户端一定要按照设定的MIME类型(text/plain)去检查,所以现在是弹不了窗了,但原理理解了就好!

其实源码中还提供了官方做好的信息链接,但我试了也无法弹窗。

https://pastebin.com/raw/R570EE00

0x02 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");

# <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发生了变化,符合要求的js脚本来源包含:

  • unsafe-inline(允许使用内联资源)
    • 内联
    • javascript:URL
    • 内联事件处理程序(如onclick=’alert(1)’)
    • 内联
  • nonce-source(仅允许特定的内联脚本块)
    • nonce=“TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=”

解题思路

根据资源限定,我们只需要构造script标签,并且携带nonce属性即可。 构造如下代码:

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

直接输入输入框,弹窗成功!

0x03 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();
    });
}

浏览器中可以看到当前页面已经没有输入框了,只有一个按钮。源码审计可以看到CSP策略只允许“self”,也就是只允许当前页面的 js 脚本。

源码中js脚本文件中,按照流程是

  • 监听click事件,当确认点击时,调用执行clickButton()函数
  • clickButton()函数会写入script标签去导入jsonp.php,并且链接带有变量callback=”solveSum”
# 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).")";
?>
  • 调用solveSum(obj)函数将obj[‘answer’]=15插入到当前的HTML文件中。

从流程可以看出callback实际上是调用了一个js函数并执行。所以可以直接抓包改callback的值,改成自己想要的js函数即可。

解题思路

用Burpsuite拦截,将get请求中的变量callback的值修改为alert(1)即可,修改后如下:

GET /dvwa/vulnerabilities/csp/source/jsonp.php?callback=alert(1) HTTP/1.1
Host: 127.0.0.1:9000
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://127.0.0.1:9000/dvwa/vulnerabilities/csp/
Cookie: security=high; PHPSESSID=fbag9cl6ld7igjdjkdetgprhlv

修改后点击发送,弹窗成功!

0x04 Impossible

源码分析

impossible.js

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();
    });
}

jsonp.php

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

$outp = array ("answer" => "15");

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

简单对比一下High和Impossible级别,就只是去掉了容易被利用的callback变量,直接将answer=15写死在代码里了。

解题思路

无。

0x05 小结

防御方法:

  • 能不给用户输入的就尽量不要给,特别是一些敏感数据操作时,尽量不要留有用户执行函数的机会。
  • 实在需要用户输入的地方需要严格控制函数关键词,过滤危险函数。

0x06 参考

https://www.cnblogs.com/-zhong/p/10906270.html