通过逆向分析挖掘 Facebook Gameroom 中的安全漏洞

  • 发表时间:
    , 文章来源:MyZaker, 新闻取自各大新闻媒体,新闻内容并不代表本网立场

    去年年底,我受邀参加了 Facebook 的 Bountycon 活动,这是一个受邀者才能参加的应用安全会议,其中有一个现场黑客环节。虽然参与者可以提交任何 Facebook 产品内的漏洞,但 Facebook 邀请我们关注 Facebook 游戏。由于之前测试过 Facebook 的产品,所以我知道这将是一个高难度的挑战。这些年来,他们的安全控制已经越来越严格——即使像跨站脚本攻击这样的简单漏洞也很难找到,这也是为什么他们肯为这些漏洞付出非常高的赏金的原因。因此,顶级白帽黑客往往会从第三方软件的角度来挖掘 Facebook 的安全漏洞,比如 Orange Tsai 著名的MobileIron MDM 漏洞。

    鉴于我的时间有限(由于某些原因,我迟到了),所以,我决定不进行全面的漏洞研究,而专注于对 Facebook Gaming 的访问控制进行简单的安全审计。然而,正如人们所期望的那样,移动和网页应用的安全性都很好。经过一番折腾,我发现了Facebook Gameroom程序,它是一个用于玩 Facebook 游戏的 Windows 本地客户端。于是,我就踏上了将攻势攻击性逆向分析应用于本地桌面应用程序的启蒙之旅。

    关于 Facebook Gameroom

    您是不是还没听说过 Facebook Gameroom ——这很正常,我之前也没听说过。实际上,Gameroom 发布于 2016 年 11 月,被誉为 Steam 的强劲对手,支持 Unity、Flash 和最近的 HTML5 游戏。然而,近年来,随着流媒体的崛起,Facebook 已经将注意力转向其移动和网络平台。事实上,Gameroom 计划在今年 6 月退役。幸运的是,在我挖到其中的安全漏洞时,它还没有退出市场。

    *(photo:MyZaker)

    我注意到的第一件事是,安装 Gameroom 时,无需提升任何的权限。它似乎是一个分步安装的程序:首先,通过一个小型安装程序从网络上下载额外的文件,也就是说,它的安装程序不是一体式的。事实上,我很快就找到了安装目录,即 C:Users< USERNAME >AppDataLocalFacebookGames 由于大多数用户级应用程序都位于该 C:Users< USERNAME >AppData 文件夹中,因此我很快在处找到了安装目录。该文件夹包含许多 .dll 文件以及几个可执行文件。一些事情对我很重要:

    1. Gameroom 自带的 7zip 可执行文件 ( 7z.exe 和 7z.dll ) ,可能已经过时,而且很容易受到攻击。

    2. Gameroom 将用户会话数据存储在 Cookies SQLite 数据库中,这对攻击者来说是一个非常有吸引力的目标。

    3. Gameroom 包含了 CefSharp 库(CefSharp.dll),经过进一步研究,发现这是一个用于 C# 的嵌入式 Chromium 浏览器。

    第三点表明,Gameroom 是用 .NET 框架编写的。我们知道,.NET 框架可以将程序被编译成通用中间语言(CIL)代码,而不是机器代码,也就是说,这些代码可以在通用语言运行时应用程序虚拟机中运行。这样做有许多好处,比如可以提高 .NET 应用程序的互操作性和可移植性。然而,由于这些应用程序被编译为 CIL 而不是纯机器代码,因此,将这些应用程序反编译回接近源码的过程也要容易得多。

    对于 .NET 程序集,DNSpy是事实上的标准。逆向工程师可以用 DNSpy 轻松地调试和分析 .NET 应用程序,包括对它们进行实时修改。于是,我把 FacebookGameroom.exe 拖拽到 DNSpy 中,开始进行分析。

    挖掘含有漏洞的函数

    首先,我们开始查找易受攻击的函数,如不安全的反序列化函数等。限于篇幅,这里就不对反序列化攻击进行详细介绍了,简单来说,这涉及到将数据类型转换为易于传输的格式,然后再转换回来,如果处理不当,这可能会导致严重的漏洞。例如,微软就曾警告不要使用 BinaryFormatter,因为 BinaryFormatter 是不安全的,也不能使其安全。

    不幸的是,在我搜索 "Deserialize" 字符串时,BinaryFormatter 竟然冒了出来。

    *(photo:MyZaker)

    然而,我还需要找到易受攻击的代码路径。为此,我右击搜索结果,选择 "Analyze",然后沿着 "Used By" 链找到了 Gameroom 使用 BinaryFormatter.Deserialize 的地方。

    *(photo:MyZaker)

    最终,这让我找到了 System.Configuration.ApplicationSettingsBase.GetPreviousVersion ( string ) 和 System.Configuration.ApplicationSettingsBase.GetPropertyValue ( string ) 函数。Gameroom 在启动时使用了反序列化函数来检索其应用程序的相关设置——但这些设置是从哪里来的呢?通过考察安装文件夹,我发现了 fbgames.settings,这是一个序列化的 blob。因此,如果我在这个文件中注入一个恶意的反序列化 payload,就可以获得代码执行权限。不过,在此之前,我还需要找到一个反序列化的 gadget。根据已知的反序列化 gadget 列表,我展开了进一步的搜索,发现 Gameroom 使用的是 WindowsIdentity 类。

    在此基础上,我研究出了一个代码执行漏洞的概念验证过程:

    1. 使用 ysoserial 反序列化攻击工具,通过命令 ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o raw -c "calc" -t > fbgames.settings 生成了相应的代码执行 payload。

    2. 接下来,我将 fbgames.settings 复制到 C:Users

    3. 最后,打开 Facebook Gameroom,计算器就弹了出来 !

    虽然获得代码执行权限是件非常令人兴奋的事情,但在与 Facebook 团队进一步讨论后,我们一致认为这不符合他们的威胁模型。因为 Gameroom 是作为用户级应用执行的,所以没有机会升级权限。此外,由于覆盖文件需要某种程度的访问权限(例如,需要通过审批才能被公开列出的恶意 Facebook 游戏),所以没有可行的远程攻击载体。

    在本地应用程序构成的不同威胁环境中,我学到了一个重要的教训——在深入研究代码级漏洞之前,最好先寻找可行的远程攻击载体。

    规划探索路径

    您是否有点击邮件中的链接就神奇地启动了 Zoom 的经验?这背后到底发生了什么?其实很简单,这只是使用了一个自定义的 URI 方案,它允许你像网络上的其他链接一样打开应用程序。例如,Zoom 注册了 zoommtg: URI 方案,来解析像下面这样的链接: zoommtg:zoom.us/join?confno=123456789&pwd=xxxx&zc=0&browser=chrome&uname=Betty。

    同样,我注意到 Gameroom 使用了自定义 URI 方案,以便可以在单击 Web 浏览器中的链接后自动打开 Gameroom。搜索完代码后,我发现 Gameroom 会通过 FacebookGamesProgram.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 已经用 fbgames://URI 打开,它还是会继续通过 SchemeHelper 类对其进行解析:

    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 含有 gameid 主机,它将用 SchemeHelper.SchemeType.Game.Game 来进行解析。如果它使用的是 launch_local 主机,则会用 SchemeHelper.SchemeType.LaunchLocal 来进行解析。于是,我从最有希望的 launch_local 路径开始,追踪到了 FacebookGames.SchemeHelper.GenLocalLaunchFile ( Uri ) :

    public static async Task { string result; if ( SchemeHelper.GetSchemeType ( uri ) != SchemeHelper.SchemeType.LaunchLocal || uri.LocalPath.Length