Every CVE Search/ Get API key

CVE-2026-115906

zip: AES Entry HMAC Authentication Bypass on Premature EOF

6.5 MediumFix available

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. 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
Required

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
None

Exploit likelihood

0.15%Unchanged since Oct 27, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.5

zip: AES Entry HMAC Authentication Bypass on Premature EOF

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
CVE-2026-115906
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-354, CWE-1284
Updated
2026-11-02
EUVDEUVD-2026-109983 6.5

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
EUVD-2026-109983
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-8hxf-qr8p-62x4 6.5

zip: AES Entry HMAC Authentication Bypass on Premature EOF

Summary

An AES-encrypted (AE-x) entry's authentication code is verified only when the reader's byte counter lands exactly on zero. If the bounded entry reader reaches the archive's actual EOF before the attacker-controlled, header-declared compressed_size, the counter remains positive, the HMAC is never read or compared, and the truncated, unauthenticated plaintext is returned as a clean end of stream. A zero-payload entry also returns before authentication. An attacker who does not know the password can modify an archive so chosen or modified plaintext is accepted with no error.

Details

src/aes.rs:246-292:

fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
    if self.data_remaining == 0 { // @audit insufficient check
        return Ok(0);
    }

    // get the number of bytes to read, compare as u64 to make sure we can read more than
    // 2^32 bytes even on 32 bit systems.
    let bytes_to_read = self.data_remaining.min(buf.len() as u64) as usize;
    let read = self.reader.read(&mut buf[0..bytes_to_read])?;
    self.data_remaining -= read as u64;

    // Update the hmac with the encrypted data
    self.hmac.update(&buf[0..read]);

    // decrypt the data
    self.cipher.crypt_in_place(&mut buf[0..read]);

    // if there is no data left to read, check the integrity of the data
    if self.data_remaining == 0 {
        debug_assert!(
            !self.finalized,
            "Tried to use an already finalized HMAC. This is a bug!"
        );
        if self.finalized {
            return Err(Error::new(
                ErrorKind::InvalidData,
                "Tried to use an already finalized HMAC",
            ));
        }
        self.finalized = true;

        // Zip uses HMAC-Sha1-80, which only uses the first half of the hash
        // see https://www.winzip.com/win/en/aes_info.html#auth-faq
        let mut read_auth_code = [0; AUTH_CODE_LENGTH];
        self.reader.read_exact(&mut read_auth_code)?;
        let computed_auth_code = &self.hmac.finalize_reset().into_bytes()[0..AUTH_CODE_LENGTH];

        // use constant time comparison to mitigate timing attacks
        if !constant_time_eq(computed_auth_code, &read_auth_code) {
            return Err(Error::new(
                ErrorKind::InvalidData,
                "Invalid authentication code, this could be due to an invalid password or errors in the data",
            ));
        }
    }

    Ok(read)
}

data_remaining starts at compressed_size - (2 + 10 + salt_length) (src/aes.rs:144-145), where compressed_size comes straight from the attacker-controlled central directory. Nothing cross-checks the declared data span against the archive's structural boundaries: ZipFileData::find_content() is just reader.take(self.compressed_size) (src/types.rs:214-223), and make_crypto_reader() passes the declared value into AesReader::new() (src/read/readers.rs).

When the entry's bounded reader reaches true EOF before the declared size, the inner reader returns Ok(0), the subtraction takes away nothing, and the finalisation block — the only place the 10-byte HMAC-SHA1-80 tag is read and compared — never runs. If bytes such as the central directory follow the entry, they are consumed as payload until the Take limit is reached; the HMAC then usually fails. The bypass therefore requires true EOF at the premature end (as in the PoC) or the zero-payload case. The early return at the top of read() means an entry whose declared size leaves zero payload bytes is never authenticated either, even though its tag is…

Record
GHSA-8hxf-qr8p-62x4
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-354, CWE-1284
Updated
2026-11-02
NVD 6.5

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
CVE-2026-115906
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-354, CWE-1284
Updated
2026-11-02
OSV 6.5

zip: AES Entry HMAC Authentication Bypass on Premature EOF

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
CVE-2026-115906
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-1284, CWE-354
Updated
2026-11-02
OSVDEBIAN-CVE-2026-115906 6.5

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
DEBIAN-CVE-2026-115906
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Updated
2026-11-02
Show 6 moreShow less
OSVGHSA-8hxf-qr8p-62x4 6.5

zip: AES Entry HMAC Authentication Bypass on Premature EOF

Summary

An AES-encrypted (AE-x) entry's authentication code is verified only when the reader's byte counter lands exactly on zero. If the bounded entry reader reaches the archive's actual EOF before the attacker-controlled, header-declared compressed_size, the counter remains positive, the HMAC is never read or compared, and the truncated, unauthenticated plaintext is returned as a clean end of stream. A zero-payload entry also returns before authentication. An attacker who does not know the password can modify an archive so chosen or modified plaintext is accepted with no error.

Details

src/aes.rs:246-292:

fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
    if self.data_remaining == 0 { // @audit insufficient check
        return Ok(0);
    }

    // get the number of bytes to read, compare as u64 to make sure we can read more than
    // 2^32 bytes even on 32 bit systems.
    let bytes_to_read = self.data_remaining.min(buf.len() as u64) as usize;
    let read = self.reader.read(&mut buf[0..bytes_to_read])?;
    self.data_remaining -= read as u64;

    // Update the hmac with the encrypted data
    self.hmac.update(&buf[0..read]);

    // decrypt the data
    self.cipher.crypt_in_place(&mut buf[0..read]);

    // if there is no data left to read, check the integrity of the data
    if self.data_remaining == 0 {
        debug_assert!(
            !self.finalized,
            "Tried to use an already finalized HMAC. This is a bug!"
        );
        if self.finalized {
            return Err(Error::new(
                ErrorKind::InvalidData,
                "Tried to use an already finalized HMAC",
            ));
        }
        self.finalized = true;

        // Zip uses HMAC-Sha1-80, which only uses the first half of the hash
        // see https://www.winzip.com/win/en/aes_info.html#auth-faq
        let mut read_auth_code = [0; AUTH_CODE_LENGTH];
        self.reader.read_exact(&mut read_auth_code)?;
        let computed_auth_code = &self.hmac.finalize_reset().into_bytes()[0..AUTH_CODE_LENGTH];

        // use constant time comparison to mitigate timing attacks
        if !constant_time_eq(computed_auth_code, &read_auth_code) {
            return Err(Error::new(
                ErrorKind::InvalidData,
                "Invalid authentication code, this could be due to an invalid password or errors in the data",
            ));
        }
    }

    Ok(read)
}

data_remaining starts at compressed_size - (2 + 10 + salt_length) (src/aes.rs:144-145), where compressed_size comes straight from the attacker-controlled central directory. Nothing cross-checks the declared data span against the archive's structural boundaries: ZipFileData::find_content() is just reader.take(self.compressed_size) (src/types.rs:214-223), and make_crypto_reader() passes the declared value into AesReader::new() (src/read/readers.rs).

When the entry's bounded reader reaches true EOF before the declared size, the inner reader returns Ok(0), the subtraction takes away nothing, and the finalisation block — the only place the 10-byte HMAC-SHA1-80 tag is read and compared — never runs. If bytes such as the central directory follow the entry, they are consumed as payload until the Take limit is reached; the HMAC then usually fails. The bypass therefore requires true EOF at the premature end (as in the PoC) or the zero-payload case. The early return at the top of read() means an entry whose declared size leaves zero payload bytes is never authenticated either, even though its tag is…

Record
GHSA-8hxf-qr8p-62x4
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-1284, CWE-354
Updated
2026-11-02
OSVUBUNTU-CVE-2026-115906 6.5

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
UBUNTU-CVE-2026-115906
CVSS 3.1
6.5 MediumCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Updated
2026-11-02
OSVAZL-112208 No score

CVE-2026-115906 affecting package rust 1.75.0-31

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
AZL-112208
Updated
2026-11-05
OSVAZL-112217 No score

CVE-2026-115906 affecting package rust 1.98.1-1

zip is a zip library for Rust that supports reading and writing simple ZIP files. From 0.6.0 until 9.0.0, the AES entry reader in src/aes.rs verifies its HMAC only when the attacker-controlled data counter reaches zero. If the underlying bounded reader reaches the real end of the archive while data_remaining is still positive, or if an encrypted entry has no payload, the reader can return a clean end of stream without reading and comparing the authentication tag. Applications using by_index_decrypt, by_name_decrypt, by_path_decrypt, or password-enabled by_index_with_options can therefore accept modified plaintext without an authentication error. This issue is fixed in version 9.0.0.

Record
AZL-112217
Updated
2026-11-05
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

Affected packages
PackageAffectedFixed
zip
crates.io
0.6.0 up to 9.0.0
9.0.0
rust
Azure Linux:3
through 1.75.0-31 None known
rust
Azure Linux:3
1.85.0 to 1.98.1-1 None known
rust-zip
Debian: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 less
0.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-zip
Debian: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-zip
Debian: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-keylime
SUSE: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-keylime
SUSE: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-zip
Ubuntu:20.04:LTS
All versions, plus listed versions
0.5.0-10.5.3-10.5.3-2
None known
rust-zip
Ubuntu:22.04:LTS
All versions, plus listed versions
0.5.8-20.5.13-1
None known
rust-zip
Ubuntu:24.04:LTS
All versions, plus listed versions
0.6.6-10.6.6-2
None known
rust-zip
Ubuntu:26.10
All versions, plus listed versions
5.1.1-35.1.1+dfsg-1
None known
rust-zip
Ubuntu:26.04:LTS
All versions, plus listed versions
2.5.0-15.1.1-25.1.1-3
None known

History

History
RecordedChangedSources
affected
factsOSV
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-115906 is a medium vulnerability (CVSS 6.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.15%, the 4th percentile. Fixed in zip 9.0.0.

References

Show 17 moreShow less
This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open