CVE-2026-40344:Snowball 自动解包认证绕过
状态: 已发布
首个包含版本: RELEASE.2026-04-17T00-00-00Z
GitHub Advisory: GHSA-9c4q-hq6p-c237
Snowball PutObjectExtractHandler 漏掉了 streaming unsigned-trailer auth case。伪造 signature 的 tar stream 可以在认证完成前进入 untar(),而一次请求又能扇出为多个对象写入。最终修复在任何 tar 字节进入解包器之前,初始化正确 reader、处理 decoded length 并完成 SigV4 验证。
编号为什么变过
修复时正式 CVE 尚未分配,commit subject 使用了临时的 fake CVE-2026-40028。正式编号后来确定为 CVE-2026-40344。历史 commit 没有重写;公开 advisory 与本文一律使用正式编号。
从一次认证遗漏到批量对象写入
入口是 Snowball / PutObjectExtract 自动解包。请求采用 unsigned-trailer streaming,而旧 handler 没有像普通 PUT 一样覆盖该 auth type。
危险不只在于一个请求被错误授权。tar stream 一旦进入 untar(),单个请求可以创建多个攻击者指定的对象。认证错误因此被放大成批量写入问题。
最终不变量:失败时解包器必须看到零字节
修复过程中最关键的一句话是:
如果认证最终失败,
untar()必须看到零字节。
这直接排除了“先解包,认证失败后再回滚”的方案。对象写入会经过多条路径,要证明回滚完整远比证明输入从未越过边界困难。正确收口点只能在数据流进入解包器之前。
实现
最终改动包括:
- 识别
authTypeStreamingUnsignedTrailer; - 读取
X-Amz-Decoded-Content-Length; - 使用
newUnsignedV4ChunkedReader(); - 在进入
untar()前执行完整 SigV4 request verification; - 保留合法 signed Snowball 与 CRC32 trailer flow。
验证
历史 commit 与会话记录覆盖:
- forged-signature Snowball unsigned-trailer 被拒绝;
- non-public bucket 的 anonymous Snowball 被拒绝;
- 合法签名与 trailing CRC32 可以正常解包;
- vulnerable parent 与 patched tree 的红绿对照;
- containerized before/after smoke。
公开修复提交为 b50ab58。本次博客整理没有重新运行容器测试。
兼容性与残余风险
- 过去依赖实际未验证的 unsigned-trailer Snowball 组合的客户端,升级后会失败;
- 认证已经前移,但 tar 内容路径、归档大小与对象数量限制仍是独立安全面;
- Snowball 与普通 unsigned-trailer 现在共享 reader,未来修改必须同时回归两条路径。
这个事件的重点不是多加了一次 if,而是把认证决定移动到真正的放大边界之前。