← Back to blog
7 novembre 2026 · 8 min read

exarch: the symlink vulnerability we reported

GHSA-9r5q-fvpf-p55p: a TAR or ZIP archive could write files outside the extraction directory by abusing a symbolic link already in place. What Liam Giannini of Webcracy found, and why you should upgrade exarch to 0.8.0 or later.

cybersecurityexarchRustPythonNode.jsvulnerability disclosure
Diagram of the vulnerability: a TAR or ZIP archive, a symbolic link already present in the destination, a file written outside the extraction directory

Extracting an archive looks like the most harmless operation in the world: you pick a folder, open the ZIP, and the files land there. But when the archive comes from outside, uploaded by a user, downloaded from an external service, attached to an email, deciding where the files end up is a security question. A crafted archive can try to write outside the chosen folder: into configuration files, into scripts the server runs at startup, into another user's directory.

exarch is a library built precisely to stop that. Its core is written in Rust (exarch-core) and it also ships as a Python package (exarch) and a Node.js package (exarch-rs). The README pitches it as a safe alternative to libraries like Python's tarfile and Node.js's tar-fs, which have a long history of exactly this kind of vulnerability. The central promise is a single one: the files in the archive stay inside the destination directory.

From version 0.6.1 up to, but not including, 0.8.0, that promise could be bypassed. This is the flaw described in advisory GHSA-9r5q-fvpf-p55p, published on 2 November 2026 and fixed in exarch 0.8.0. We reported it: the finding is by Liam Giannini (Liam-G on GitHub), ethical hacker and co-founder of Webcracy. Our thanks to the exarch maintainer, who confirmed the issue and released the fix within a few days.

Webcracy and cybersecurity

Webcracy doesn't only build websites and applications. Cybersecurity is a central part of our work: penetration tests, code review, configuration analysis, and research into the open source libraries and programs that we and our clients use every day. Liam works as an ethical hacker: he looks for flaws using the same methods as an attacker, but with the opposite goal. Whatever he finds is reported privately to whoever can fix it, and made public only once the fix is available. This process is called responsible disclosure, and it's the one we followed here too, as with the FreeScout vulnerability we reported in October.

What happened during extraction

Two ingredients are needed to understand the problem.

The first is a symbolic link, an entry on the filesystem that behaves like a folder but actually points somewhere else. With its default configuration, exarch rejects symbolic links contained in the archive: that's one of the classic protections against malicious archives. But it didn't account for a different case: a symbolic link already present in the destination directory, before extraction even starts. This happens more often than you'd think: shared folders, updates extracted over an existing installation, environments where uploads or cache point to another disk.

The second ingredient is a cache of created directories. To avoid repeating the same checks for every file, exarch remembered which directories it had already created during extraction and treated them as trusted. The problem is that the Rust function used to create them, create_dir_all, returns success even when the path already exists and is a symbolic link to a directory. So exarch recorded as "a directory I created" a link that actually led somewhere else.

All an attacker needed was an archive with two perfectly ordinary entries: a directory redirect/ and a file redirect/pwned.txt. If redirect already existed in the destination as a link to a writable external directory, the first entry put the link into the cache of trusted directories. When validating the second entry, exarch skipped resolving the parent path, because it was trusted, and also skipped resolving the full path, because the archive contained no symbolic links. The O_NOFOLLOW flag used when opening the file only protects the last component of the path, not the intermediate directories. The result: extraction completed successfully and the file showed up in outside/pwned.txt, outside the chosen directory, with content chosen by whoever had prepared the archive.

The problem affected both TAR and ZIP, with the default security configuration. Extraction of 7z files, on the other hand, was not vulnerable, because by design it didn't use the directory cache when validating paths.

How serious it is

The real-world impact depends on where the application runs. In the worst case it's an arbitrary file write outside the destination, limited only by the process's permissions. With the skip_duplicates = false option, which allows overwriting, existing files reachable through the link could also be truncated or replaced. From there the consequences can include modified configurations, attacker persistence, service disruption, or code execution when the written file is later read or executed by another component.

There is, however, a necessary condition: the symbolic link must already be in the destination, and the attacker must know its name. That's why the severity is moderate, in the CWE-59 category, improper link resolution before file access. Moderate doesn't mean marginal, though: the flaw hits the one guarantee for which you'd choose a library like exarch in the first place.

How we verified it

The problem emerged from reading the source code of exarch 0.6.1, following the path of an archive entry from the directory cache (DirCache) through validation in SafePath to the final opening of the file. To confirm it, Liam wrote two regression tests, one for TAR and one for ZIP, which build the archive in memory and extract into purpose-made temporary directories, on Linux with Rust 1.98.0. On the vulnerable version both tests failed, because extraction succeeded and the file escaped the destination.

Along with the report we also prepared a minimal fix: stop using cache membership as proof of safety in TAR and ZIP validation, bringing them in line with what the 7z code already did. With that change the two regression tests passed, as did the existing 801 unit tests and 136 integration tests.

The fix in 0.8.0

The maintainer went further than the minimal patch. In pull request #816, included in version 0.8.0, the cache was removed and replaced by a check that examines every path component under the destination with lstat, for every file written. The parent path is resolved every time, except when it is the destination root itself. The fix applies to TAR, ZIP and 7z, including the atomic extraction modes.

There's one behaviour change to be aware of: by default, extraction now fails if there is any symbolic link in the destination, even one that points inside the destination itself. Anyone who needs them can re-enable them with the symbolic link option, in which case they're accepted only if they stay inside the destination directory. A symbolic link used as the destination directory itself is still allowed.

The pull request openly stated a remaining limitation: a path-based check leaves a time window between verification and write, which can be exploited by someone who already has write access to the destination during extraction. Version 0.9.0, released on 5 November, includes among other changes anchoring writes to directory descriptors, which is the recommended solution for that case. It's a great example of how a report can become an opportunity to harden an entire subsystem, not just to close a single hole.

What to do if you use exarch

If you use exarch-core, the Python package exarch or the npm package exarch-rs in a version earlier than 0.8.0, upgrade. Since 0.9.0 adds further protections around file writes, it makes sense to go straight to the latest available version, checking the breaking API changes listed in the release notes. After upgrading, also check that your extractions don't fail because of legitimate symbolic links in the destination: that's the new default behaviour, and the safest choice is to extract into an empty, dedicated directory.

That advice applies to any extraction library, not just exarch: extract archives that come from outside into a new, empty directory created by your own process, running as a user with only the permissions it strictly needs. That way even a flaw like this one finds no ready-made links to exploit.

Why we do research on open source software

Much of the software running in companies, even in those that think they don't use any, is made of open source libraries like exarch, often maintained by a single person or a small group. The code is public, but someone has to read it with the eyes of someone who wants to break it. Spending time reviewing these projects and responsibly reporting what we find is our way of contributing, and also of knowing exactly what we're putting into production for our clients.

If your application accepts files uploaded by users, processes archives, documents or images from outside, or simply has never been looked at by someone hunting for flaws, we're happy to talk about it. Our penetration testing work starts right there: reading the code and configuration, understanding where data goes unchecked, and telling you precisely what we verified and what we didn't.

Let's talk about the security of your applications

HAVE A PROJECT?

Let's discuss how we can help your business with custom digital solutions.

CONTACT US