CVE-2026-40344: Snowball Auto-Extract Authentication Bypass
Status: Released
First containing release: RELEASE.2026-04-17T00-00-00Z
Fix commit: efb6e5b00
GitHub advisory: GHSA-9c4q-hq6p-c237
Snowball’s PutObjectExtractHandler (X-Amz-Meta-Snowball-Auto-Extract: true) previously omitted the authTypeStreamingUnsignedTrailer authentication case. A tar archive sent with X-Amz-Content-Sha256: STREAMING-UNSIGNED-PAYLOAD-TRAILER and a forged signature could enter untar() before authentication completed, allowing a single unauthenticated request to fan out into multiple object writes.
Commit efb6e5b00 initializes newUnsignedV4ChunkedReader(), validates the decoded content length, and completes SigV4 verification before any tar byte reaches the extractor.
Why the commit subject used a temporary identifier
When the fix was committed on 2026-04-16, the official CVE number had not yet been published by GitHub Security Advisories, so the Git commit subject used the temporary placeholder fake CVE-2026-40028. Once GHSA-9c4q-hq6p-c237 was published, the official identifier was assigned as CVE-2026-40344. We kept the immutable Git commit SHA efb6e5b00 intact and mapped it to CVE-2026-40344 in the advisory ledger.
From one missing authentication branch to bulk object writes
The affected entry point is PutObjectExtractHandler, which handles Snowball batch archive uploads (X-Amz-Meta-Snowball-Auto-Extract: true). When the client selected STREAMING-UNSIGNED-PAYLOAD-TRAILER (authTypeStreamingUnsignedTrailer), PutObjectExtractHandler lacked the switch branch that standard PutObjectHandler used to authenticate the request and wrap the chunked stream.
The impact exceeded a single unauthorized object write: as soon as the unauthenticated tar stream entered untar(), the server extracted each archive entry as a separate object write. A single missing authentication branch was therefore amplified into an arbitrary multi-object write primitive.
Final invariant: the extractor sees zero bytes on authentication failure
Design review established a strict requirement for the fix:
If authentication fails,
untar()must have read zero bytes.
That invariant rules out any design that begins streaming or extracting archive entries and attempts to roll back created objects if a later check fails. Because Snowball extraction writes objects across multiple internal paths, proving a clean rollback is far more error-prone than proving that unauthenticated bytes never cross into untar(). Authentication had to close before the extractor was invoked.
Implementation
Commit efb6e5b00 aligns PutObjectExtractHandler with standard PutObjectHandler for authTypeStreamingUnsignedTrailer:
- adds the
authTypeStreamingUnsignedTrailercase to the handler’s authentication switch; - parses and validates
X-Amz-Decoded-Content-Length; - wraps the request body with
newUnsignedV4ChunkedReader(), which enforces SigV4 signature verification before yielding payload bytes (sharing the reader boundary hardened in CVE-2026-41145); - preserves valid signed Snowball auto-extract uploads and trailing CRC32 checksum verification.
Verification
Regression tests in cmd verify the full boundary:
- rejection of a Snowball unsigned-trailer request carrying a forged SigV4 signature with zero objects created;
- rejection of anonymous Snowball auto-extract writes to non-public buckets;
- successful extraction of valid signed Snowball unsigned-trailer archives with trailing CRC32 checksums.
Compatibility and operational boundaries
- Rejected unverified combinations: Clients that sent unsigned-trailer Snowball requests with invalid or unverified query-string signatures will now fail with
AccessDeniedbefore extraction begins. - Shared reader boundary: Because Snowball auto-extract and standard
PutObjectnow sharenewUnsignedV4ChunkedReader()for unsigned-trailer streams, any future change to chunked trailer parsing must run regression tests for both paths. - Orthogonal archive surfaces: Closing pre-extraction authentication does not alter how
untar()processes archive entry paths, sizes, or object counts once an authenticated caller is inside the extractor.