CVE-2026-42600:ReadMultiple Storage-REST 路径穿越

从完整 preflight 校验到删除整条 API:一个没有生产调用者的内部文件读取接口为什么不值得保留。

状态: 已发布
首个包含版本: RELEASE.2026-06-18T00-00-00Z
GitHub Advisory: GHSA-xh8f-g2qw-gcm7
影响范围: 仅 distributed erasure;需要 cluster-root / internode JWT

/rmpl 的 msgpack body 中包含 BucketPrefixFiles,旧代码直接把它们拼成文件系统路径,没有 containment。最初的修复实现了完整 preflight validation;继续审计调用链后却发现,这条 API 从 2024 年起已经没有 production caller。最终方案因此从“保留并加固”转为删除 route、handler、client、interface 与生成代码。

删除约一千行代码看似比局部校验更大,长期攻击面却更小。

威胁模型

这个漏洞只在 distributed erasure 模式下注册,single-node 部署不受影响。攻击者需要 root secret 派生的 internode JWT、被控节点,或者能够截获未加密的节点间流量。

危险字段位于 msgpack body,不在 URL 或 form 中,因此上层 HTTP path middleware 看不到。xlStorage.ReadMultiple 会直接 join 并读取,允许路径逃出 drive root。

这不是匿名 S3 漏洞,而是从“集群 root / peer”到“节点进程可读文件系统”的边界跨越。

第一版:保留 API,完整校验

最初补丁在 xlStorage.ReadMultiple 中:

  • 拒绝 absolute path、. / .. segment、反斜杠、Windows drive prefix 与 NUL;
  • 校验 drive → volume → prefix → file 的最终 containment;
  • 尽量保留空 Bucket 与 .minio.sys/multipart 历史契约;
  • 在任何 read 或 streaming 发生前返回错误。

这套设计本身可以关闭已知穿越,但审查很快发现了一个 early-return 缺口。

MaxResults 暴露了“边用边校验”的问题

第一版在读取循环里逐项验证 Files。如果请求是 Files=[good, bad]MaxResults=1,函数读取第一项后提前返回,第二项永远不会被校验。

它没有直接读取第二个恶意文件,却破坏了“整个 msgpack request 必须先合法”的修复目标。于是校验被前移成全量 preflight,path length 也在 streaming 前统一检查。

这次转折留下了一条通用规则:存在 early return 或 streaming 的请求,逐项使用前检查不等于整请求安全验证。

最终决定:删除 API

进一步调用链审计确认:

  • 上游在 2024 年 9 月移除了最后一个 production caller;
  • multipart 已改用 ReadParts
  • 当前树没有 in-tree production consumer;
  • 上游正式处置也选择删除 ReadMultiple

最终删除 route、handler、client wrapper、StorageAPI / xlStorage method、metric、datatype 与生成代码,storageRESTVersion 保持原有兼容策略。

方案短期变化长期维护面结论
原地 validationdiff 较小,保留接口永久保留无人使用的高权限文件读取 API放弃
删除 API删除较多接口与生成代码攻击面与维护面最小接受

验证与发布

原地校验阶段运行过 xlStorage、storage-REST client、msgpack encode/decode 与 path edge case focused tests;对抗性 review 补出了 MaxResults 问题。删除阶段检查了 route、client、interface、generated surface 与 caller absence。

公开修复提交为 73ac524,并随 SILO 2026-06-18 发布。本次博客整理没有重新运行删除后的 full suite。

兼容性与结论边界

  • 外部 S3 API 没有变化;
  • 私自调用内部 /rmpl 的第三方实现会失效;
  • mixed-version rolling upgrade 可能出现 protocol mismatch,因此升级时应保持节点版本一致;
  • 删除 endpoint 只证明 ReadMultiple 不再存在,不能外推成所有内部节点 body path 都已经完成 containment 审计。

这个 CVE 的最终修复是正确的,但它也提醒我们:关闭一个 endpoint,与关闭一个缺陷类别,是两种不同结论。

最后修改 August 2, 2026: init commit (8338d5b)