CVE-2026-42600:ReadMultiple Storage-REST 路径穿越
状态: 已发布
首个包含版本: RELEASE.2026-06-18T00-00-00Z
GitHub Advisory: GHSA-xh8f-g2qw-gcm7
影响范围: 仅 distributed erasure;需要 cluster-root / internode JWT
/rmpl 的 msgpack body 中包含 Bucket、Prefix 与 Files,旧代码直接把它们拼成文件系统路径,没有 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 保持原有兼容策略。
| 方案 | 短期变化 | 长期维护面 | 结论 |
|---|---|---|---|
| 原地 validation | diff 较小,保留接口 | 永久保留无人使用的高权限文件读取 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,与关闭一个缺陷类别,是两种不同结论。