CVE-2026-42600: ReadMultiple Storage-REST Path Traversal
Status: Released
First containing release: RELEASE.2026-06-18T00-00-00Z
GitHub advisory: GHSA-xh8f-g2qw-gcm7
Affected scope: Distributed erasure only; cluster-root / internode JWT required
The msgpack body of /rmpl carried Bucket, Prefix, and Files. The old code joined those values into filesystem paths without a containment check. The initial fix implemented full preflight validation. Continued call-chain review then found that this API had had no production caller since 2024. The final solution changed from “retain and harden” to removing the route, handler, client, interface, and generated code.
Deleting roughly a thousand lines was a larger source diff than a local validation guard, but it left a smaller long-term attack surface.
Threat model
The vulnerable route was registered only in distributed erasure mode; single-node deployments were unaffected. An attacker needed an internode JWT derived from the root secret, control of a node, or the ability to intercept unencrypted traffic between nodes.
The dangerous fields were inside the msgpack body, not the URL or form data, so upper HTTP path middleware never saw them. xlStorage.ReadMultiple joined and read the resulting paths directly, allowing them to escape the drive root.
This was not an anonymous S3 vulnerability. It crossed the boundary from “cluster root or peer” to “any file readable by the node process.”
First design: retain the API and validate it completely
The initial patch in xlStorage.ReadMultiple:
- rejected absolute paths,
.and..segments, backslashes, Windows drive prefixes, and NUL bytes; - checked final containment across drive, volume, prefix, and file;
- preserved the historical contract for an empty Bucket and
.minio.sys/multipartwhere possible; - returned an error before any read or streaming began.
That design could close the known traversal, but review quickly exposed an early-return gap.
MaxResults exposed the danger of validate-as-you-use
The first patch validated each Files item inside the read loop. For Files=[good, bad] with MaxResults=1, the function returned after reading the first item and never validated the second.
It did not read the second malicious file, but it violated the intended invariant that the entire msgpack request must be valid before execution. Validation was therefore moved into a complete preflight pass, with path-length checks also completed before streaming.
The general rule is useful beyond this endpoint: when a request can return early or stream a partial response, checking an element immediately before use is not equivalent to validating the whole request.
Final decision: delete the API
Further call-chain audit established that:
- upstream removed the last production caller in September 2024;
- multipart had moved to
ReadParts; - the current tree had no in-tree production consumer;
- upstream’s final remediation also deleted
ReadMultiple.
The final change removed the route, handler, client wrapper, StorageAPI / xlStorage method, metric, datatype, and generated code. storageRESTVersion retained the existing compatibility strategy.
| Option | Short-term change | Long-term maintenance surface | Decision |
|---|---|---|---|
| Validate in place | Smaller diff and preserved endpoint | Permanently retains an unused, privileged file-reading API | Abandoned |
| Delete the API | Removes more interface and generated code | Minimizes attack and maintenance surface | Accepted |
Verification and release
The in-place validation phase ran focused tests for xlStorage, the storage-REST client, msgpack encode/decode, and path edge cases; adversarial review exposed the MaxResults flaw. The deletion phase checked the route, client, interface, generated surfaces, and absence of callers.
The public fix is 73ac524, released with SILO 2026-06-18. The post-deletion full suite was not rerun while preparing this article.
Compatibility and claim boundary
- The external S3 API is unchanged.
- Third-party implementations that privately called the internal
/rmplendpoint will stop working. - A mixed-version rolling upgrade may encounter a protocol mismatch, so cluster nodes should be kept on the same version during the upgrade.
- Removing this endpoint proves only that
ReadMultipleno longer exists; it does not establish that every internal node request carrying body paths has completed a containment audit.
This CVE reached the right final fix, but it also leaves an important distinction: closing one endpoint is not the same claim as closing an entire vulnerability class.