Apple fixed this zip bug in 2008. Upstream never did.

Name a file ';id >poc;'.zip, hand it to a machine that checks uploaded archives, and that machine will run whatever command you buried in the filename. The archive can be perfectly valid. Nothing inside it matters. The attack is the name.

That is the whole bug, and it is roughly as old as the iPhone.

Finnish researcher Harry Sintonen posted the advisory to the oss-security mailing list on 14 August. It concerns zip -T, the option that tells Info-ZIP's zip command to test an archive's integrity. Info-ZIP is not a niche tool: it is the zip binary sitting on most Linux and BSD systems, wired into countless upload handlers, mail gateways, backup scripts and CI jobs. Sintonen classes the flaw as OS command injection, and he is blunt about who gets hurt: "The most impacted are automated systems that perform the command in response to an external party providing a zip file (such as a website upload form or message attachment or similar)."

There is no CVE number yet. One has been requested from MITRE and is reserved as CAN-2026-2034879, and Debian's own advisory says the identifier is still not available. No severity score has been published either, so treat anybody quoting one as guessing.

Every version since 2.2, and no upstream fix

Sintonen lists versions 2.2 through 3.0 inclusive as affected, along with the unreleased 3.1a, 3.1b and 3.1c betas. Version 1.1 and earlier escape only because the feature did not exist yet. Debian has shipped fixes, 3.0-16 in unstable and 3.0-15+deb13u1 for stable, under DSA-6439-1.

One platform is conspicuously safe. "zip command in macOS is not affected," the advisory notes, and the reason for that turns out to be the actual story.

Diagram comparing three versions of the check_zipfile function in Info-ZIP: upstream still building a shell string and calling system, Apple's 2008 patch using an argv array with posix_spawnp and deleting the system call, and Debian's 2026 fix adding a quote_arg escaping helper while keeping system

One function, three answers. Diagram by IntelFusions, with the code quoted verbatim from Apple's patch and Debian's fix.

How a filename becomes a command

Inside zip.c there is a function called check_zipfile(). To test an archive, it builds a text string that looks like a shell command and hands the whole thing to system(), which asks the operating system's shell to interpret it. Building the string means gluing the filename into the middle of it, and the only protection upstream applies is a pair of single quotes:

That comment is worth pausing on, because it shows the author was thinking about safety. Single quotes really do neutralise spaces and dollar signs. What they cannot survive is a single quote inside the filename itself, and nothing here escapes one.

So a file called ';id >poc;'.zip produces this command line: unzip -t -qq '';id >poc;'.zip'. The leading quote closes the opening quote around an empty string, the semicolon ends that command, id > poc runs as a brand new one, and the trailing quote swallows the rest. The shell does exactly what it was asked. There is no memory corruption here and no clever parsing trick, just a string that stopped being data and became a program.

Sintonen's proof of concept is three lines long, which is part of why this matters. It needs no exploit development, no heap grooming and no race window. Anyone who can influence a filename on a system that later runs zip -T over it has code execution as whoever ran the command.

The part nobody noticed for eighteen years

Sintonen found something else on the day he reported it, and hedges carefully about it: Apple "appears to have fixed the very issue in 2008 already," and "it appears this fix never migrated to upstream."

We pulled Apple's patch to check. It is a file called patch-Apple in Apple's published zip source, applied at build time, and the diff headers carry their own timestamps: upstream's zip.c dated 5 July 2008, Apple's modified copy dated 22 September 2008. That is eleven weeks after upstream shipped zip 3.0, which remains upstream's most recent release to this day.

What Apple did was not patch the quoting. It deleted the shell. The patch adds an include for spawn.h, throws away the string building entirely, and replaces it with an argument array handed to posix_spawnp(), with the line result = system(cmd) removed outright. Once the filename is an argument in an array rather than a fragment of a sentence the shell will parse, the injection is not merely blocked, it is structurally impossible.

We also checked whether that fix survived. It did. In Apple's newest published tag the patch is still present, carrying the same 22 September 2008 timestamps, the same spawn.h include, and the same two posix_spawnp calls. It has ridden unchanged for nearly eighteen years, and it is why the zip on a Mac shrugs at the proof of concept.

One thing we cannot tell you is whether Apple knew. The patch has no description, no changelog entry and no authorship metadata, and it bundles unrelated portability fixes alongside the rewrite, which is what a general maintenance pass looks like. It also removes a feature and hardens two format strings, which is what a security pass looks like. The honest claim is about effect, not intent: Apple's 2008 rewrite eliminated this injection and has kept macOS clear of it ever since.

Three answers to one function

Which leaves a genuinely interesting split. Sintonen's recommendation to upstream reads: "Do not use the system() function to execute commands. Rather, always use posix_spawn() when available." That is, word for word, the thing Apple already did in 2008.

Debian went the other way. We read its patch too. It adds a helper called quote_arg() that wraps the filename in double quotes and escapes the dangerous characters, then feeds the result into the same string and calls system() exactly as before. The shell stays. To be clear, it works: inside double quotes a single quote is just a character, the proof of concept dies, and we found no weakness in it and are alleging none. Debian users who have updated are fine.

But it is a different philosophy. One fix removes the category of bug. The other escapes harder inside the design that produced it.

Why upstream is silent

It is tempting to read eighteen years of exposure as negligence, and that is not quite right. Info-ZIP's own homepage still announces zip 3.0 as the latest release, dated 7 July 2008. The page footer reads "Last updated 4 October 2008." It still promises that the next major release will be version 3.1, with AES encryption and Unicode comment support. That release has never arrived, and the betas that do exist are affected too.

Upstream did not decline to fix this. Upstream stopped shipping, eighteen years ago, while its code kept propagating into everything. That is the uncomfortable part, and it is not unique to zip: the same week we wrote about rsync clearing 33 CVEs in a single release, another decades-old Unix utility that quietly underpins far more infrastructure than its maintenance level suggests.

What to do about it

If you run Debian or a derivative, update zip and you are done. If you run anything else, check whether your distribution has picked up a patch, because upstream has none to give.

The mitigation that works everywhere is simpler: stop using zip -T. Sintonen's own advice is to replace it with unzip -t, which does the same integrity check without the shell round trip. If you maintain a pipeline that accepts archives from outside, that one substitution closes this today regardless of what your package manager offers.

And the broader lesson is the cheap one. The reason this bug existed for eighteen years is that a filename was allowed to become part of a sentence the shell reads. Command injection through attacker-controlled names is the same shape as the flaw that handed root on Haiwell industrial gateways earlier this week. Passing arguments as arguments is not a clever defence. It is just the correct one, and somebody at Apple worked that out in 2008.

This briefing is provided by IntelFusions for informational and defensive purposes only. It is based on sources assessed to be reliable at the time of writing, and analytic judgments carry the confidence levels indicated. Indicators of compromise are defanged; re-arm them only in controlled environments. IntelFusions is not affiliated with the organizations named and makes no warranty as to completeness or accuracy.

Read the full analysis on IntelFusions