Software Supply Chain Security

Symlink Attacks in Package Managers: Following Links to Trouble

Symbolic links in package archives can redirect file operations to unintended locations. Here is how this old trick still works against modern tools.

Bob
Application Security Lead
4 min read

Symbolic links are a fundamental filesystem feature that allows one path to reference another. In the context of package management, they are a security landmine. A malicious package can create a symlink inside its installation directory that points to a sensitive location, then write through that symlink to modify files outside the package boundary.

This attack is subtler than path traversal because the file paths in the archive look legitimate. The treachery is in the link target, not the file path.

The Attack Mechanism

The attack works in two phases:

Phase 1: Create the symlink. The malicious package contains a symlink entry. For example, a directory named config inside the package that is actually a symlink pointing to /etc or ~/.ssh. Most archive formats (tar, ZIP) support symlink entries.

Phase 2: Write through the symlink. The package also contains a regular file at config/authorized_keys. When the archive is extracted, the extraction tool first creates the config symlink (pointing to ~/.ssh), then extracts authorized_keys into config/ -- which now means writing to ~/.ssh/authorized_keys.

The result: the attacker has written their SSH public key to the victim authorized keys file, gaining SSH access to the system.

Where This Hits Package Managers

npm

npm has had multiple symlink-related vulnerabilities. npm packages are distributed as tarballs, and the tar extraction library has historically followed symlinks during extraction. CVE-2021-32803 was a vulnerability in the tar package where symlinks could be used to write files outside the extraction directory.

npm now includes protections against symlink attacks in its extraction code, but the underlying tar package needs to be current for these protections to work.

pip

Python packages distributed as .tar.gz files can contain symlinks. The Python tarfile module extractall() method follows symlinks by default. Crafted sdist packages can exploit this to write to arbitrary locations during pip install.

Wheel files (.whl) are ZIP-based and do not support symlinks, making them inherently safer from this attack vector. This is another reason to prefer wheels over source distributions.

RubyGems

Ruby .gem files are tar archives containing a data.tar.gz with the gem contents. Symlinks in this inner archive have been exploitable in older versions of RubyGems.

Go

Go modules are distributed as ZIP files through the module proxy. The Go specification prohibits symlinks in module ZIP files, and the go tool validates this during download. This is one of the more robust defenses in any package ecosystem.

Defensive Strategies

Reject symlinks during extraction. The safest approach is to reject any archive entry that is a symlink. Most packages have no legitimate need for symlinks. If your extraction code encounters a symlink, treat it as suspicious.

Validate symlink targets. If you must support symlinks, validate that the target resolves to a path within the extraction directory. This check must happen after symlink resolution (using realpath or equivalent), not just by examining the target string.

Use restricted filesystem permissions. Run package installation as a user with minimal filesystem access. If the installation user cannot write to /etc or ~/.ssh, symlink attacks targeting those locations will fail.

Prefer formats without symlink support. When possible, use package formats that do not support symlinks. Python wheels, Go module ZIP files, and other formats that prohibit symlinks are inherently safer.

Extract in a temporary directory and copy. Extract the archive to a temporary directory, validate the contents (checking for symlinks, path traversal, and unexpected file types), and then copy the legitimate files to the installation directory.

The TOCTOU Problem

Some defenses against symlink attacks are vulnerable to time-of-check-time-of-use (TOCTOU) races. The defender checks that a path is not a symlink, then writes to that path. Between the check and the write, an attacker (or a concurrent extraction step) can create a symlink at that path.

Robust defenses use atomic filesystem operations or work within a namespace where symlinks are completely prohibited.

How Safeguard.sh Helps

Safeguard.sh monitors the package manager infrastructure and extraction libraries your build pipeline depends on. When symlink-related vulnerabilities are discovered in npm tar, pip, or other extraction tools, our platform alerts you immediately. Our supply chain analysis identifies packages that contain symlinks or other suspicious archive entries before they reach your development environment.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.