CVE-2026-115906
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.
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
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 Medium
CVSS: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 Medium
CVSS: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
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 Medium
CVSS: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 Medium
CVSS: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 Medium
CVSS: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 Medium
CVSS: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
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 Medium
CVSS: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 Medium
CVSS: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
| Package | Affected | Fixed |
|---|---|---|
zipcrates.io |
0.6.0 up to 9.0.0 | 9.0.0 |
rustAzure Linux:3 |
through 1.75.0-31 | None known |
rustAzure Linux:3 |
1.85.0 to 1.98.1-1 | None known |
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:20.04:LTS |
All versions, plus listed versions 0.5.0-10.5.3-10.5.3-2 |
None known |
rust-zipUbuntu:22.04:LTS |
All versions, plus listed versions 0.5.8-20.5.13-1 |
None known |
rust-zipUbuntu:24.04:LTS |
All versions, plus listed versions 0.6.6-10.6.6-2 |
None known |
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 |
History
| Recorded | Changed | Sources |
|---|---|---|
| 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-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
github.com/zip-rs/zip2/security/advisories/GHSA-8hxf-qr8p-62x4
github.com/zip-rs/zip2/commit/ec265db0b0602f8ffb9479327397bddefb7e2216
github.com/zip-rs/zip2/releases/tag/v9.0.0
nvd.nist.gov/vuln/detail/CVE-2026-115906
github.com/advisories/GHSA-8hxf-qr8p-62x4
github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115906.json
Show 17 moreShow less
security-tracker.debian.org/tracker/CVE-2026-115906
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-115906
cve.org/CVERecord?id=CVE-2026-115906
github.com/zip-rs/zip2/security/advisories/ghsa-8hxf-qr8p-62x4
bugs.debian.org/cgi-bin/bugreport.cgi?bug=1152227
security-tracker.debian.org/tracker/cve-2026-115906
github.com/cveproject/cvelistv5/tree/main/cves/2026/115xxx/cve-2026-115906.json
nvd.nist.gov/vuln/detail/cve-2026-115906
Left out here: the rest of 2 source descriptions. The JSON record has them.