DVWA靶场——Content Security Policy Bypass(CSP 内容安全策略)

📅 2026/7/24 22:31:08 👁️ 阅读次数 📝 编程学习
DVWA靶场——Content Security Policy Bypass(CSP 内容安全策略)

CSP(Content Security Policy)是一种浏览器安全机制,通过 HTTP 响应头告诉浏览器:

“这个页面只能加载哪些来源的资源(脚本、样式、图片等),其他的一律禁止。”主要是为了缓解潜在的跨站脚本问题(XSS),浏览器的扩展程序系统引入了内容安全策略这个概念。原来应对XSS攻时,主要采用函数过滤转义输入中的特殊字符、标签、文本来规避攻击。CSP的实质就是白名单制度,开发人员明确告诉客户端,哪些外部资源可以加载和执行。开发者只需要提供配置,实现和执行全部由浏览器完成。

这对我们来说是一个全新的内容,所以务必理解它的原理,下面给出一个具体的场景带大家理解一下

CSP 说:来自 pastebin.com 的脚本是安全的 攻击者:在 pastebin.com 上放恶意脚本 浏览器:因为是 pastebin.com,所以执行 结果:CSP 被绕过

这是浏览器配置CSP后的工作流程

用户访问页面 ↓ 浏览器发起 Request(请求页面) ↓ 服务器处理请求 ↓ 服务器返回 Response(包含 HTML + 响应头) ↓ 浏览器解析 Response Headers ↓ 发现 Content-Security-Policy ↓ 浏览器按照策略限制后续资源加载

明白了它的原理,这个漏洞最重要的点就在于找到那个它信任的来源

Low

源码分析

<?php $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); # These might work if you can't create your own for some reason # https://pastebin.com/raw/R570EE00 # https://hastebin.com/raw/ohulaquzex ?> <?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 来防御,并且开发者给了我们两个URL示例

https://pastebin.com/raw/R570EE00 https://hastebin.com/raw/ohulaquzex

回到关卡中

我们先抓个包,放行,在返回头里找找看有没有信息

直接告诉了我们CSP 策略

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

我们来解读一下

来源

是否允许

'self'(同源)

允许

https://pastebin.com

允许

hastebin.com

允许(注意:没有 https:// 前缀

example.com

允许

code.jquery.com

允许

https://ssl.google-analytics.com

允许

我们就试试https://pastebin.com这个网站,它是一个快速文本分享网站

构造弹框

将URL复制进输入框,include

我的浏览器报错了

了解了一下,原因如下

Pastebin 返回给浏览器的文件类型是"纯文本"(text/plain),但浏览器执行脚本需要"JavaScript"类型(application/javascript),类型不匹配,所以拒绝执行。

这是现代浏览器为提升安全性而采取的统一标准行为,因此我们不用纠结是否弹框,了解其原理即可。

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

移除了所有第三方域名(Pastebin、hastebin 等全部删除),并且script-src 还可以设置一些特殊值,unsafe-inline 允许执行页面内嵌的 <script> 标签和事件监听函数, 引入了 nonce(一次性随机数) 机制

CSP策略

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

指令

含义

'self'

允许同源脚本

'unsafe-inline'

允许内联脚本<script>alert(1)</script>

'nonce-...'

允许带有指定 nonce 值的脚本标签

Nonce 机制

nonce= "Number used ONCE"(一次性数字)

服务器生成一个随机值,同时:

  1. 放在 CSP 头里:nonce-abc123
  2. 放在合法的<script>标签里:<script nonce="abc123">

浏览器只会执行nonce 值匹配的内联脚本。

开发者给了我们提示,能够直接拿到nonce

通过内连注入

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

成功弹窗了

High

源码分析

vulnerabilities/csp/source/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> ';

vulnerabilities/csp/source/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(); }); }

1.在high.php中设置了csp头,规定只允许从同源的脚本加载

2.提交表单的时候,如果存在POST形式的include参数

3.游览器执行clickbutton,动态创建<script>元素,将src属性设置为‘source/jsonp.php?callback=solveSum’(json .php是一个远程脚本的url)

4.加载远程脚本,返回数据给solveSum,展示到页面

CSP策略

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

'self'

仅允许同源脚本

unsafe-inline

内联脚本被禁止

无第三方域名

外部脚本被禁止

我们打开开发者工具输入

var s = document.createElement("script"); // 创建 <script> 标签 s.src = "source/jsonp.php?callback=alert(1)//"; // 设置 src 属性 document.body.appendChild(s); // 把 <script> 添加到页面中

其效果是在页面中动态插入了一个脚本标签:

<script src="source/jsonp.php?callback=alert(1)//"></script>

浏览器工作流程

我们的 Console 代码 ↓ 创建 <script src="source/jsonp.php?callback=alert(1)//"> ↓ 浏览器请求该 URL ↓ jsonp.php 返回:alert(1)//({"answer": 15}) ↓ 浏览器执行这段 JavaScript ↓ alert(1) 被执行 → 弹窗

因为source/jsonp.php同源的(都在http://192.168.158.130:5432下),因此被成功执行

impossible

源码分析

vulnerabilities/csp/source/impossible.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>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> ';

vulnerabilities/csp/source/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 callback 注入的攻击路径,结合 CSP 的'self'限制,使得该模块在当前环境下真正无法绕过

攻击方式

能否成功

原因

输入框注入内联脚本

CSP 禁止unsafe-inline

输入框注入外部脚本

CSP 只允许'self'

输入框注入同源脚本

后端虽接收include

,但前端无输入框

修改s.src

参数

JavaScript 代码已固定,用户无法控制

修改jsonp_impossible.php

的响应

需要服务器文件写入权限

Console 执行s.src = "...?callback=..."

jsonp_impossible.php

不接受 callback 参数