Description
Zip Slip is a specialized form of path traversal (CWE-22) that targets archive extraction routines. Rather than manipulating URL parameters, an attacker crafts a malicious archive file — ZIP, TAR, JAR, WAR, EAR, GZ, or any other archive format — in which one or more entries contain path traversal sequences in their filename (e.g., ../../etc/cron.d/backdoor). When the application extracts the archive without sanitizing entry names, the traversal sequences are resolved relative to the extraction target directory, writing the file to an arbitrary location on the server's file system.
The vulnerability was formally documented and named in 2018 by Snyk, who found it in hundreds of libraries and tools across Java, JavaScript, Go, Ruby, .NET, and Python ecosystems. Despite years of awareness, Zip Slip continues to appear in upload handlers, plugin installers, firmware update mechanisms, build systems, and desktop applications. It is classified under CWE-22 and falls within A01:2021 — Broken Access Control because the mechanism is a failure to restrict where extracted files land on the file system.
The severity depends on what the attacker can write and where. Writing to cron directories, SSH authorized_keys, web server document roots, or application configuration files can translate directly to remote code execution or persistent backdoor access.
How It Works
A malicious archive is crafted using a script or tool such as evilarc (a purpose-built Zip Slip PoC tool):
python evilarc.py shell.php -o unix -d 5 -p /var/www/html/
# Creates: evil.zip containing ../../../../var/www/html/shell.php
The archive entry name stored inside the ZIP file is literally ../../../../var/www/html/shell.php. When a vulnerable extraction routine processes it:
# Vulnerable Python extraction
import zipfile
with zipfile.ZipFile("upload.zip") as zf:
zf.extractall("/var/app/uploads/")
Python's extractall resolves the traversal sequences and writes shell.php to /var/www/html/shell.php — a web-accessible path. The attacker then sends GET /shell.php?cmd=id to achieve remote code execution.
In Java, the classic vulnerable pattern uses ZipEntry.getName() directly:
ZipInputStream zis = new ZipInputStream(inputStream);
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
File destFile = new File(destDir, entry.getName()); // VULNERABLE
// ... write to destFile
}
The fix requires canonicalizing the resolved path and verifying it starts with the destination directory.
TAR archives are particularly dangerous because they natively support absolute paths and symlinks. A symlink entry pointing to /etc/cron.d followed by a regular file entry named after that symlink effectively writes through the symlink, even if the destination appeared safe.
Impact
- Arbitrary file write — write files to any location writable by the server process, including web roots, cron directories, SSH configuration, and application binaries.
- Remote code execution — writing a web shell to a web-accessible path grants direct command execution on the server.
- Persistent backdoor — adding an SSH public key to
~/.ssh/authorized_keysor a cron job creates persistent unauthorized access. - Configuration tampering — overwriting application config files to change database credentials, disable security controls, or redirect logging.
- Supply chain compromise — in build systems and CI/CD pipelines, Zip Slip in artifact extraction can compromise the entire build process.
- Desktop application exploitation — plugins, themes, and update packages in desktop applications that use vulnerable extraction libraries allow local privilege escalation.
Detection
- Craft a malicious test archive using evilarc or manually: create a ZIP containing an entry named
../../tmp/zipslip_test_<timestamp>.txt. Upload it via every file upload endpoint and check whether the file was created in/tmp/. - Audit source code extraction routines — search for uses of
extractall,ZipEntry.getName(),TarFile.extract(),Archive::extract(), and equivalent functions. Verify that each performs path validation before writing. - Test all archive upload features — plugin uploads, theme installers, firmware updates, backup restore, import features. These are the highest-risk points.
- Check for symlink entries in TAR handling — verify that extraction code checks
entry.isSymbolicLink()and either rejects symlinks or resolves them to confirm the target is within the extraction directory. - Test with multiple archive formats — ZIP, TAR, TAR.GZ, JAR, WAR. A server may handle each with different libraries; only some may be vulnerable.
Remediation
Validate every archive entry name before extraction. Canonicalize the resolved destination path and confirm it starts with the extraction directory:
// Safe Java extraction
public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
File destFile = new File(destinationDir, zipEntry.getName());
String destDirPath = destinationDir.getCanonicalPath();
String destFilePath = destFile.getCanonicalPath();
if (!destFilePath.startsWith(destDirPath + File.separator)) {
throw new IOException("Zip Slip: entry outside target dir: " + zipEntry.getName());
}
return destFile;
}
Use a well-maintained library that handles this automatically. Apache Commons Compress (versions after the Zip Slip disclosure), and many modern language standard library extractors, include built-in path validation. Verify your version is patched.
Reject or strip traversal sequences in entry names as a secondary defense. Any entry name containing .. should be rejected outright.
Disable symlink extraction. Unless specifically required, configure the extraction to skip symlink entries entirely.
Run the extraction process as a minimal-privilege user. Limit the directories the process can write to, so that even a successful Zip Slip cannot write to sensitive system paths.
