CVE-2026-42600: ReadMultiple Storage-REST Path Traversal
Status: Released
First containing release: RELEASE.2026-06-18T00-00-00Z
Fix commit: 73ac524
GitHub advisory: GHSA-xh8f-g2qw-gcm7
Affected scope: Distributed erasure deployments only; cluster-root / internode JWT required
The internode storage-REST route /rmpl (ReadMultiple) decoded a msgpack request body carrying Bucket, Prefix, and Files, and joined those fields directly into filesystem paths without a drive-root containment check.
Our initial fix retained the endpoint and added preflight path validation. A subsequent call-chain audit then revealed that ReadMultiple had had zero production callers since September 2024. We therefore discarded the in-place validation patch and deleted the route, handler, client wrapper, StorageAPI method, and generated msgpack types (73ac524)—eliminating roughly a thousand lines of unused privileged RPC surface.
Threat model
The /rmpl storage-REST route was registered only in distributed erasure mode; single-node deployments do not register storage-REST routes and were unaffected. Exploiting the endpoint required an internode JWT derived from the cluster root credentials, control of a peer node, or interception of unencrypted internode traffic.
Because the attacker-controlled Bucket, Prefix, and Files fields arrived inside the msgpack request body rather than in the URL or query string (r.Form), upper HTTP path-validation middleware never saw them. xlStorage.ReadMultiple joined the raw strings directly onto the drive path and read the resulting files, allowing a caller with internode credentials to escape the drive root and read any file accessible to the SILO server process.
First design: retain the API and validate every path
Our first patch hardened xlStorage.ReadMultiple in place:
- rejected absolute paths,
.and..segments, backslashes, Windows drive prefixes, and NUL bytes; - verified drive-root containment across
drive -> volume -> prefix -> file; - preserved historical edge cases for an empty
Bucketand.minio.sys/multipart; - returned an error before opening files or streaming responses.
That first draft closed the obvious traversal path, but code review immediately caught a flaw in how the loop interacted with MaxResults.
How MaxResults exposed the flaw of validate-as-you-go
The initial draft validated each entry of Files inside the file-reading loop right before opening it. When a request passed Files = [good, "../../etc/passwd"] together with MaxResults = 1, the loop read the first file, hit MaxResults == 1, and returned early without ever inspecting the second entry.
Although that specific call did not read the second file, it violated a core input-validation rule: a malformed msgpack request containing a path-traversal payload must be rejected before any partial execution or streaming begins. We moved path and length validation into a complete preflight pass over the entire request before touching the filesystem.
Final decision: why we deleted ReadMultiple instead
Before merging the preflight-validation patch, we audited every caller of ReadMultiple across the repository and git history:
- upstream had removed the last production caller in September 2024 when multipart reads migrated to
ReadParts; - the current SILO tree had zero in-tree production callers;
- upstream’s own remediation for GHSA-xh8f-g2qw-gcm7 also deleted
ReadMultiple.
| Option | Short-term diff | Long-term attack and maintenance surface | Decision |
|---|---|---|---|
| Validate paths in place | Smaller diff; preserves /rmpl |
Permanently maintains an unused, privileged multi-file read API | Abandoned |
Delete ReadMultiple |
Larger diff across interface and msgpack codegen | Removes the unused endpoint completely while preserving storageRESTVersion wire compatibility |
Adopted |
Commit 73ac524 removes the /rmpl route, storage-REST server handler, client wrapper, StorageAPI and xlStorage methods, metric definitions, and generated msgpack request/response types.
Verification and release
During the in-place validation phase, we ran unit tests covering xlStorage, the storage-REST client, msgpack encoding, path traversal vectors, and the MaxResults preflight edge case. After switching to full deletion in 73ac524 (shipped in RELEASE.2026-06-18T00-00-00Z), verification confirmed that all routes, interfaces, and generated msgpack surfaces compiled and passed go test ./cmd cleanly.
Compatibility and follow-up audit (SN-2026-002)
- External S3 API: Unchanged.
- Rolling upgrades: Keep all nodes in a distributed cluster on the same SILO release during upgrades.
- Follow-up audit across all internode endpoints: All remaining internode endpoints carrying filesystem paths inside msgpack bodies or Grid RPC frames were audited and hardened in SN-2026-002: Internode Path and Allocation Containment (shipped in
RELEASE.2026-08-04T00-00-00Z), which enforced volume and file-path containment across storage-REST, storage Grid RPC, and peer-S3 Grid RPC endpoints.