一句话总结:CVE-2026-111497 是 Google Brotli C 解码器里的一个堆越界写,CVSS 4.0 给了 9.2 分。它的特殊之处在于位置太底层:Node.js、Bun、Python 的 requests/httpx、Envoy、Istio、cuRL 都会顺带把这段代码打进自己的产物,所以同一个 bug 在三天内沿着各个语言栈炸了一圈。
十月中旬先有一个名字叫 Node2Shell,指的是 Node.js 打包的 Brotli 库出问题。两天后 Python 那边补了同一个洞,接着 Envoy、Istio、Bun 陆续发版,社区才意识到这不是某个运行时的锅。于是有了 HTTP2Shell 这个叫法。
顺带纠正一个容易误会的点:这个名字跟 HTTP/2 协议没关系。取这个名字是因为 HTTP 内容编码(Content-Encoding: br)是最常见的到达路径,跟 h2 还是 h1.1 无关。我第一次看到的时候也以为要做协议层排查,白翻了一会儿 RFC。
先确认你在不在影响面里
漏洞本身很简单:Brotli 1.0.3 到 1.2.0 版本受影响。麻烦的是「受影响」不等于「你 pip list 能看到」,因为大量项目是把 Brotli 源码 vendored 进去的。
| 组件 | 受影响版本 | 修复版本 |
|---|---|---|
| Google Brotli / libbrotli | 1.0.3 – 1.2.0 | 1.2.1(老线 1.0.10 / 1.1.1) |
| Node.js | 22.x / 24.x / 26.x | 22.23.4 / 24.22.1 / 26.12.1 |
| Bun | 1.0.21 – 1.4.3 | 1.4.4 |
| PyPI Brotli | 1.2.0 及以前 | 1.2.1 |
| PyPI brotlicffi | 1.2.0.x | 1.2.1.0 |
| Envoy | 1.36.11 / 1.37.7 / 1.38.5 / 1.39.2 | 1.36.12 / 1.37.8 / 1.38.6 / 1.39.3 / 1.40.0 |
| Istio | 1.29.0–1.29.8 / 1.30.0–1.30.5 / 1.31.0–1.31.1 | 1.29.9 / 1.30.6 / 1.31.2 |
| Go andybalholm/brotli | v1.2.7 之前 | v1.2.7 |
Wiz 给了一个数字:91% 的云环境里有可被这个漏洞影响的资源,82% 的云环境存在公网暴露的 Node.js 实例。这个比例听起来夸张,但想想容器基础镜像、API 网关、服务网格里塞了多少 Brotli,就不难理解。
还有一个更隐蔽的事实:你的代码一行都没提 Brotli,也可能中招。Python 侧只要环境里装了 Brotli 或 brotlicffi,urllib3(也就是 requests)、带 brotli extra 的 httpx、aiohttp 就会自动声明 Accept-Encoding: br 并解码响应。攻击路径是你去请求一个攻击者控制的地址,对面回一段精心构造的压缩流。
原理:一个布尔返回值引发的越界写
漏洞点在上游 c/dec/decode.c 的 DecodeBlockTypeAndLength(),这个函数负责处理 Brotli 流里的「块类型切换」,也就是切换 Huffman 树的位置。
问题在于返回值的语义被压扁了。这个函数返回一个普通 bool,BROTLI_FALSE 同时表示两件完全不同的事:
- 这个流格式坏了,应该报错退出
- 我还需要更多输入,请继续喂数据
调用方没法区分两者。更糟的是快路径上干脆把这个返回值丢掉了。
攻击的构造思路就顺着这个模糊地带:先声明单一 block type,把这个 block 的长度耗尽,逼出一次注定失败的切换。解码器于是走到两条错误路径之一。第一条是「流已经完整了但还说要更多输入」,这一条顶多让解析卡住。第二条则是带着旧 block 状态继续解码,而这时缓冲区的位置已经不对了,写指针越过分配边界,相邻的 decoder state 被覆盖。
CVE 记录同时打了两个 CWE:CWE-252(未检查返回值)和 CWE-787(越界写)。这个组合恰好是上面这条链路的精确描述。
上游修复是 comMIT 0ed4268,把函数改成返回错误码,并新增了一个专门的错误 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH(-17),让安全路径和快路径都在格式错误时停下来。有团队对比了 1.2.0 和 1.2.1 的源码包,发现两者只差版本号和这一个 commit。
AddressSanitizer 的对照测试很直观:喂一段构造好的约 23MB 流,1.2.0 会把数据全吃完还继续要输入(整块喂和逐字节喂都一样),1.2.1 每次都直接返回 -17。
图 1:上下两条攻击路径都通到同一个解码器,下半部分是根因的四步
双向可达是这个漏洞值得单独写一篇的原因。恶意客户端能打解压请求体的服务端,恶意服务端也能打解压响应的客户端。webhook、链接预览、爬虫、URL 导入、会浏览网页的 AI Agent,全在范围内。而入向 WAF 规则通常只管请求,管不到你去抓别人响应的那一段。
自查:三条命令加两处人工确认
优先级按「能扫出来的先扫」排。
第一步,看运行时版本。 这是最容易查也最容易漏的,因为很多人只看应用依赖不看运行时本身。
node -v # 需要 >= 22.23.4 / 24.22.1 / 26.12.1
bun -v # 需要 >= 1.4.4
第二步,查 Python 环境里的两个包。 注意要查所有环境和所有镜像,包括 CI 里临时建的那个。
pip show Brotli brotlicffi
pipdeptree -r -p Brotli # 谁把它拉进来的
grep -rn "Brotli\|brotlicffi" requirements*.txt poetry.lock uv.lock
第三步,查网关和服务网格。 Envoy / Istio 的版本号不会出现在应用仓库里,得去基础设施配置看。
envoy --veRSIon
istioctl version
剩下两处只能人工确认,这两处恰恰是最容易出事的地方:
一是容器基础镜像。node:24-slim、python:3.12-slim 这类镜像里的 Brotli 是编译进去的,manifest 扫描(SCA)看到的是「没有依赖声明」,会报干净。Wiz 的调查里明确提到,绝大多数有漏洞的副本都是 vendored 进运行时、wheel 和静态二进制里的,所以基于 manifest 的扫描全部漏报。
二是能不能禁掉 br。如果升级排期长,临时缓解是:边缘层拒收 Content-Encoding: br 的请求体,同时关掉服务的响应自动解压。前者防入向,后者防出向,只做一半等于没做。
坑清单
坑一:pip-audit / Dependabot 报你干净,其实是漏洞库没映射上。 到 10 月 23 日为止,CVE 还没映射到 PyPI 的 Brotli 和 brotlicffi 包,所以这几个工具都不会标红。别把这个当结论,手动 pip show 一遍。
坑二:只升级了应用依赖,没重建镜像。 静态链接和 vendored 的副本不会因为 pip install -U 就更新。改完 lockfile 必须 rebuild 镜像再 restart,然后在跑起来的容器里再验一次版本,不要在构建机上验。
坑三:以为 Apache 的 mod_brotli 也中招。 它只负责压缩输出,不解码请求体,不在影响面上。这类「名字里有 brotli 但路径不同」的组件要逐个判断,不要按关键词一刀切。
坑四:修了服务端没修客户端。 出向路径同样能打。服务 A 调服务 B,B 挂了没关系,但如果 A 会去抓任意外部 URL(导入、预览、抓 RSS),A 就是那个暴露面。
坑五:把这次当协议层问题排查。 名字带 HTTP2,但跟 HTTP/2 无关。查 TLS、查 h2 帧格式、查 HPACK 全是浪费时间。
边界与后续
这篇只讲原理和自查,不给利用代码。原因不是保守:这个洞的真实利用可靠性在研究人员手里都「因环境而异」,Imperva 在实验室跑通了 Node.js 24.22.0 arm64 的原生构建,但复现率不稳定。对着别人的线上系统试,收益和风险都不划算。
要往深了走,三个方向:读一遍 0ed4268 这个 commit 的 diff(只有几十行,是很好的「错误处理设计」教材);了解一下 Decompression Bomb 和「压缩比与信任边界」的关系,这类洞最近两年明显变多;以及在自己环境里把上面的自查跑一遍,把这个清单固化成 CI 检查。站内另一条线是客户端方向的反调试识别,排查顺序的思路可以互相照搬。
目前 CISA 的 SSVC 评估是「未发现野外利用,未进 KEV」,所以不用恐慌,但升级别拖。这类底层库的洞一旦有公开 PoC,扫描器全网扫一遍只是几天的事。
参考资料
- Phoenix Security - HTTP2Shell / Node2Shell (CVE-2026-111497) 技术分析,含 CVSS 与受影响组件清单
- Wiz 安全公告(Merav Bar)- 跨语言栈影响面与 91% 云环境数据
- Aikido Security - Python 侧影响与 pip 自查方法
- Random Chaos -
DecodeBlockTypeAndLength()原理与 ASan 对照测试细节 - Google brotli 上游修复 commit
0ed4268
- 参考资料

发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。