<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>VulnFeed.org: zip</title><link>https://vulnfeed.org/</link><description>Every disclosure, as it lands. Critical-only feed available.</description><language>en</language><atom:link href="https://vulnfeed.org/feed/product/zip.xml" rel="self" type="application/rss+xml"/><item><title>CVE-2026-115907: zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size</title><link>https://vulnfeed.org/cve/CVE-2026-115907/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-115907/</guid><pubDate>Thu, 29 Oct 2026 16:40:19 +0000</pubDate><description>### Summary A malicious 149-byte ZIP archive can cause a denial of service in applications that enable `legacy-zip` and use `ZipArchive::extract()`. An attacker-controlled ZIP64 uncompressed size triggers a `capacity overflow` panic in the Shrink decoder in optimized builds, potentially terminating the affected process. ### Details When reading a Shrink-compressed entry, `ShrinkDecoder::read()` buffers the compressed input and reserves output capacity using the entry&apos;s declared uncompressed size: ```rust if !self.stream_read { self.stream_read = true; let mut compressed_bytes = Vec::new(); self.compressed_reader.read_to_end(&amp;mut compressed_bytes)?; self.stream.reserve(self.uncompressed_size as usize); hwunshrink( &amp;compressed_bytes, self.uncompressed_size as usize, &amp;mut self.stream, )?; } ``` In version 8.6.0, this is located in [`src/legacy/shrink.rs`](https://github.com/zip-rs/zip2/blob/771dfc534d2614158af5497ea3dff4d4208d7db1/src/legacy/shrink.rs#L341-L352). The vulnerable legacy codecs are compiled when the `legacy-zip` Cargo feature is enabled. `self.uncompressed_size` is populated from ZIP metadata. For ZIP64 entries, an attacker can set it to a 64-bit value independently of the archive&apos;s actual compressed payload size. The PoC declares an uncompressed size of `u64::MAX`, while supplying only one byte of compressed data. The reserve occurs before `hwunshrink()` can validate or decode the compressed stream. Consequently, the impossible capacity causes a deterministic `capacity overflow` panic instead of returning a normal archive or I/O error. The behavior also occurs in optimized release builds and is reachable through `ZipArchive::extract()`. The same unbounded allocation pattern is present in the legacy Reduce and Implode decoder implementations. This PoC specifically demonstrates the Shrink path. ### PoC Create a new Cargo project with the following layout: ```text zip2-legacy-poc/ |-- Cargo.toml `-- src/ `-- main.rs ``` Use this `Cargo.toml`: ```toml [pack…</description></item><item><title>CVE-2026-115908: zip: Arbitrary file write outside the extraction directory, because ZipArchive::extract validates symlink targets lexically.</title><link>https://vulnfeed.org/cve/CVE-2026-115908/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-115908/</guid><pubDate>Thu, 29 Oct 2026 16:40:12 +0000</pubDate><description>### Summary - **Verified version:** zip `9.0.0-pre3` - **Affected versions:** `7.2.0` and later - **Precondition:** The attacker knows (or can guess) the absolute path of the extraction destination The validation applied to symlinks is purely lexical. As a result, an attacker can write arbitrary files outside the extraction directory through a symlink. Exploitation requires the attacker to know (or guess) the absolute path of the destination, but this is realistic in practice: CI workspaces, `/opt/app`, a fixed Downloads path, and so on. &gt; **Impact extends further on Windows (verified on real hardware for both the separate-drive and UNC-share cases)**: &gt; Because `simplified_components` discards the `Prefix` component, paths like `D:\&lt;same tail as dest&gt;` or a UNC path `\\attacker\share\&lt;same tail&gt;` pass the `starts_with` check. &gt; As a result, `outpath.push` gets replaced onto a different drive/remote share, so **a single symlink is enough — no intermediate symlink chain is required**. &gt; That said, creating a symlink on Windows requires `SeCreateSymbolicLinkPrivilege` (administrator rights or Developer Mode enabled). &gt; **Real-hardware verification (Windows 10 VM (19045.2604), `tests/poc_win.rs`)**: &gt; With the extraction target set to a temporary directory under `C:`, both of the following paths returned `Ok(())` from `extract()` and successfully wrote outside the extraction target. &gt; In both cases `extract()` returned no error (`Ok(())`), and it was confirmed that the contents of `pwned.txt` matched the attacker-specified value. ### Relationship to CVE-2025-29787 This is **not a duplicate** of the existing [CVE-2025-29787](https://github.com/zip-rs/zip2/security/advisories/GHSA-94vh-gphv-8pm8) (2025-03); rather, **the protection introduced for that CVE was subsequently lost**. #### Timeline (git history, UTC) | commit | Date | Description | | ---------- | ---------- | ----------------------------------------------------------------------------------------------------…</description></item><item><title>CVE-2026-115909: zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction</title><link>https://vulnfeed.org/cve/CVE-2026-115909/</link><guid isPermaLink="true">https://vulnfeed.org/cve/CVE-2026-115909/</guid><pubDate>Thu, 29 Oct 2026 16:40:01 +0000</pubDate><description>### Summary The symbolic-link branch of `ZipArchive::extract_internal()` allocates a buffer using an attacker-declared size before reading any entry data. A 112-byte archive terminates the extracting process with `SIGABRT` through this infallible allocation. ### Details ```rust #[cfg(any(unix, windows))] if file.is_symlink() { let mut target = Vec::with_capacity(file.size() as usize); // @audit unbounded memory allocation file.read_to_end(&amp;mut target)?; drop(file); make_symlink(&amp;outpath, &amp;target, &amp;self.shared.files)?; continue; } ``` [`src/read/mod.rs#L399-L405`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/mod.rs#L399-L405) (reviewed commit `6f09b306`); the same code is at [`src/read.rs#L417-L418`](https://github.com/zip-rs/zip2/blob/v8.6.0/src/read.rs#L417-L418) in released v8.6.0. Both inputs are attacker-controlled: - [`ZipFile::size()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L243-L246) returns `uncompressed_size` verbatim from the central directory record. A ZIP64 extended-information extra field widens it to a full `u64` ([`src/read/mod.rs#L619-L625`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/mod.rs#L619-L625)). It is never cross-checked against the compressed size, the remaining archive length, or any platform-appropriate bound for a symlink target. - [`ZipFile::is_symlink()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L256-L260) is decided entirely by the central directory&apos;s external-attributes field, via the `S_IFLNK` bit. **Memory allocation failure → uncatchable process abort.** The reservation is sized from the declared `uncompressed_size` before a single byte of the entry is read. `Vec::with_capacity()` is infallible: on allocator refusal it calls `handle_alloc_error()`, which aborts the process. This is not a Rust panic — it is not caught by `catch_unwind()`…</description></item></channel></rss>
