当我们想到 漏洞时,脑海中首先浮现的可能是与复杂的浏览器零日攻击链相关的场景。尽管浏览器可能是 V8漏洞最有趣的目标,但我们不应忽视这个开源 JavaScript 引擎还嵌入到无数项目中,不仅限于浏览器。而在 JavaScript引擎被用于跨安全边界来执行潜在不受信任代码的地方,安全问题可能会出现。
其中一个问题影响了超人气的视频游戏 。Dota 采用了一个于 2018 年 12月编译的
的 v8.dll
。没有惊讶的是,这个版本存在多种 CVE 漏洞,其中许多甚至是
,并伴有公开的概念验证(PoC)漏洞利用。我们发现其中一个漏洞,
,在游戏内四个自定义游戏模式中被利用。由于
Dota 中的 V8 并没有沙盒隔离,这个漏洞可以直接允许远程代码执行,影响其他 Dota 玩家。
我们向 Dota 2 的开发者 披露了我们的发现。在回应中,Valve于 1 月 12 日为 Dota 推出了更新,升级了过时且存在漏洞的 V8 版本。此更新立即生效,因为玩家参与在线游戏时,Dota需要保持最新状态。Valve 还采取了额外措施,关闭了有问题的自定义游戏模式,通知受影响玩家,并引入新的缓解措施以降低游戏的攻击面。
Dota在 1 月 12 日更新的 。
背景
Dota 2 是一款 MOBA 游戏,最初于 2013 年 7 月 9 日发布。尽管它已经近十岁(如果算上原版的 Dota 1,可能是 20年),但仍吸引着大约 1500 万的每月活跃玩家。像其他受欢迎的游戏一样,Dota 在背后是一种由多个独立组件组成的复杂软件。我们特别关注的一个组件是
。这是
Valve 自行设计的一个框架,用于使用 HTML、CSS 和 JavaScript 这三种知名网络技术开发用户界面。这里的 JavaScript部分存在问题,因为它是通过脆弱的 V8 版本执行的。因此,恶意 JavaScript 可能利用 V8漏洞,并控制受害者的计算机。对于未修改过的游戏而言,这并不会造成太大问题,因为默认情况下应该只会执行合法的 Valve 编写的脚本。然而,Dota允许玩家高度自定义,这就为威胁行为者提供了机会,让他们尝试向不知情的受害者偷偷植入恶意 JavaScript 代码。
Dota的自定义可以有多种形式:自定义可穿戴游戏内物品、解说包、加载界面、聊天表情等。至关重要的是,还有自定义的社区开发游戏模式。这些本质上是全新的游戏,利用
Dota 强大的游戏引擎,允许任何具备一定编程经验的人实现自己的游戏创意。自定义游戏模式在 Dota 中扮演了重要角色,Valve非常清楚允许玩家通过开发自定义游戏模式来表达创造力的好处。毕竟,Dota 最初来源于 Warcraft III: The Frozen Throne
的一个游戏模式。因此,自定义游戏模式可以通过游戏内的一键安装。结果,市面上有数千个游戏模式可供选择,其中一些极受欢迎。例如,
的玩家超过了 1000 万。
在自定义游戏模式可供普通玩家体验之前,它必须在 发布。发布过程包括
Valve的验证。虽然这可能会排除一些恶意的游戏模式,但任何验证过程都不完美。正如下文所述,至少有四个恶意游戏模式成功潜入。我们认为验证过程主要是出于管理原因,以防止不当内容被发布。隐藏后门的方法多种多样,在验证过程中寻找所有这些方法将极为耗时。
自定义游戏模式的主要逻辑是用 Lua 编写的。这些代码会在游戏服务器上执行,服务器可能是主机玩家的机器,也可能是 Valve拥有的专用服务器。至于客户端脚本,则是来自 Panorama 框架的
JavaScript。这主要用于控制用户界面元素,例如记分板或任务状态条。JavaScript 由 V8 引擎执行,并且完全支持许多高级功能,包括
WebAssembly 执行。此外,还有一个 ,用于提供其他功能。特别有趣的是
$.AsyncWebRequest
函数,它可以与 eval
结合使用,从而为游戏模式植入后门,以便执行从互联网下载的任意 JavaScript代码。或许正是出于这种担忧,$.AsyncWebRequest
函数被弃用,最终完全移除。然而,依旧有变通的方法。例如,服务器端的 Lua代码可以发出网络请求,并通过游戏事件消息 API 将响应传递给客户端 JavaScript。
只是测试
我们发现了四个在 Steam 商店发布的恶意自定义游戏模式,所有这些游戏模式都由同一作者开发。第一个游戏模式(ID1556548695)尤其有趣,因为根据它缺乏实际有效负载的情况来看,似乎是攻击者仅在此处测试
exploit。值得注意的是,攻击者还在此游戏模式中测试了多种其他技术,留下了注释掉的代码或未使用的函数。这为我们了解攻击者的思维过程提供了很好的机会。
攻击者测试漏洞的自定义游戏模式的 Steam 页面。
如上图所示,攻击者对于这个游戏模式的性质非常透明,命名为 test addon plzignore
,甚至在描述中呼吁其他玩家不要下载这个游戏模式。虽然这看起来像是出于良好信任的表示,但我们将很快表明,在另外三个恶意游戏模式中,同一攻击者采取了完全相反的做法,试图让恶意代码尽可能隐蔽。
此自定义游戏模式中的 JavaScript 漏洞可以在 overthrow_scoreboard.vjs_c
文件中找到。这个文件原本是一个合法的
JavaScript 文件,负责实现记分板功能,但攻击者将其内容替换为对
的攻击代码。这个漏洞最初是由 Google 的研究人员 Clément Lecigne 和
Samuel Groß 发现的,作为针对一个已完全修补的三星手机的针对-zero-day(零日漏洞)使用的漏洞链来利用。
现在已有公开的 和
供公众查阅。然而,这些在攻击者最后更新该游戏模式的 2022 年 3月时并不可用。这意味着他们必须自己开发大部分 exploit (即便当时有公开的 PoC,攻击者仍需要一些技术能力将其移植到 Dota 使用的过时 V8版本上)。尽管如此,漏洞的核心代码仍在 CVE 的
中提供。代码片段能够触发漏洞以泄露声称不可访问的 ,然后利用这个泄露的对象破坏一个映射的大小。攻击者将这个代码片段复制到他们的 exploit中,并在这个受损的映射上构建其余 exploit。
触发 CVE-2021-38003 以泄露
TheHole
对象的核心 exploit 代码。注意末尾的 yay!
,这仅仅是喜悦的表达,对 exploit 的功能没有任何必要性。
有趣的是,exploit 中包含大量注释掉的代码和调试打印。这进一步表明,攻击者不得不花费很多精力来武器化这个漏洞。攻击者开发的 exploit部分首先利用受损的映射来破坏一个数组的长度,从而实现相对的读/写原语。接着,它损坏了一个 ArrayBuffer
后备存储指针,以获得一个任意读/写原语。没有 addrof
函数,因为地址是通过将目标对象放置在从受损数组的已知偏移量处来泄露的,然后使用相对读原语。最后,凭借任意读/写的权限,exploit 利用一个
来执行自定义 shellcode。我们已在本地对整个 exploit 在 Dota 中进行了测试,并确认它成功运行。
取自 exploit 的 JavaScript代码片段。注意其中的调试打印、注释和注释掉的代码。
除了这个 JavaScript exploit,自定义游戏模式中还有一个名为 evil.lua
的有趣文件。攻击者在此测试了服务器端 Lua执行的能力。以下是攻击者特别测试的一些 Lua 代码片段:
- 日志记录
- 动态编译额外的 Lua 代码(
loadstring
) - 确定 Lua 解释器的确切版本
- 执行任意系统命令(
whoami
) - 协程创建
- 网络连接性(HTTP GET 请求)
取自 evil.lua
的 Lua 代码片段。
不幸的是,我们无法获取此特定游戏模式的完整更新历史。因此,有可能一些之前版本的有趣代码在我们分析的版本中不再存在。从更新日志我们至少可以看出,该游戏模式经历了九次更新,所有更新均发生在
2018 年 11 月或 2022 年 3 月。由于利用的 JavaScript 漏洞仅在 2021年被发现,我们猜测该游戏模式最初是合法的,并且恶意功能是在 2022 年 3 月的更新中添加的。
后门
发现这个第一个恶意游戏模式后,我们当然想知道是否存在其他类似的漏洞。由于攻击者没有向 Valve报告该漏洞,我们认为他们可能有恶意意图,并试图在更大范围内利用它。因此,我们开发了一个脚本,下载了 Steam 商店中所有自定义游戏模式的所有
JavaScript 文件。这为我们带来了数GB的 JavaScript 代码,我们可以用来查询可疑的代码模式。
不久后,我们发现了由同一作者(也是之前分析的 test addon plz ignore
游戏模式的作者)发布的三个恶意游戏模式。这些游戏模式分别命名为
Overdog no annoying heroes
(ID 2776998052)、Custom Hero Brawl
(ID2780728794)和 Overthrow RTZ Edition X10 XP
(ID2780559339)。有趣的是,同一作者还发布了一个不包含任何恶意代码的第五个游戏模式,名为 Brawl in Petah Tiqwa
(ID1590547173),这让我们十分惊讶。
其中一个具有后门的自定义游戏模式的
Steam 页面。
这三个新游戏模式中的恶意代码更为隐蔽。没有名为 evil.lua
的文件,也没有直接在源代码中可见的 JavaScript漏洞。取而代之的是只有大约二十行代码的简单后门。这个后门可以通过 HTTP 执行任意 JavaScript,给攻击者不仅提供了隐藏 exploit代码的能力,还随后不必更新整个自定义游戏模式便可更新它。
后门开始时,JavaScript 代码向服务器发送自定义的 ClientReady
事件,以通知服务器有新的受害者游戏客户端,等待接收
JavaScript 载荷。服务器端的 Lua 代码会注册一个对 ClientReady
事件的监听器。当接收到这个事件时,它会向其 C&C 服务器发出
HTTP GET 请求以获取 JavaScript 载荷。期望载荷在响应体中,并通过自定义事件 test
转发给客户端 JavaScript。
后门中的 Lua 部分代码,在游戏服务器上执行。
当客户端 JavaScript 接收到此 test
事件时,它会解压载荷,动态创建一个新函数并立即执行。从高层来看,这显然只是一个简单的下载器,能够执行从 C&C 服务器下载的任意
JavaScript。客户端 JavaScript 和服务器端 Lua 代码的合作仅仅是因为 JavaScript 不再被允许直接访问互联网。
后门中的 JavaScript部分代码,在游戏客户端上执行。
当我们发现这条后门时,C&C 服务器已不再响应。尽管如此,我们可以自信地假设这条后门是为了下载针对 CVE-2021-38003 的 JavaScriptexploit。因为所有三个带后门的游戏模式都是该作者在将 JavaScript exploit 引入他们的首个恶意游戏模式后 10天内更新的。然而,我们不确定是否有任何恶意 shellcode 附加在 exploit 中。毕竟,使用
作为 C&C的方式略显不同寻常,可能也表明攻击者只是测试了后门的功能。无论如何,我们可以说这次攻击的规模并不是很大。根据 Valve 的说法,受影响的玩家不超过 200人。
结束语
在发现这四个恶意游戏模式后,我们试图寻找更多,遗憾的是没有线索。因此,攻击者的最终意图并不清晰。然而,我们认为他们的意图并不是出于纯粹的研究,主要有两个原因。首先,攻击者并没有向
Valve 报告漏洞(通常被认为是一件好事)。其次,攻击者试图将 exploit藏在隐蔽的后门中。尽管如此,也有可能攻击者并没有完全恶意的意图,因为这样的攻击者绝对可以利用这个漏洞带来更大影响。
例如,恶意攻击者可能尝试接管一个流行的自定义游戏模式。许多游戏模式都被原始开发者忽视,因此攻击者可以简单地承诺修复错误并继续免费开发。在进行了一些合法更新后,攻击者可以尝试暗中偷偷植入
JavaScript 后门。由于游戏模式在后台自动更新,毫无戒心的受害者玩家将没有太多机会自我防范。
另外,攻击者也可以寻找其他方法来利用这个漏洞而不涉及自定义游戏模式。例如,攻击者可以尝试寻找分离的 XSS 漏洞,以便与 V8 exploit 连接。这样的
XSS 漏洞能够允许攻击者在远程受害者的 Panorama 实例内执行任意 JavaScript。然后可以使用 V8 漏洞突破 Panorama框架。值得注意的是,Panorama 也广泛应用于游戏的主菜单,因此,视 XSS 漏洞的性质,其影响半径可能高达每月 1500 万的玩家。
在结束之前,我们想感谢 Valve 对我们的报告迅速做出回应。我们希望他们今后能够持续更新 V8,并尽可能减少补丁时间差。Valve还与我们分享了一些有关额外缓解措施的计划,我们会对此在实践中得到应用而感到十分兴奋。考虑到潜在影响,我们还建议对未来流行自定义游戏的更新进行非常仔细的审核。
我们也能够理解 Valve 选择在 Steam 上发布自定义游戏模式的决定,即使这可能让他们肩负更多责任。最终,这是对整体玩家安全的积极影响,因为
Valve可以对发布的游戏模式进行审查并移除恶意内容。许多其他游戏并没有这样集成的自定义游戏支持,因此玩家往往不得不从不明的第三方网站下载模组,而这些网站通常已知会捆绑恶意软件。
受威胁指标(IoCs)
SHA-256| 名称
—|—
cca585b896017bd87038fd34a7f50a1e0f64b6d6767bcde66ea3f98d6dd4bfd0
|
overthrow_scoreboard.vjs_c
4fad709e74345c39a85ce5a2c7f3b71d755240d27dd46688fa3993298056cf39
| evil.lua
3c00f15d233a3dd851d68ecb8c7de38b1abf59787643a2159c9d6a7454f9c3b7
|
overthrow_scoreboard.vjs_c
880a0722a5f47d950170c5f66550e1cdef60e4e84c0ce1014e2d6d7ad1b15c14
|
addon_game_mode.lua
85635bd92cc59354f48f8c39c6db7a5f93cabfb543e0bcc3ec9e600f228f2569
|
overthrow_scoreboard.vjs_c
44c79f185576e1ec7d0d7909eb7d4815cbf8348f37f62c0debd0d5056fb1100b
|
addon_game_mode.lua
4d3c6986b924108911709b95cb4c379720c323e6f7b3a069b866b76e0e3ec6b5
|
overthrow_scoreboard.vjs_c
4bb1d6dcb1e12c3e5997b8dc7fa3db45d44bade39cfcceab56f90134ca2d09f3
|
addon_game_mode.lua
标记为
、、、、、