Skip to content

CVE-2026-42600: ReadMultiple Storage-REST Path Traversal

From full preflight path validation to deleting the entire RPC surface: why an internode file-reading endpoint with no production callers since 2024 was removed in 73ac524.

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 Bucket and .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.