Brotli CVE-2026-111497 (Node2Shell / HTTP2Shell):受影响版本、修复版本和临时解决方案

CVE-2026-111497 发生了什么

CVE-2026-111497 是 Google Brotli 解码器中的一个严重越界写入漏洞,影响所有平台上的 Brotli 1.0.3 至 1.2.0 版本。Node.js 在 2026 年 10 月 21 日那一周将其披露为“Node2Shell”,随着安全研究人员确认相同的解码器漏洞影响 Python 包、原生 libbrotli、curl、Bun、Envoy、Istio、Git for Windows 以及大量捆绑 Brotli 解码器的应用程序,该名称扩展为“HTTP2Shell”。修复版本在 Brotli 1.2.1(以及向后移植的 1.0.10 和 1.1.1)中提供,任何静态链接或捆绑易受攻击的 Brotli 解码器的库或应用程序都需要发布自己的修复版本。

Aikido 于 2026 年 10 月 23 日发布了汇总公告,指出补丁仍在整个生态系统中陆续发布,供应商正在按顺序宣布受影响的组件。

技术根本原因:DecodeBlockTypeAndLength() 中的模糊布尔值

该缺陷位于 DecodeBlockTypeAndLength() 中,这是处理块切换的 Brotli 解码器函数,块切换是 Brotli 流更改其使用的霍夫曼树的位置。在易受攻击的版本中,该函数返回一个普通布尔值,因此 BROTLI_FALSE 可能表示“此流格式错误”或“我需要更多输入”。调用者无法区分这两种情况,在快速解码路径上,解码器完全丢弃返回值。

精心构造的流可以声明单个块类型,然后耗尽该块的长度,迫使块切换无法成功。解码器要么报告需要更多输入(对于实际上完整的流),要么继续使用已有的块状态进行解码。第二条路径产生越界写入。

上游修复,google/brotli 提交 0ed4268,使该函数返回错误代码,并引入新的错误值 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (-17)。现在,在安全路径和快速路径上,解码都会在格式错误时停止。对 Brotli 1.2.0 和 1.2.1 源码发行版的差异分析发现,1.2.1 仅与版本号提升和该提交中的解码器更改不同。

CVE 记录将 CWE-252(未检查的返回值)与 CWE-787(越界写入)配对,并分配 CVSS v4.0 评分为 9.2(严重)。

攻击影响:内存泄漏、崩溃或双向远程代码执行

CVE-2026-111497 允许远程、未经身份验证的攻击者发送精心构造的 Brotli 压缩流,从而破坏内存、泄漏敏感数据、使进程崩溃,或可能实现任意代码执行。该漏洞被报告为未经身份验证的远程代码执行,且双向有效,这意味着恶意客户端可以接管未修补的服务器,恶意服务器可以接管接受压缩响应的未修补客户端。

两条数据路径暴露了解码器:

  • 出站请求。 应用程序从攻击者控制的服务器获取 Brotli 编码的响应,例如通过 webhook 传递、URL 预览、爬虫或“获取此 URL”功能。
  • 入站请求。 框架或中间件解码使用 Content-Encoding: br 发送的请求体,这相当于 Node.js 中 Express 5 的 Python 版本。

截至 2026 年 10 月 22 日,The Hacker News 报告未发现野外利用。

CVE-2026-111497 的修复版本

每个捆绑 Brotli 的项目都必须发布自己的更新,因为根本原因已在源代码中上游修复,但直到现在才发布包含修复的上游版本。2026 年 10 月 23 日审查的修复版本:

项目 补丁状态 修复版本 备注
Node.js 补丁可用 22.23.4, 24.22.1, 26.12.1 使用受支持的发布线
Bun 补丁可用 1.4.4 包含 Brotli 1.2.1
Envoy 补丁可用 1.40.0, 1.39.3, 1.38.6, 1.37.8 1.36.12 也包含修复,但 1.36 不受支持
Istio 补丁可用 1.29.9, 1.30.6, 1.31.2 1.29 已停止维护;请迁移到受支持的版本线
Google Brotli(上游) 补丁可用 1.0.10, 1.1.1, 1.2.1 为匹配的库分支选择修复版本
Python: Brotli (PyPI) 补丁可用 1.2.1 发布说明未提及 CVE
Python: brotlicffi (PyPI) 补丁可用 1.2.1.0 发布说明未提及 CVE

