通過逆向分析挖掘Facebook Gameroom中的安全漏洞
去年年底,我受邀參加了Facebook的Bountycon活動(dòng),這是一個(gè)受邀者才能參加的應(yīng)用安全會(huì)議,其中有一個(gè)現(xiàn)場黑客環(huán)節(jié)。雖然參與者可以提交任何Facebook產(chǎn)品內(nèi)的漏洞,但Facebook邀請(qǐng)我們關(guān)注Facebook游戲。由于之前測(cè)試過Facebook的產(chǎn)品,所以我知道這將是一個(gè)高難度的挑戰(zhàn)。這些年來,他們的安全控制已經(jīng)越來越嚴(yán)格——即使像跨站腳本攻擊這樣的簡單漏洞也很難找到,這也是為什么他們肯為這些漏洞付出非常高的賞金的原因。因此,頂級(jí)白帽黑客往往會(huì)從第三方軟件的角度來挖掘Facebook的安全漏洞,比如Orange Tsai著名的MobileIron MDM漏洞。
鑒于我的時(shí)間有限(由于某些原因,我遲到了),所以,我決定不進(jìn)行全面的漏洞研究,而專注于對(duì)Facebook Gaming的訪問控制進(jìn)行簡單的安全審計(jì)。然而,正如人們所期望的那樣,移動(dòng)和網(wǎng)頁應(yīng)用的安全性都很好。經(jīng)過一番折騰,我發(fā)現(xiàn)了Facebook Gameroom程序,它是一個(gè)用于玩Facebook游戲的Windows本地客戶端。于是,我就踏上了將攻勢(shì)攻擊性逆向分析應(yīng)用于本地桌面應(yīng)用程序的啟蒙之旅。
關(guān)于Facebook Gameroom
您是不是還沒聽說過Facebook Gameroom——這很正常,我之前也沒聽說過。實(shí)際上,Gameroom發(fā)布于2016年11月,被譽(yù)為Steam的強(qiáng)勁對(duì)手,支持Unity、Flash和最近的HTML5游戲。然而,近年來,隨著流媒體的崛起,F(xiàn)acebook已經(jīng)將注意力轉(zhuǎn)向其移動(dòng)和網(wǎng)絡(luò)平臺(tái)。事實(shí)上,Gameroom計(jì)劃在今年6月退役。幸運(yùn)的是,在我挖到其中的安全漏洞時(shí),它還沒有退出市場。
我注意到的第一件事是,安裝Gameroom時(shí),無需提升任何的權(quán)限。它似乎是一個(gè)分步安裝的程序:首先,通過一個(gè)小型安裝程序從網(wǎng)絡(luò)上下載額外的文件,也就是說,它的安裝程序不是一體式的。事實(shí)上,我很快就找到了安裝目錄,即C:\Users\< USERNAME >\AppData\Local\Facebook\Games由于大多數(shù)用戶級(jí)應(yīng)用程序都位于該C:\Users\< USERNAME >\AppData文件夾中,因此我很快在處找到了安裝目錄。該文件夾包含許多.dll文件以及幾個(gè)可執(zhí)行文件。一些事情對(duì)我很重要:
1. Gameroom自帶的7zip可執(zhí)行文件(7z.exe和7z.dll),可能已經(jīng)過時(shí),而且很容易受到攻擊。
2. Gameroom將用戶會(huì)話數(shù)據(jù)存儲(chǔ)在Cookies SQLite數(shù)據(jù)庫中,這對(duì)攻擊者來說是一個(gè)非常有吸引力的目標(biāo)。
3. Gameroom包含了CefSharp庫(CefSharp.dll),經(jīng)過進(jìn)一步研究,發(fā)現(xiàn)這是一個(gè)用于C#的嵌入式Chromium瀏覽器。
第三點(diǎn)表明,Gameroom是用.NET框架編寫的。我們知道,.NET框架可以將程序被編譯成通用中間語言(CIL)代碼,而不是機(jī)器代碼,也就是說,這些代碼可以在通用語言運(yùn)行時(shí)應(yīng)用程序虛擬機(jī)中運(yùn)行。這樣做有許多好處,比如可以提高.NET應(yīng)用程序的互操作性和可移植性。然而,由于這些應(yīng)用程序被編譯為CIL而不是純機(jī)器代碼,因此,將這些應(yīng)用程序反編譯回接近源碼的過程也要容易得多。
對(duì)于.NET程序集,DNSpy是事實(shí)上的標(biāo)準(zhǔn)。逆向工程師可以用DNSpy輕松地調(diào)試和分析.NET應(yīng)用程序,包括對(duì)它們進(jìn)行實(shí)時(shí)修改。于是,我把FacebookGameroom.exe拖拽到DNSpy中,開始進(jìn)行分析。
挖掘含有漏洞的函數(shù)
首先,我們開始查找易受攻擊的函數(shù),如不安全的反序列化函數(shù)等。限于篇幅,這里就不對(duì)反序列化攻擊進(jìn)行詳細(xì)介紹了,簡單來說,這涉及到將數(shù)據(jù)類型轉(zhuǎn)換為易于傳輸?shù)母袷?,然后再轉(zhuǎn)換回來,如果處理不當(dāng),這可能會(huì)導(dǎo)致嚴(yán)重的漏洞。例如,微軟就曾警告不要使用BinaryFormatter,因?yàn)锽inaryFormatter是不安全的,也不能使其安全。
不幸的是,在我搜索“Deserialize”字符串時(shí),BinaryFormatter竟然冒了出來。

