Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 3 min read

CVE-2026-115906: CVE-2026-115906: AES Entry HMAC Authentication Bypass in Rust zip Crate

CVE-2026-115906: AES Entry HMAC Authentication Bypass in Rust zip Crate Vulnerability ID: CVE-2026-115906 CVSS Score: 6.5 Published: 2026-10-29 CVE-2026-115906 is a cryptographic authentication bypass vulnerability

CVE-2026-115906: AES Entry HMAC Authentication Bypass in Rust zip Crate

Vulnerability ID: CVE-2026-115906
CVSS Score: 6.5
Published: 2026-10-29

CVE-2026-115906 is a cryptographic authentication bypass vulnerability in the Rust zip library (zip-rs/zip2) affecting versions from 0.6.0 prior to 9.0.0. In the WinZip AE-x AES decryption reader implementation within src/aes.rs, the HMAC-SHA1-80 authentication tag is only validated when the internal byte counter (data_remaining) decrements to exactly zero. Because this counter is initialized from the unauthenticated compressed_size header field, an attacker can craft a ZIP archive where the entry stream hits premature physical end-of-file (EOF) before data_remaining reaches zero, causing the reader to return Ok(0) and skip HMAC verification entirely.

TL;DR

The Rust zip crate prior to version 9.0.0 fails to verify the HMAC-SHA1-80 authentication tag on AES-encrypted ZIP entries if the underlying stream reaches premature EOF or if the payload length is zero. Combined with AES-CTR ciphertext malleability and omitted CRC-32 checks in AE-2 archives, an attacker without knowledge of the password can modify or forge encrypted archive entries to deliver arbitrary plaintext without detection.

⚠️ Exploit Status: POC

Technical Details

  • Vulnerability Type: Cryptographic Authentication Bypass (CWE-354 / CWE-1284)
  • Affected Component: WinZip AE-x AES Decryption Reader (src/aes.rs in Rust zip crate)
  • Attack Vector: Network / Malicious ZIP Archive (AV:N/AC:L/PR:N/UI:R)
  • CVSS v3.1 Score: 6.5 Medium
  • EPSS Score: 0.151% (4.17th Percentile)
  • Security Impact: High Integrity Compromise (Arbitrary Ciphertext Forgery / Truncation)
  • Exploit Status: Proof-of-Concept Available in Upstream Tests
  • Patched Version: zip 9.0.0

Affected Systems

  • Rust applications using the zip crate (zip-rs/zip2) versions >= 0.6.0, < 9.0.0 to decrypt AES-encrypted ZIP entries
  • zip: >= 0.6.0, < 9.0.0 (Fixed in: 9.0.0)

Code Analysis

Commit: ec265db

Fix AES entry HMAC authentication bypass on premature EOF and zero-length entries, and validate compressed_size against AES metadata length.

File: src/aes.rs
@@ -140,15 +140,18 @@ impl<R: Read> AesReader<R> {
-    pub const fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> AesReader<R> {
-        let data_length = compressed_size
-            - (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode.salt_length()) as u64;
-
-        Self {
+    pub fn new(reader: R, aes_mode: AesMode, compressed_size: u64) -> ZipResult<AesReader<R>> {
+        let metadata_len = (PWD_VERIFY_LENGTH + AUTH_CODE_LENGTH + aes_mode.salt_length()) as u64;
+        // we accept 0 size data
+        if compressed_size < metadata_len {
+            return Err(invalid!("Compressed size of AES data is invalid"));
+        }
+        let data_length = compressed_size - metadata_len;
+        Ok(Self {
             reader,
             aes_mode,
             data_length,
-        }
+        })
     }
@@ -245,6 +250,10 @@ impl<R: Read> Read for AesReaderValid<R> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         if self.data_remaining == 0 {
+            // handle case were payload is empty
+            if self.data_length == 0 {
+                self.verify_hmac()?;
+            }
             return Ok(0);
         }
@@ -262,38 +271,48 @@ impl<R: Read> Read for AesReaderValid<R> {
         if self.data_remaining == 0 {
+            self.verify_hmac()?;
+        } else if read == 0 && self.data_remaining > 0 {
+            return Err(Error::new(
+                ErrorKind::UnexpectedEof,
+                "Read 0 bytes of AES data but still contains remaining data",
+            ));
         }

Exploit Details

  • GitHub: Official regression test and Proof of Concept (hmac_is_bypassed_when_the_entry_ends_early and hmac_is_never_checked_for_a_zero_length_entry) included in the remediation commit.

Mitigation Strategies

  • Upgrade the Rust zip crate dependency to version 9.0.0 or later to ensure premature EOF and zero-length payload conditions are properly checked during AES decryption.
  • Implement pre-extraction structural validation on untrusted ZIP archives to verify that no entry's local header or declared compressed_size extends into the Central Directory or past the End of Central Directory (EOCD) record.
  • Replace or supplement password-based ZIP AE-x encryption with detached digital signatures (e.g., Ed25519) or modern authenticated encryption containers (e.g., age) when archive integrity is security-critical.

Remediation Steps:

  1. Locate all Rust projects using the zip crate by scanning Cargo.lock files with 'cargo audit' or 'cargo tree -i zip'.
  2. Update Cargo.toml to specify zip = ">= 9.0.0" and run 'cargo update -p zip'.
  3. Run test suites to accommodate any major version API adjustments introduced between prior releases and v9.0.0.
  4. Rebuild and redeploy affected binaries and services.

References

Read the full report for CVE-2026-115906 on our website for more details including interactive diagrams and full exploit analysis.

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.