CVE-2026-40344:Snowball 自动解包认证绕过
状态: 已发布
首个包含版本: RELEASE.2026-04-17T00-00-00Z
修复提交: efb6e5b00
GitHub Advisory: GHSA-9c4q-hq6p-c237
Snowball 自动解包处理器 PutObjectExtractHandler(X-Amz-Meta-Snowball-Auto-Extract: true)此前漏掉了 authTypeStreamingUnsignedTrailer 认证分支。携带伪造签名的 STREAMING-UNSIGNED-PAYLOAD-TRAILER tar 数据流可以在未完成认证的情况下直接进入 untar(),使单次未认证请求扇出为多个对象写入。
最终修复(efb6e5b00)在任何 tar 归档字节进入解包器之前,初始化 newUnsignedV4ChunkedReader()、校验解码后内容长度并完成完整 SigV4 签名验证。
为什么提交标题使用了临时编号
2026-04-16 编写并合入补丁时,GitHub Security Advisories 尚未公布正式 CVE 编号,因此 Git 提交标题使用了临时占位编号 fake CVE-2026-40028。GHSA-9c4q-hq6p-c237 正式发布后,该漏洞编号确定为 CVE-2026-40344。我们保留了不可变的 Git 提交对象 efb6e5b00,并在安全台账与本文中统一映射为正式编号。
从一个遗漏的认证分支到批量对象写入
受影响入口为处理 Snowball 归档自动解包的 PutObjectExtractHandler(X-Amz-Meta-Snowball-Auto-Extract: true)。当请求使用 STREAMING-UNSIGNED-PAYLOAD-TRAILER(authTypeStreamingUnsignedTrailer)模式时,旧处理器没有像普通 PutObjectHandler 那样覆盖该认证类型。
这一遗漏的危害远超单次普通对象误放行:一旦未认证的 tar 数据流进入 untar(),单个请求即可在目标桶内创建或覆盖任意多个由攻击者指定的对象,把一次认证分支遗漏直接放大为批量写入原语。
最终不变量:认证失败时解包器必须看到零字节
设计复核为修复确立了一条硬性不变量:
只要认证最终失败,
untar()读取到的字节数必须为零。
这条规则直接排除了“边解包边校验、失败后再回滚”的思路。由于 untar() 解包出的对象写入会流经多条内部存储路径,事后证明回滚完整远比在入口拦截困难。唯一可靠的收口点,就是在任何字节进入 untar() 之前完成认证决断。
实现细节
提交 efb6e5b00 将 PutObjectExtractHandler 对 authTypeStreamingUnsignedTrailer 的处理与标准 PutObjectHandler 对齐:
- 在认证分发 switch 中显式识别
authTypeStreamingUnsignedTrailer; - 读取并校验
X-Amz-Decoded-Content-Length; - 使用
newUnsignedV4ChunkedReader()包装请求流,在向调用方产出任何数据字节之前完成完整 SigV4 请求验签(与 CVE-2026-41145 共用同一底层 reader 边界); - 完整保留合法签名 Snowball 自动解包请求与尾部 CRC32 校验和(trailer)工作流。
验证与发布
cmd 包中的回归测试覆盖了完整边界:
- 拒绝携带伪造签名的 Snowball unsigned-trailer 请求,且确认桶内零对象落盘;
- 拒绝针对非公开桶的匿名 Snowball 自动解包写入;
- 验证携带合法签名与尾部 CRC32 校验和的 Snowball 归档可正常流式解包。
兼容性与运维边界
- 拦截未验签请求组合:此前依赖未实际验签的 unsigned-trailer Snowball 请求客户端,在升级后会在解包开始前收到
AccessDenied。 - 共享 reader 边界:Snowball 自动解包与普通
PutObject的 unsigned-trailer 路径现在共用newUnsignedV4ChunkedReader(),后续对该分块读取器的修改必须同时回归两条路径。 - 解包器内部边界独立:将认证前移至
untar()之前解决了未授权进入解包器的问题,而已授权归档内部的条目路径规范化、归档体积与对象数量控制仍属于独立的防御面。