公告中的其他指导:

  • curl / libcurl: 将 Brotli 依赖项更新到 1.2.1、已修补的旧版本或供应商的安全修复包。捆绑或静态链接的副本需要更新应用程序构建。
  • 操作系统包: 安装供应商的安全更新。供应商可能会在不更改上游版本号的情况下向后移植修复。
  • 其他 HTTP 服务器、反向代理、API 网关和 HTTP 客户端: 更新受影响的运行时或它们使用的 Brotli 库。如果 Brotli 捆绑在产品中,请安装产品供应商明确解决此 CVE 的版本。对于托管服务,请与提供商确认修复措施。

升级后,更新依赖锁定文件,重建受影响的应用程序和容器镜像,重新部署并重启服务,然后验证正在运行的工作负载使用已修补的组件。

为什么一个 Brotli 漏洞影响这么多语言和产品

Python、Node.js、C++、libbrotli、Envoy 和 Bun 都受到影响,因为它们编译或捆绑相同的 C Brotli 解码器。解码逻辑只有一个实现,因此单个内存安全缺陷会传播到每个嵌入它的运行时。

两个 PyPI 包,Brotli 和 brotlicffi,编译了与 Node.js 捆绑的相同的易受攻击的解码器,Brotli 每月下载量为 8280 万次。HTTP 客户端如 urllib3(因此也包括 requests)、带有 brotli 附加功能的 httpx 以及 aiohttp 在存在时自动加载任一包,并在应用程序不直接导入 Brotli 的情况下解码 Brotli 响应。Scrapy 和其他包将 Brotli 作为依赖项安装。因此,Python 应用程序可能在其自身源代码中不包含任何 Brotli 导入的情况下暴露。

某些组件不易受解码路径影响。Apache 的标准 mod_brotli 不解码 Brotli,Apache 和 Nginx 默认不解码 Brotli。

咨询数据库缺口:pip-audit、Dependabot 和 Safety 尚未标记

截至 2026 年 10 月 23 日,咨询数据库尚未将 CVE-2026-111497 映射到 Brotli 或 brotlicffi PyPI 包。CVE 记录、OSV 和 GitHub Advisory Database 未列出这两个包,这意味着 pip-audit、Dependabot 和 Safety 不会标记易受攻击的版本。GitHub 公告已发布在 GHSA-9v8h-qqgm-4m35,报告者正在提交 PYSEC 条目以及 GitHub 公告的 PyPI 包映射,以便工具能够识别。在该映射落地之前,暴露检查必须手动进行。

如何检查您的应用程序是否暴露

安装易受攻击的 Brotli 解码器副本就足以暴露,即使没有应用程序代码调用它。实际检查:

  1. 在每个环境中使用 pip show Brotli brotlicffi 检查已安装的版本,并使用 docker run --rm <image> pip show Brotli brotlicffi 检查每个容器镜像。
  2. 找出哪些依赖引入了这些包。运行 pipdeptree -r -p Brotli 查看哪些依赖需要它,并使用 grep -i brotli poetry.lock uv.lock requirements*.txt 搜索锁定文件。
  3. 列出指向您不控制的服务器的出站 HTTP 调用,以及任何解码 Content-Encoding: br 请求体的端点。
  4. 首先升级与步骤 3 匹配的服务,然后升级其余部分。

对于复现工作:brotli.decompress() 对任何错误都会引发通用的 brotli.error: decoder failed,因此从 Python 中看不到具体的错误代码。在 1.2.0 上,将精心构造的流增量输入 Decompressor() 永远不会完成。使用 AddressSanitizer 构建的 Brotli 1.2.0 和 1.2.1 测试表明,1.2.0 消耗了大约 23 MB 的精心构造的流,同时反复报告需要更多输入,无论流是一次性到达还是以小至一个字节的块到达;版本 1.2.1 每次都以错误 -17 拒绝该流。

如果无法立即升级的临时解决方案