然而,我還需要找到易受攻擊的代碼路徑。為此,我右擊搜索結(jié)果,選擇“Analyze”,然后沿著“Used By”鏈找到了Gameroom使用BinaryFormatter.Deserialize的地方。

最終,這讓我找到了System.Configuration.ApplicationSettingsBase.GetPreviousVersion(string)和System.Configuration.ApplicationSettingsBase.GetPropertyValue(string)函數(shù)。Gameroom在啟動(dòng)時(shí)使用了反序列化函數(shù)來檢索其應(yīng)用程序的相關(guān)設(shè)置——但這些設(shè)置是從哪里來的呢?通過考察安裝文件夾,我發(fā)現(xiàn)了fbgames.settings,這是一個(gè)序列化的blob。因此,如果我在這個(gè)文件中注入一個(gè)惡意的反序列化payload,就可以獲得代碼執(zhí)行權(quán)限。不過,在此之前,我還需要找到一個(gè)反序列化的gadget。根據(jù)已知的反序列化gadget列表,我展開了進(jìn)一步的搜索,發(fā)現(xiàn)Gameroom使用的是WindowsIdentity類。
在此基礎(chǔ)上,我研究出了一個(gè)代碼執(zhí)行漏洞的概念驗(yàn)證過程:
1. 使用ysoserial反序列化攻擊工具,通過命令ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o raw -c "calc" -t > fbgames.settings生成了相應(yīng)的代碼執(zhí)行payload。
2. 接下來,我將fbgames.settings復(fù)制到C:\Users\
3. 最后,打開Facebook Gameroom,計(jì)算器就彈了出來!
雖然獲得代碼執(zhí)行權(quán)限是件非常令人興奮的事情,但在與Facebook團(tuán)隊(duì)進(jìn)一步討論后,我們一致認(rèn)為這不符合他們的威脅模型。因?yàn)镚ameroom是作為用戶級(jí)應(yīng)用執(zhí)行的,所以沒有機(jī)會(huì)升級(jí)權(quán)限。此外,由于覆蓋文件需要某種程度的訪問權(quán)限(例如,需要通過審批才能被公開列出的惡意Facebook游戲),所以沒有可行的遠(yuǎn)程攻擊載體。
在本地應(yīng)用程序構(gòu)成的不同威脅環(huán)境中,我學(xué)到了一個(gè)重要的教訓(xùn)——在深入研究代碼級(jí)漏洞之前,最好先尋找可行的遠(yuǎn)程攻擊載體。
規(guī)劃探索路徑
您是否有點(diǎn)擊郵件中的鏈接就神奇地啟動(dòng)了Zoom的經(jīng)驗(yàn)?這背后到底發(fā)生了什么?其實(shí)很簡單,這只是使用了一個(gè)自定義的URI方案,它允許你像網(wǎng)絡(luò)上的其他鏈接一樣打開應(yīng)用程序。例如,Zoom注冊(cè)了zoommtg: URI方案,來解析像下面這樣的鏈接: zoommtg:zoom.us/join?confno=123456789&pwd=xxxx&zc=0&browser=chrome&uname=Betty。
同樣,我注意到Gameroom使用了自定義URI方案,以便可以在單擊Web瀏覽器中的鏈接后自動(dòng)打開Gameroom。搜索完代碼后,我發(fā)現(xiàn)Gameroom會(huì)通過FacebookGames\Program.cs來檢查fbgames:URI方案:
- private static void OnInstanceAlreadyRunning()
- {
- Uri uri = ArgumentHelper.GetLaunchScheme() ?? new Uri("fbgames://");
- if (SchemeHelper.GetSchemeType(uri) == SchemeHelper.SchemeType.WindowsStartup)
- {
- return;
- }
- NativeHelpers.BroadcastArcadeScheme(uri);
- }
即使Gameroom已經(jīng)用fbgames://URI打開,它還是會(huì)繼續(xù)通過SchemeHelper類對(duì)其進(jìn)行解析:
- public static SchemeHelper.SchemeType GetSchemeType(Uri uri)
- {
- if (uri == (Uri) null)
- return SchemeHelper.SchemeType.None;
- string host = uri.Host;
- if (host == "gameid")
- return SchemeHelper.SchemeType.Game;
- if (host == "launch_local")
- return SchemeHelper.SchemeType.LaunchLocal;
- return host == "windows_startup" ? SchemeHelper.SchemeType.WindowsStartup : SchemeHelper.SchemeType.None;
- }
- public static string GetGameSchemeId(Uri uri)
- {
- if (SchemeHelper.GetSchemeType(uri) != SchemeHelper.SchemeType.Game)
- return (string) null;
- string str = uri.AbsolutePath.Substring(1);
- int num = str.IndexOf('/');
- int length = num == -1 ? str.Length : num;
- return str.Substring(0, length);
- }
如果URI含有g(shù)ameid主機(jī),它將用SchemeHelper.SchemeType.Game.Game來進(jìn)行解析。如果它使用的是launch_local主機(jī),則會(huì)用 SchemeHelper.SchemeType.LaunchLocal來進(jìn)行解析。于是,我從最有希望的launch_local路徑開始,追蹤到了FacebookGames.SchemeHelper.GenLocalLaunchFile(Uri):
- public static async Task
- {
- string result;
- if (SchemeHelper.GetSchemeType(uri) != SchemeHelper.SchemeType.LaunchLocal || uri.LocalPath.Length <= 1)
- {
- result = null;
- }
- else if (!(await new XGameroomCanUserUseLocalLaunchController().GenResponse()).CanUse)
- {
- result = null;
- }
- else
- {
- string text = uri.LocalPath.Substring(1);
- result = ((MessageBox.Show(string.Format("Are you sure you want to run file\n\"{0}\"?", text), "Confirm File Launch", MessageBoxButtons.YesNo) == DialogResult.Yes) ? text : null);
- }
- return result;
- }
遺憾的是,即使我可以通過像fbgames://launch_local/C:/evilapp.exe這樣的URI來啟動(dòng)系統(tǒng)中的任何任意文件(正如Facebook所記錄的那樣),但仍然會(huì)被確認(rèn)對(duì)話框所阻止。我試圖用格式字符串和非標(biāo)準(zhǔn)輸入繞過這個(gè)對(duì)話框,但并沒有得手。
于是,我將目光轉(zhuǎn)向gameid路徑,該路徑會(huì)根據(jù)URI中的游戲ID打開Facebook URL。例如,如果您想在Gameroom中啟動(dòng)Words With Friends,可以在瀏覽器中訪問fbgame://gameid/168378113211268,這時(shí)Gameroom就會(huì)在本地應(yīng)用窗口中打開https://apps.facebook.com/168378113211268。
然而,我發(fā)現(xiàn)GetGameSchemeId會(huì)從URI中提取ID,并將其添加到app.facebook.com URL中,但是,它并沒有驗(yàn)證slug是否為有效的ID。因此,攻擊者可以將本地應(yīng)用程序窗口重定向到Facebook上的任何其他頁面。
- public static string GetGameSchemeId(Uri uri)
- {
- if (SchemeHelper.GetSchemeType(uri) != SchemeHelper.SchemeType.Game)
- return (string) null;
- string str = uri.AbsolutePath.Substring(1);
- int num = str.IndexOf('/');
- int length = num == -1 ? str.Length : num;
- return str.Substring(0, length);
- }
例如,fbgame://gameid/evilPage會(huì)把Gameroom窗口重定向到https://apps.facebook.com/evilPage。
但是,我怎么才能在Gameroom中重定向到攻擊者控制的代碼呢?這里有多種方法,其中包括濫用app.facebook.com上的開放式重定向漏洞。不幸的是,當(dāng)時(shí)我手頭一個(gè)這樣的漏洞也沒有。另一種方法是重定向到允許將自定義代碼嵌入iframe的Facebook頁面或廣告。
這時(shí),我遇到了一個(gè)障礙。我需要重新修改GetGameSchemeId的代碼,因?yàn)樗蝗RI路徑中的第一個(gè)slug,所以fbgame://gameid/evilPage/app/123456會(huì)將原生應(yīng)用窗口引導(dǎo)到https://apps.facebook.com/evilPage,并會(huì)丟棄/app/123456。
幸運(yùn)的是,我還可以借助于其他的代碼gadget。另外,在Gameroom中使用的Chrome瀏覽器版的確有點(diǎn)陳舊:63.0.3239.132——而當(dāng)時(shí)的版本是86.0.4240.75。因此,它并不支持新版本的Facebook Pages。經(jīng)典的Facebook Pages版本會(huì)接受一個(gè)sk參數(shù),這樣,https://apps.facebook.com/evilPage?sk=app_123456就會(huì)生成一個(gè)自定義標(biāo)簽頁,其中攻擊者控制的代碼位于https://apps.facebook.com/evilPage/app/123456!
但是,怎樣才能在我們的自定義方案中注入額外的查詢參數(shù)呢?別忘了,Gameroom會(huì)丟棄第一個(gè)URL slug之后的所有內(nèi)容,包括查詢參數(shù)。果真是這樣嗎?回顧FacebookGames/SchemeHelper.cs,我發(fā)現(xiàn)了GetCanvasParamsFromQuery:
- public static IDictionary
- {
- if (uri == (Uri) null)
- return (IDictionary
- string stringToUnescape;
- if (!UriHelper.GetUrlParamsFromQuery(uri.ToString()).TryGetValue("canvas_params", out stringToUnescape))
- return (IDictionary
- string str = Uri.UnescapeDataString(stringToUnescape);
- try
- {
- return JsonConvert.DeserializeObject<IDictionary
- }
- catch
- {
- return (IDictionary
- }
- }
在傳遞自定義URI之前,GetCanvasParamsFromQuery會(huì)尋找查詢參數(shù)canvas_params,并將其序列化為JSON字典,并將其轉(zhuǎn)換為新的URL,并用作查詢參數(shù)。
這讓我想到了最終的payload方案——fbgames://gameid/evilPage?canvas_params={"sk": "app_123456"}將被Gameroom解析成本地應(yīng)用瀏覽器窗口中的https://apps.facebook.com/evilPage/app/123456,然后執(zhí)行我的自定義的JavaScript代碼。
如前所述,原生應(yīng)用的威脅環(huán)境與Web應(yīng)用有很大區(qū)別。通過將嵌入式Chrome原生窗口重定向到攻擊者控制的Javascript代碼,攻擊者可以繼續(xù)在3年前的嵌入式Chromium瀏覽器上執(zhí)行已知的漏洞。雖然完整的漏洞利用代碼尚未公開發(fā)布,但我仍然可以利用CVE-2018-6056的概念驗(yàn)證代碼,通過類型混淆漏洞使Chrome引擎崩潰。
另外,攻擊者可以創(chuàng)建本質(zhì)上是合法的原生MessageBoxes的彈出框來執(zhí)行釣魚攻擊,或者嘗試讀取緩存的憑證文件。幸運(yùn)的是,與集成Node.JS APIs的Electron應(yīng)用程序不同,CefSharp限制了API的訪問。然而,它仍然容易受到Chromium和第三方庫漏洞的影響。
小結(jié)
Facebook將本文介紹的安全漏洞評(píng)為高危,并隨即修復(fù)了該漏洞。同時(shí),我藉此榮升Bountycon排行榜的前十名。雖然Gameroom即將退出市場,但它仍然給我留下了攻擊性逆向分析的美好回憶(和實(shí)踐機(jī)會(huì))。對(duì)于應(yīng)用逆向工程的新手來說,Electron、CefSharp和其他基于瀏覽器的框架是一個(gè)很好的練手工具,可以用于測(cè)試跨站腳本攻擊和開放式重定向等類Web漏洞,同時(shí),還可以利用桌面應(yīng)用特有的代碼執(zhí)行手段。
本文翻譯自:https://spaceraccoon.dev/applying-offensive-reverse-engineering-to-facebook-gameroom如若轉(zhuǎn)載,請(qǐng)注明原文地址。