WebAssembly(WASM)是一种二进制指令格式,可在大多数现代 Web 浏览器中执行。它是 C、C++、Rust 和 GO 等多种高级语言的编译目标,允许用这些语言编写代码并编译为 WASM。
CVE-2021-38297 揭示了 GO 编译和加载 GO 编译的 WASM 二进制文件时的一个关键错误。该漏洞位于 GO 提供的 JS wasm 加载器(wasm_exec.js)中,该加载器允许加载 WASM 二进制文件时 argv 参数中的数据不受限制。由于 argv 存储在 WASM 的线性内存中,恶意攻击者可能利用这一点,通过过大的 argv 输入覆盖 GO 编译的 WASM 程序的线性内存。
此漏洞存在于 GO 1.17.2 之前的版本中。
本概念验证展示了一个社交媒体应用 Vuln-Twitter,允许多个用户发布内容和评论。Web 服务器基于 Node.js,使用 SQLite 存储帖子和评论数据。
前端使用纯 JS 以及一个名为 wordprocessor.wasm 的 GO WASM 模块。该模块公开了 toLeetSpeak 等方法,可将输入字符串转换为“Leet语”(例如,“Hello!” 变成 “h3ll0!”)。
GO WASM 模块用于以 Leet 语渲染帖子和评论。

在前端渲染过程中,当从服务器接收帖子和评论时,每条评论都会使用 GO WASM 模块渲染为“Leet语”。每条帖子的评论在加载 GO WASM 模块后作为 argv 变量的一部分传递。
此外,GO 模块中有一个方法 processSharedVar(),旨在读取地址 0x5000 处的字符串并将其转换为简化语音(例如,“How are you?” 变成 “How r u?”)。原始帖子被显式添加到线性内存中的 0x5000 位置,以便该方法访问并更改帖子内容。
请参考执行相同操作的代码部分:

渲染评论时的 WASM 线性内存示意图:

总结:
argv 变量处理评论,通过内存地址 0x5000 处理帖子。toLeetSpeak 和 processSharedVar 函数处理评论和帖子内容。考虑到基于 CVE-2021-38297 的 argv 缺少大小检查,出现了潜在威胁。如果恶意用户在他们不拥有的帖子上评论一条过大的评论,这条评论将在渲染时通过 argv 传递。由于没有大小限制,地址 0x5000 处(代表原始帖子)的内容容易被覆盖。
通过利用此缺陷,恶意用户有效地更改了原始帖子内容,类似于存储型 XSS 攻击。随后,当其他人查看页面时,会显示更改后的内容,这是因为共享的前端逻辑应用于每个人的渲染,导致覆盖后的帖子对所有用户可见。

注意:要复现此漏洞,你需要在本地安装 go1.17.1 版本,这是本场景中使用的易受攻击版本。你可以参考官方 go 文档了解如何安装特定 go 版本。
现在让我们尝试复现上述场景:
git clone [email protected]:gkrishnan724/CVE-2021-38297.git && cd vuln-twitternpm install 安装所有依赖npm run resetDB 初始化数据库,其中包含一些帖子和评论。npm run dev 启动本地服务器,在浏览器中打开 localhost:3000,你应该能看到登录页面。现在,让我们使用恶意账户登录,使用凭据 用户名:I_CANT_HACK,密码:hacker,登录后,你应该能看到包含一些帖子的信息流。
这条帖子看起来相当有趣:
Amazon: ready 4 black friday? https://www.amazon.com/blackfriday
如果利用上述技术,我们能否覆盖来自 Amazon.com 的帖子,使其指向一个恶意链接?
请参考 exploit.txt 文件,其中包含一条评论,用大量的“A”填充,以便我们覆盖直到地址 0x5000 的所有内容,在末尾你可以看到文本 ready for black friday? https://evil.com/blackfriday。如果我们复制这段文本并在上述帖子上评论,我们应该能够用上面的文本覆盖原始帖子。
自己试试看吧 :)

在此应用中,我还提供了一个修补脚本。它使用较新版本的 go:
npm run patchServer这将使用新版本重新编译 go 文件,并以修补版本启动服务器。
你现在应该注意到帖子没有被覆盖,如果观察控制台,我们会看到一条错误信息 Argument length too long。

我们演示了一个场景,利用 WASM 线性内存上的缓冲区溢出,我们能够执行存储型 XSS 攻击。然而,必须注意此利用的特定性:它要求我们操纵线性内存中硬编码地址处的文本。在实际的 Web 应用程序中,由于这种特定性,发现此类漏洞可能极具挑战性。此外,在不导致系统崩溃的情况下覆盖线性内存中的任意数据是复杂的,尤其是在处理 GO 的内部模块和数据时,这主要是由于缺乏关于 GO 内存布局的全面文档。
根据我们的理解,我们认为 GO 的线性内存布局如下所示:

虽然这种利用在 Web 开发中引入了有趣的攻击向量,特别是在 WASM 中,但它也带来了与编程语言相关的固有安全风险。例如,考虑一个 C 程序被编译为 WASM 的场景。如果原始 C 程序存在溢出或漏洞,这些风险会转移到 WASM 环境中,使其暴露于类似的漏洞和威胁之下。
我们是卡内基梅隆大学的学生,曾在我们的一个课程(18-739D Hacking101)中展示过这个 CVE 概念验证。你可以在此处查看我们的幻灯片: GOWasm.pptx