这些缓解措施在准备补丁期间减少暴露:

  • Python 应用程序: 如果 Brotli 支持是可选的,请从受影响的环境中卸载 Brotli 和 brotlicffi。删除依赖项附加功能,如 httpx[brotli] 以及任何会重新安装它们的其他要求,然后重新生成依赖锁定文件并重建或重启工作负载。httpx、requests 和 urllib3 在没有可选包的情况下保留 gzip 和 deflate 支持,但服务器仍然发送 Brotli 可能会产生不可读的响应或应用程序错误,并且不保证自动回退。
  • 入站 HTTP 请求: 在反向代理、负载均衡器或应用程序中间件中,在任何易受攻击的解码器处理请求体之前,拒绝其 Content-Encoding 包含 br 的请求。匹配 br 时不区分大小写,包括当它出现在编码列表中时。
  • 出站 HTTP 请求: 从 Accept-Encoding 中删除 br,并禁用 Brotli 响应解码或在解码开始前拒绝其 Content-Encoding 包含 br 的响应。检查必须在自动处理请求体之前进行;在客户端下载并解码响应后检查为时已晚。

仅更改 Accept-Encoding 是不够的,因为恶意服务器可以无视客户端通告的内容发送 Brotli 响应,并且 urllib3 根据响应的 Content-Encoding 头选择其解码器。卸载 Python 包也不会删除捆绑在其他运行时、库或产品中的单独 Brotli 解码器。

披露时间线和 OpenAI 二月份的发现

Aikido 的 TL;DR 指出 Node.js 于 2026 年 10 月 21 日星期三披露了 CVE-2026-111497,而同一公告的后续时间线部分将 Node.js 披露描述为 2026 年 10 月 22 日星期三;来源列出了两个日期。Python 第二天发布了修补相同漏洞的安全更新。10 月 23 日星期五,人们知道该 CVE 影响其他主要语言和使用 HTTP 的服务应用程序,补丁尚未完全可用。

OpenAI 表示其在二月份发现了该漏洞。根本原因当时已在 Brotli 源代码中修复,但未被识别为安全问题,直到 2026 年 10 月才发布包含修复的版本。OpenAI 将其报告为双向工作的未经身份验证的远程代码执行,并发布了在 patchtheplanet.org/brotli 引用的公告。

Hacker News 评论者对归属提出了质疑。一位评论者认为二月份的更改来自 Google 而不是 OpenAI:

文章声称 OpenAI 在二月份发现了这个问题,但我认为这不正确。我认为 Google 在二月份对 Brotli 源代码进行了更改,修复了该问题,但没有意识到这是一个安全问题,因此没有发布版本。

另一位评论者强调了报告严重性与发布版本之间的差距:

报告为双向工作的未经身份验证的远程代码执行……但当时未被识别为安全问题,直到现在才发布包含修复的版本。我总是把危险 RCE 漏洞和无害 RCE 漏洞混为一谈。我很好奇这是如何在二月份报告和修复的,但没有人认识到其重要性。这是 AI 自动化且无人值守的案例吗?

社区反应:命名、复杂性和 C 语言辩论

Hacker News 讨论中最一致的抱怨是名称。“Node2Shell”在确认 Python 和其他生态系统受影响后不再描述范围,而“HTTP2Shell”被广泛拒绝为过于宽泛和模糊:

好吧,选择 HTTP2Shell 这个名字的人肯定没有使用任何智能,无论是人工的还是真正的。它太无意义、太宽泛、太不描述实际问题了。不,它可能不会影响没有其他东西的 HTTP 服务器,那么为什么要攻击 HTTP?

线程中提出的替代名称包括 Brotli2Shell 和 Broccoli2Shell。一位评论者认为 HTTP 只是该漏洞的附带现象:

更喜欢 Broccoli2Shell 名称和网站(针对同一漏洞)。首先,我很确定如果你手动进行任何 Brotli 解码,你可以在没有 HTTP 的情况下触发它,HTTP 恰好是最常见的案例。

几位评论者质疑在默认配置中实际解码 Brotli 压缩的请求体的普遍程度,认为公告模糊了正常响应解压缩和更冷门的入站路径之间的区别:

对我来说,令人惊讶的是有多少实现了请求(而不仅仅是响应)体的 brotli 压缩,考虑到这是一个多么冷门的功能。无论如何,所有这些耸人听闻的言论开始变得烦人。修补你的东西然后继续前进。

