本文介绍“VectorFreed”,一个漏洞利用链,始于我在 librsvg 中发现的一个释放后使用漏洞(CVE-2026-96889)。在 Node.js/Sharp/libvips 构建中,该漏洞可导致命令执行。本文涵盖该漏洞、目前已在应用程序中确认的路径,以及当前可用的修复方案。
一个 SVG 可以通过 XInclude 包含另一个 SVG。在存在漏洞的路径中,当 libxml2 仍在展开外部文档中的实体时,librsvg 就开始解析被包含的文档。如果被包含的 SVG 声明了同名实体,librsvg 会替换并释放第一个实体。而 libxml2 仍持有指向它的指针。
当被包含的解析结束时,libxml2 会继续使用那个旧指针。此时该内存可能已属于其他对象,因此后续写入可能破坏它。崩溃是其中一种结果。在其他情况下,这也会导致命令执行。
攻击者需要构造 SVG 标记才能触达 librsvg。当应用程序接受 SVG 文件时可能发生,但当应用程序根据用户输入构建 SVG 时也可能发生。仅仅在依赖树中的某处使用 librsvg 并不能构成可利用路径;输入必须在图像渲染时到达受影响的构建。
我们测试的 Next.js 路由接受的是文本,而不是 SVG 上传。它将该文本放入 Node.js 版 ImageResponse 的内联 SVG 中。Satori 中一个单独的转义漏洞让该文本能够改变生成的 SVG 标记。随后 Sharp/libvips 将该 SVG 传递给 librsvg。Vercel 将 Next.js 路径追踪为 CVE-2026-94545。Edge 版 ImageResponse 不受此路径影响。这里有一个简短的 Next.js 复现。
第二条 Next.js 路由通过图像优化 API 触达了同一个 librsvg 漏洞。当 API 接受攻击者控制的图像源时,构造的图像可以绕过框架的 SVG 拒绝机制,随后被 Sharp/libvips 当作 SVG 处理。这使得基于 glibc 的 Linux 上的未认证请求能够触发上游内存破坏,而无需使用 Satori 或 ImageResponse。
Vercel 将此路径发布为 GHSA-2464-fhr4-w9xw,归属于上游 CVE-2026-96889。该公告将其评为严重,CVSS 评分为 9.5。
Ghost 的路径不同。工作人员用户(包括 Contributor)可以为他们控制的站点创建书签卡片。处理卡片图像时,就可能在 Ghost 服务器上执行命令。Ghost 将此发布为 CVE-2026-105642。
在本研究过程中,我在多个下游产品中识别出受影响的输入路径,并在其中几个产品中确认了命令执行,包括上述两条 Next.js 路由和 Ghost 案例。输入如何到达 librsvg 因产品而异。这就是为什么上游图像解析器漏洞会出现在乍看之下并不相关的地方;最近的 libheif 案例是另一个例子。这并不意味着每个使用 librsvg 的产品都可被远程利用。
ImageResponse): 当应用程序将攻击者可控的值放入 SVG 内容、属性或样式时,16.2.0 至 16.3.5 版本会受到影响。请升级到 16.3.6。>= 10.0.0 <= 15.5.27 和 > 16.0.0 <= 16.4.0 列为受影响版本。请升级到 15.5.28 或 16.4.1。Sharp 0.35.5 或更高版本包含上游修复。如果你使用 Sharp 的预构建二进制文件,请检查你的应用安装的 @img/sharp-libvips-* 包。它捆绑了 libvips 及其依赖项,包括 librsvg。更新系统副本的 librsvg 可能会让你的应用仍在使用旧版本。
UAF PoC 包含一个针对多个输入路径的 SVG 生成器,可用于检查链起始处的释放后使用漏洞。
EQSTLab Next.js ImageResponse RCE PoC 是针对其固定 Node.js/Sharp 构建版本的该链的一个可用示例。它会自动生成并发送载荷。可用的漏洞利用现已公开,因此请尽快更新受影响的依赖项或缓解输入路径。
[!WARNING] 此链没有通用的 RCE PoC。EQSTLab 示例针对的是其固定构建版本;其他目标可能需要不同的载荷。依赖 SVG
<foreignObject>执行命令的 PoC 并不能证明此处描述的链。
对于其他目标的初步验证,UAF PoC 更为实用。我还将在未来几周内发布技术文章,其中包含从 UAF 到命令执行的步骤,以及涵盖过去一个月对该链研究的复盘。