Cargo 符号链接漏洞 CVE-2026-5223:第三方注册源下的缓存覆写风险

2026-05-25 19 预计阅读时间:1 分钟
来源:blog.rust-lang.org AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

Rust 安全响应团队披露了一个影响所有 Cargo 版本的漏洞——CVE-2026-5223。恶意构造的 crate tarball 可以利用符号链接(symlink)绕过 Cargo 的目录保护,把文件提取到自身缓存目录的上一层,从而覆写同一注册源下其他 crate 的源码缓存。如果你只使用 crates.io,不受影响;但依赖第三方注册源(私有 registry、企业内部源、镜像站)的团队需要立刻关注。

缓存结构与攻击路径

Cargo 构建一个 crate 时,会将其 tarball 解压到本地缓存目录 ~/.cargo/registry/src/ 下,后续构建直接复用这份源码,不再重新下载。Cargo 有防护机制,防止解压时文件逃逸到缓存目录之外。

问题在于:符号链接可以突破这层防护的边界。攻击者在一个 tarball 中放入指向上一层目录的符号链接,解压后该链接指向的是同一注册源下其他 crate 的缓存位置。随后 tarball 中再包含一个同名文件,Cargo 会顺着符号链接将文件写入目标 crate 的缓存——源码被静默替换。

举个简化的例子,假设注册源的缓存布局如下:

~/.cargo/registry/src/my-registry-abc123/
├── crate-a-1.0.0/      被攻击目标
   └── src/lib.rs
└── crate-b-1.0.0/      恶意 crate
   ├── evil_symlink -> ../crate-a-1.0.0/
   └── evil_symlink/src/lib.rs    覆写内容

解压 crate-b 时,Cargo 沿符号链接将 lib.rs 写入 crate-a 的缓存,此后任何依赖 crate-a 的构建都在使用被篡改的代码。

crates.io 为什么没事

crates.io 在上传阶段就禁止包含任何符号链接的 crate,从源头堵死了这条路径。所以只消费 crates.io 的项目不受此漏洞影响。

风险集中在第三方注册源——私有 registry、企业内部源、社区镜像等。这些源如果对上传内容不做符号链接检查,攻击者就有机会构造恶意 tarball。值得注意的是,cargo packagecargo publish 本身不会在打包时添加符号链接,所以正常发布的 crate 不会携带链接。风险来自故意构造的恶意包。

修复时间线与版本范围

所有 Rust 1.96.0 之前版本的 Cargo 都受影响。

修复方案:Rust 1.96.0(计划 2026 年 5 月 28 日发布)中的 Cargo 将在解压时直接拒绝任何符号链接,不再区分来源是 crates.io 还是第三方注册源。这是最干净的修复——既然正常流程不会产生符号链接,那就一律拒绝。

无法立即升级到 1.96.0 的团队,官方建议两条缓解措施:

  1. 审计注册源中已有 crate 的内容,检查是否包含符号链接。
  2. 如果注册源支持配置,开启拒绝符号链接的选项。

立刻可以做的事:排查与缓解

下面给出几个可以直接运行的检查命令,帮助你评估当前环境的风险。

检查项目是否依赖第三方注册源

查看 .cargo/config.toml.cargo/config 中的 registry 配置:

# 搜索所有 Cargo 配置文件中的第三方注册源定义
grep -r "registry" .cargo/config.toml .cargo/config 2>/dev/null || echo "未找到本地配置"

# 同时检查 Cargo.lock 中是否有来自非 crates.io 的依赖
grep -E 'source = "registry\+[^crates\.io]' Cargo.lock 2>/dev/null || echo "Cargo.lock 中无第三方注册源依赖"

如果以上输出为空或只出现 crates.io 相关内容,你目前不受此漏洞影响。

扫描本地缓存中的符号链接

# 扫描 ~/.cargo/registry/src 下所有符号链接
find ~/.cargo/registry/src -type l -ls 2>/dev/null

# 如果只想看第三方注册源的缓存(排除 crates.io),先确认目录名前缀:
ls ~/.cargo/registry/src/ | grep -v crates.io
# 然后针对特定目录扫描
find ~/.cargo/registry/src/<your-registry-hash> -type l -ls 2>/dev/null

输出为空说明当前缓存中没有符号链接,但这不代表未来下载的包也安全——除非注册源本身做了拦截。

扫描注册源上已发布的 crate tarball

如果你运营私有注册源,可以批量检查已存储的 tarball:

# 假设注册源的 crate 存储目录为 /srv/registry/crates
# 列出所有 tarball 中包含符号链接的条目
for tarball in /srv/registry/crates/*.crate; do
  if tar tf "$tarball" | grep -qE '->'; then
    echo "警告: $tarball 包含符号链接"
    tar tf "$tarball" | grep '->'
  fi
done

tar tf 列出条目时,符号链接会显示为 link_name -> target 的格式,用 -> 过滤即可。

为私有注册源添加上传校验

如果你的私有注册源有上传 API,在接收 tarball 时增加校验逻辑。以下是一个 Python 示例,可在 CI 或服务端部署:

#!/usr/bin/env python3
"""检查 crate tarball 是否包含符号链接,拒绝不安全的包上传"""
import tarfile
import sys

def check_crate_tarball(path: str) -> bool:
    with tarfile.open(path, "r:gz") as tf:
        for member in tf.getmembers():
            if member.issym() or member.islnk():
                print(f"拒绝: {path} 包含符号链接: {member.name} -> {member.linkname}")
                return False
    print(f"通过: {path} 无符号链接")
    return True

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python check_crate.py <tarball.crate> [tarball2.crate ...]")
        sys.exit(1)
    all_ok = True
    for path in sys.argv[1:]:
        if not check_crate_tarball(path):
            all_ok = False
    sys.exit(0 if all_ok else 1)

将此脚本集成到注册源的上传流程或 CI 的 cargo publish 前置步骤中,可以从源头拦截恶意构造。

采取行动的优先级

场景 风险等级 建议动作
仅使用 crates.io 无需操作,但升级到 1.96.0 仍是好习惯
使用第三方注册源,可升级 Rust 5 月 28 日后升级到 1.96.0
使用第三方注册源,短期内无法升级 立即扫描缓存与注册源,添加上传校验
运营私有注册源 部署 tarball 符号链接检查,配置拒绝策略

这个漏洞的攻击前提是攻击者能在第三方注册源上发布恶意 crate——对于有准入控制的私有源,实际风险取决于你的发布审核流程是否严格。但缓存覆写的隐蔽性很高(被篡改的代码只在本地缓存中,Cargo.lock 和源码仓库看不出异常),一旦被利用很难追踪。建议在 Rust 1.96.0 发布前做好排查,发布后尽快升级。


相关推荐