还出现了复杂性论点,将其框定为替换 gzip/deflate 的成本:

我想这是增加复杂性的代价的一个例子。Brotli 比它替换的 gzip/deflate 复杂得多。……当然我们仍然必须暴露 gzip 以进行攻击。每次你添加一些东西,你就会失去一些东西……

一个线程围绕这是否是反对 C 的论点:

老实说,是时候摆脱 C 作为在安全因素重要的地方使用的语言了。受够了。为什么像 brotli 这样处理公共互联网流量并由 Google 维护的库不是用 Rust 编写的?

一位上游补丁的审阅者认为它很仓促:

我甚至阅读了 C 代码的补丁(顺便说一句,对我来说似乎有点 hacky,添加了 isSafe? 方法等等……这个补丁在我看来真的像“给缓冲区溢出的 C 猪涂口红”)。……Python / Node / C++ / libbrotli / Envoy / Bun 之间有什么联系:它们是否都使用相同的 C brotli 编码器/解码器?

对该评论者问题的回答是肯定的:它们都嵌入或链接相同的 Brotli C 解码器,这就是为什么仅上游 Brotli 1.2.1 不能保护捆绑自己副本的系统。

评论者报告的操作上受影响的软件

评论者列出了公告表格之外的真实世界暴露:

  • Git for Windows 即使在其最新版本(2.56.0.windows.3)中仍然易受攻击;修复已合并到仓库中,但仅在快照构建中可用,后续版本预计在周一发布(2.56.0.windows.4)。通过 HTTPS 从不受信任的服务器拉取仓库是相关风险。
  • Codex(GUI)自带 Git 和 Node,评论者认为风险可能更高;建议的缓解措施是更新 Codex 并清除其缓存 ~/.cache/codex-runtimes。
  • Wireshark 和 ImHex 即使在其最新的仓库提交中仍然易受攻击。
  • Microsoft 365 Copilot 仍然自带未修补的 brotlidec.dll,日期为 10 月 7 日,没有可用的修复,也没有确认的路径到达易受攻击的解码器。
  • Ubuntu 仍然将 libbrotli1 补丁作为中等严重性项目进行审查,这阻碍了 libcurl 的更新,从而影响了 Git 和其他依赖包。
  • 在单个 Windows 开发机器上的文件搜索发现 Brotli 库链接到 Git、Sublime Text、Visual Studio、CUDA、.NET、Inkscape 和 Azure Tools。

一位运维方面的评论者检查了他们的环境,发现完全没有暴露,说明了传递依赖的彩票性质:

我不得不检查我们所有的 Python 部署和安装 BOM,并且非常欣慰 - 结果发现实际上没有安装 brotli 库。像 httpx、requests 和框架这样的 HTTP 库将它们视为可选并自动检测它们的存在。

线程中提出的一个未回答的架构问题是,当负载均衡器终止 TLS 并处理解压缩时,应用程序是否安全:

假设我有一个 node.js 应用程序在 Amazon 弹性负载均衡器后面,它终止 TLS。我的理解是,在几乎所有配置中,我的应用程序都不会受到影响,因为 ELB 会处理解压缩。这通常正确吗?

讨论中没有记录明确的答案,因此实际规则仍然是:审计解码路径中的每个组件,而不是假设边缘代理消除了风险。

补丁速度问题

讨论还捕捉到了依赖管理中的更广泛紧张关系。包生态系统在 2025 年建议工程师对新发布的版本应用冷却期以避免供应链攻击,而关键漏洞现在迫使这些冷却期被搁置:

几个月前,每个人都建议在包升级中使用冷却期。现在我们不断覆盖这些,因为安全事件会立即被利用。我想从现在开始就是这样了。

一些工程师通过自动化修复来回应,一位评论者描述将 Claude Code 指向每个新出现的公告。其他人则直截了当地描述了实际姿态:修补、重建、重新部署,然后继续前进。线程没有争议的一点是,Brotli 解码器现在几乎每个 HTTP 客户端和服务器的不受信任输入攻击面的一部分,并且每个捆绑 Brotli 的项目都必须为 CVE-2026-111497 发布自己的更新。

Sources

相关

  • Dispatch
  • 项目
  • 项目
  • Dispatch
  • Dispatch