跳转到主要内容

CVE-2026-40344:Snowball 自动解包认证绕过

Snowball unsigned-trailer 请求原本可在完成认证前进入解包器;提交 efb6e5b00 将 SigV4 验签与 newUnsignedV4ChunkedReader 初始化前移到任何 tar 字节进入 untar() 之前。

状态: 已发布
首个包含版本: 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 归档可正常流式解包。
go test ./cmd -run 'Test.*Snowball.*' -count=1

兼容性与运维边界

  • 拦截未验签请求组合:此前依赖未实际验签的 unsigned-trailer Snowball 请求客户端,在升级后会在解包开始前收到 AccessDenied。
  • 共享 reader 边界:Snowball 自动解包与普通 PutObject 的 unsigned-trailer 路径现在共用 newUnsignedV4ChunkedReader(),后续对该分块读取器的修改必须同时回归两条路径。
  • 解包器内部边界独立:将认证前移至 untar() 之前解决了未授权进入解包器的问题,而已授权归档内部的条目路径规范化、归档体积与对象数量控制仍属于独立的防御面。