ZIP cloaking shows why scanners and extractors need the same parser

A ZIP file can carry two different names for the same entry. The local file header sits next to the compressed data. The central directory sits at the end of the archive and normally provides the index used when a program opens a specific file. If two parsers disagree about which name is authoritative, a security scanner and an extractor can inspect different objects.

The Rust zip crate handled that disagreement in different ways before version 9.0.5. ZipArchive, the random-access reader, treats the central directory file header as authoritative and ignores the local name. ZipStreamReader extracts sequentially and relies on the local file header. An attacker can therefore present a benign central-directory name to one component and have another component write a different path.

The advisory calls this dual-header inconsistency "Zip cloaking." The risk is strongest in pipelines where approval and extraction happen in separate products or services. An antivirus scanner, mail gateway, or upload filter may approve report.pdf after reading the central directory. A backend worker using the streaming reader may then extract cron_worker.sh, overwrite an existing file, or place content outside the intended application directory.

The result is not a generic ZIP path-traversal issue. The attacker abuses interpretation conflict between two valid parsers. The downstream impact can include file overwrite, denial of service, and code execution when the written path is later executed or loaded. The advisory rates the issue critical and the affected range is zip <= 9.0.4.

Defensive design

Upgrade the Rust crate to 9.0.5 or later. Then rescan systems that accept untrusted archives. A version query alone does not show whether a service extracts user uploads or passes them to another process.

Use one parser for validation and extraction, or compare both header views before any write. Reject archives when the local and central names differ, when an entry appears more than once, or when normalization changes the target path. Canonicalize paths against a fixed extraction root and reject absolute paths, parent traversal, and symlink targets.

Run extraction in a disposable directory with a non-privileged identity and a strict file-size and entry-count budget. Do not let the extraction worker inherit write access to application code, startup scripts, or shared configuration. Scan the extracted files as well as the original archive, because a gateway that approved the central-directory view may not have seen the bytes that reached disk.

Add regression fixtures with a benign name in the central directory and a different name in the local header. Test the exact libraries and wrappers used in production, including language runtimes that vendor a ZIP implementation. The important control is agreement between the component that decides and the component that writes.

References