Safeguard
Vulnerability Analysis

Zip Slip: archive extraction path traversal explained

Zip Slip lets malicious archives write files outside their extraction folder via ../ paths — how it works, real CVEs, and how to detect and fix it.

Hritik Sharma
Security Engineer
7 min read

Zip Slip is a path traversal vulnerability class that lets a malicious archive — a ZIP, TAR, RAR, or JAR file — write files outside the directory an application intends to extract into, by embedding relative path sequences like ../../ in entry filenames. Snyk's security research team disclosed the vulnerability class publicly on June 5, 2018, after finding it in archive-extraction libraries across at least five language ecosystems: Java, JavaScript, Go, .NET, and Ruby. Because the flaw lives in the extraction logic itself rather than in any single library, it has resurfaced repeatedly — most notably when Trellix researchers reported in December 2022 that CVE-2007-4559, an identical path-traversal bug in Python's tarfile module, was still reachable in more than 350,000 open-source repositories, fifteen years after it was first disclosed. If your application unpacks archives from any source you don't fully control — uploads, dependency artifacts, backups, container layers — Zip Slip is a bug class worth checking for directly, not assuming your framework already handles.

What is Zip Slip and how does it actually work?

Zip Slip works by exploiting extraction code that concatenates an archive entry's filename directly onto a target directory without validating that the result stays inside that directory. A ZIP file's central directory stores each entry's name as an arbitrary string — nothing in the ZIP format itself restricts it to a simple filename. An attacker can craft an entry named ../../../../etc/cron.d/malicious or, on Windows, ..\..\..\Users\Public\Startup\payload.exe, and if the extraction code does something like outputDir + entry.getName() and writes the file at that path, the archive walks out of the intended output folder. Extract that entry over an existing scheduled task file, an SSH authorized_keys file, or a web server's startup script, and the archive has achieved arbitrary file write — which frequently escalates to remote code execution. The vulnerability requires no memory corruption and no clever encoding; it is a missing string check in code most developers assumed was safe because "it's just unzipping a file."

Which real-world libraries and CVEs were exposed by Zip Slip?

Snyk's original 2018 research found the pattern in widely used extraction libraries spanning multiple ecosystems, not one obscure package. Named projects included Go's mholt/archiver, Java's plexus-utils/plexus-archiver (a dependency pulled in by parts of the Maven build ecosystem), .NET's DotNetZip, and Ruby's rubyzip, along with archive-handling code in products from vendors including Amazon, Apache, LinkedIn, OWASP, Eclipse, and Alibaba. Electron, the framework behind desktop apps like Slack and Visual Studio Code, received its own tracked fix under CVE-2018-1002200 for a Zip Slip–style flaw in its asar archive handling. Snyk's disclosure process ran responsible-disclosure timelines with each maintainer individually, which is why patches landed on staggered dates through mid-2018 rather than a single coordinated release — a pattern typical of vulnerability classes discovered by scanning an entire ecosystem rather than a single CVE report.

Why does this same bug keep reappearing years later?

Zip Slip keeps reappearing because the vulnerable pattern is easy to write, hard to notice in review, and gets reimplemented independently every time a developer writes archive-extraction code from scratch. Python's tarfile.extractall() is the clearest example: the path-traversal weakness was assigned CVE-2007-4559 in 2007, but Python's maintainers didn't ship a default-safe extraction filter until Python 3.12 in October 2023 — sixteen years later — with the filter='data' argument. In the intervening years, the Trellix scan of public GitHub repositories found the unsafe call pattern in use across more than 350,000 projects, meaning a bug fixed "in the library" still shipped in application code that called the vulnerable API without the optional protections. The lesson generalizes past Python: fixing a vulnerable library doesn't fix already-built binaries, container images, or vendored copies of that library sitting in downstream repositories, and archive extraction is exactly the kind of low-visibility utility code that rarely gets a second security review once it works.

How is Zip Slip different from tar slip and symlink-based archive attacks?

Zip Slip and "tar slip" describe the identical path-traversal logic applied to different archive formats, while symlink-based archive attacks are a distinct but related technique. The underlying flaw — an entry name containing ../ sequences that isn't validated before being joined to an output path — applies equally to ZIP, TAR, 7z, and RAR containers, so "tar slip" is really Zip Slip against .tar/.tar.gz extraction code rather than a separate root cause. Symlink-based attacks work differently: instead of writing a file outside the target directory directly, the archive first extracts a symbolic link entry pointing outside the extraction root, then extracts a second entry that writes through that link — a two-step version of the same goal that bypasses checks looking only at the current entry's own path. Docker addressed this exact symlink variant in runc and image-layer extraction (tracked under multiple CVEs across 2019 and 2021) because container image layers are, functionally, tar archives extracted with root-equivalent privileges — making the blast radius of an unpatched extractor a host filesystem escape rather than a single corrupted file.

How do you detect Zip Slip vulnerabilities before they ship?

You detect Zip Slip by finding every code path that calls an archive-extraction API and checking whether it validates resolved output paths before writing, which is a search pattern static analysis tools can automate at scale. Manually, that means grepping for ZipInputStream, TarArchiveInputStream, tarfile.extractall, unzipper, adm-zip, extract-zip, and equivalent APIs in your dependency tree and application code, then confirming each call site either uses a library version with built-in path sanitization or applies its own canonical-path check (resolving the final path with something like Java's File.getCanonicalPath() or Python's os.path.realpath() and verifying it still starts with the intended output directory). At scale, this is exactly the kind of check that benefits from SCA scanning plus reachability analysis: knowing that plexus-utils 2.0.x is present in a build tells you almost nothing about risk on its own, but knowing that your application actually calls its vulnerable extraction method — and that the call is reachable from an endpoint accepting user-uploaded archives — is the difference between a finding that needs triage and one that needs an incident response plan.

How do you fix and prevent Zip Slip in your own code?

You fix Zip Slip by resolving each archive entry's target path to an absolute, canonical path and rejecting extraction if that path falls outside the intended destination directory, before any file is written. In Java, that means calling File.getCanonicalFile() on the joined path and checking it starts with the canonical output directory's path plus a separator (checking with startsWith() alone, without the separator, is a well-known bypass since /output incorrectly matches /output-evil). In Python 3.12+, pass filter='data' to tarfile.extractall(), which strips absolute paths, ../ sequences, and dangerous symlinks by default; on earlier versions, validate manually or use a maintained wrapper library. In Node.js, prefer extract-zip or a current adm-zip release over hand-rolled extraction, and still verify the resolved path server-side. Beyond code fixes, treat any endpoint that accepts uploaded archives — CI artifact ingestion, plugin/theme installers, backup restore features — as an untrusted-input boundary that deserves its own extraction sandbox, size limits, and entry-count limits, since Zip Slip is frequently paired with zip-bomb style resource exhaustion in the same malicious archive.

How Safeguard Helps

Safeguard's SBOM generation and ingest pipeline inventories every archive-handling library across your codebase and containers, so a Zip Slip–prone dependency doesn't stay invisible in a build you haven't audited since it was added. Reachability analysis then determines whether your application code actually calls the vulnerable extraction path — and whether that call is exposed to attacker-controlled input like a file upload endpoint — cutting through the noise of "present but never invoked" library findings that inflate most SCA reports. Griffin AI correlates that reachability signal with your specific extraction logic to flag the exact call sites missing a canonical-path check, rather than a generic "update this dependency" alert. Where a safe fix is well established, such as adding filter='data' or a canonical-path validation wrapper, Safeguard opens an auto-fix pull request with the patch already in place, so your team reviews a diff instead of researching the vulnerability class from scratch.

Never miss an update

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