Published: June 2024
Severity: Critical
Introduction
A serious vulnerability (CVE-2025-29787) has been found in the popular zip crate for Rust, which developers use to read and write ZIP archives. If you extract a maliciously crafted ZIP file using certain high-level library functions, files outside your target directory—including critical system files—might be silently and dangerously overwritten.
Let’s break down how this happens, look at exploitable cases, and show secure alternatives after the fix in version 2.3..
What’s the Bug?
The problem lies in how zip extracts files when a ZIP archive contains symbolic links (symlinks). The zip crate, in affected versions, doesn’t check if extracted files are redirected through symlinks to sensitive or unintended locations. This can let attackers overwrite arbitrary files, possibly leading to privilege escalation or remote code execution.
Exploit Scenario
Let’s say you run a Rust program that extracts ZIP files submitted by users, assuming that only files from within the archive will be written inside a safe folder.
But a specially crafted ZIP can do this
malicious.zip/
|- symlink: foo -> /etc/
|- regular file: foo/passwd (with attacker’s content)
When extracted
- The archive first creates a symlink foo to /etc/
- Later, it writes foo/passwd
- Instead of creating foo/passwd inside the extraction directory, this actually writes (and possibly overwrites!) /etc/passwd
Result: The attacker has written arbitrary data to a critical system file.
Here’s a simple Rust example that extracts everything with the unsafe behavior
// Vulnerable extraction with zip crate < 2.3.
use std::fs::File;
use std::io::BufReader;
use zip::ZipArchive;
fn unsafe_extract(zip_path: &str, out_dir: &str) -> zip::result::ZipResult<()> {
let file = File::open(zip_path)?;
let mut archive = ZipArchive::new(BufReader::new(file))?;
archive.extract(out_dir)?; // <-- VULNERABLE
Ok(())
}
fn main() {
// Example usage; do NOT use with untrusted zip files in affected versions!
unsafe_extract("malicious.zip", "/tmp/extract_here").unwrap();
}
Important: Any call to archive.extract() in vulnerable versions is potentially unsafe with untrusted ZIPs.
A ZIP file can be made like this, even from the command line or Python
# In the archive folder:
ln -s /etc/ foo
echo "root:x:::root:/root:/bin/bash" > bar
zip --symlinks myarchive.zip foo bar
Now replace 'bar' with 'foo/passwd' in the archive manifest (easily done with zip utilities or script). When extracted by a vulnerable program, foo/passwd will mean /etc/passwd!
Mitigation: How It Was Fixed
Starting with version 2.3., the zip crate validates every write operation, following symlinks and checking if the real, canonical path stays inside the intended extraction root. This blocks redirections through symlinks from reaching outside the extraction directory.
Upgrade Now:
If you use the zip crate in any project that interacts with untrusted ZIPs, immediately
# Cargo.toml
zip = "2.3."
After upgrading to zip >= 2.3., extraction is safe by default
use std::fs::File;
use std::io::BufReader;
use zip::ZipArchive;
fn safe_extract(zip_path: &str, out_dir: &str) -> zip::result::ZipResult<()> {
let file = File::open(zip_path)?;
let mut archive = ZipArchive::new(BufReader::new(file))?;
archive.extract(out_dir)?; // Now SAFE on zip 2.3.+
Ok(())
}
For extra safety, avoid following symlinks entirely when dealing with files from untrusted sources.
References
- zip crate security advisory (RustSec)
- zip crate changelog
- RustSec Advisory Database
- CVE-2025-29787 entry (NVD) *(may update as CVE is processed)*
Summary
CVE-2025-29787 is a critical security issue in the Rust zip crate. It allows archive creators to trick extraction tools into overwriting files anywhere on disk using symlinks. It is easy to exploit and very dangerous if your app unpacks ZIPs from untrusted users. Simply upgrade to zip 2.3. or higher as soon as possible. Review your code for untrusted archive extraction.
Please share with any developer or operations teams working with Rust and file uploads that could include ZIP files!
Timeline
Published on: 03/17/2025 14:15:22 UTC
Last modified on: 03/19/2025 15:50:49 UTC