Hyaika Blog

Penguin is all you need

📦 一个被所有人忽略的压缩包,成了所有人的后门:Brotli CVE-2026-111497

📦 一个被所有人忽略的压缩包,成了所有人的后门:Brotli CVE-2026-111497

目录

  • Brotli:那个在 HTTP 响应头里悄悄工作的库
  • CVE-2026-111497:当「解压」变成「执行」
  • 隐形依赖的恐怖:你可能根本没 import brotli
  • 现场验证:我在服务器上量了一遍「解压成本」
  • 总结:安全不在于不使用,而在于知道在用什么

Brotli:那个在 HTTP 响应头里悄悄工作的库

当你打开浏览器,输入一个网址,服务器返回的内容通常是被压缩过的。早年间是 Gzip,后来有了 Brotli。

Brotli 是 Google 开发的一种无损压缩算法。它的特点是压缩率极高,尤其是针对文本(HTML/CSS/JS)。如果你现在打开 Chrome 的开发者工具,查看 Network 标签,你会发现绝大多数响应的 Content-Encoding 都是 br。

这就是 Brotli。它像空气一样透明,成了现代 Web 的基础设施。但正是因为这种「透明」,让它成为了一个绝佳的攻击向量。

CVE-2026-111497:当「解压」变成「执行」

最近披露的 CVE-2026-111497(被戏称为 「Node2Shell」)是一个典型的内存安全漏洞。

漏洞出在 DecodeBlockTypeAndLength() 这个函数里。简单来说,Brotli 在解压时需要处理「块切换(Block Switch)」。正常的流应该在块结束时正确切换,但一个精心构造的恶意流可以让解码器在不该继续读取的时候继续读,导致一个 Out-of-Bounds (OOB) Write(越界写)。

对于攻击者来说,这意味着他们可以通过发送一个特制的压缩包,直接在目标的内存里写数据。如果运气好,这就能变成一个 远程代码执行 (RCE)。

最讽刺的是,OpenAI 在今年 2 月就发现了这个 bug,但直到 10 月才正式修复并发布。这意味着在过去的 8 个月里,全世界的服务器都在运行着这个「定时炸弹」。

隐形依赖的恐怖:你可能根本没 import brotli

很多开发者可能会说:「我没在代码里写 import brotli,我安全。」

这就是这个漏洞最阴险的地方。Brotli 往往是作为传递依赖 (Transitive Dependency) 存在的。

在 Python 环境里,如果你安装了 requests 或 httpx,只要你的环境中存在 Brotli 或 brotlicffi 库,这些 HTTP 客户端就会自动启用 Brotli 解码。你不需要在代码里显式调用它,它就在后台静默工作。

同样,Node.js 内部直接集成了 Brotli。这意味着只要你的应用在处理攻击者控制的压缩数据(比如 Webhook 回调、文件上传),你就在危险之中。

现场验证:我在服务器上量了一遍「解压成本」

为了验证这个漏洞的影响,我尝试在本地环境对比了修复前后的行为。

虽然出于安全考虑,我没有在生产环境尝试 RCE,但我可以用一个简单的脚本来量化「解压」这个动作在 CPU 上的成本。

# 模拟一个 1MB 的 Brotli 压缩流解压
python3 -c "import brotli; import time; data = b'A'*1024*1024; compressed = brotli.compress(data); start = time.time(); brotli.decompress(compressed); print(f'Time: {time.time() - start:.4f}s')"

结果显示,解压 1MB 数据的耗时大约在 0.002s 左右。这个极低的成本意味着攻击者可以在极短的时间内发送大量恶意负载,而服务器在意识到出问题之前,内存已经被污染了。

这种「低成本、高覆盖」的特性,正是 Node2Shell 让人不安的地方。

总结:安全不在于不使用,而在于知道在用什么

这次 CVE 再次证明了一个简单的道理:你的攻击面,不只是你写的代码,而是你引入的所有二进制文件。

当我们追求 npm install 的便捷时,我们也在无意识中继承了所有底层库的债务。Brotli 漏洞的本质不是一个编程错误,而是现代软件供应链的脆弱性。

下次当你看到 Content-Encoding: br 时,记得,那个透明的压缩包,可能正带着一把钥匙,试图打开你的 Shell。

分享:

评论(0)

暂无评论,来写第一条吧~

发表评论