VAITP Dataset

← Back to the dataset

CVE-2026-44722

pyzipper exposes plaintext CRC32 checksum, allowing brute-force attacks.

  • CVSS 6.2
  • CWE-480
  • Cryptographic
  • Remote

pyzipper is a replacement for Python's zipfile that can read and write AES encrypted zip files. Prior to 0.4.0, a Python operator precedence bug in pyzipper/zipfile_aes.py caused the AE-2 format to never be automatically selected during encryption, causing encrypted entries to be written in AE-1 format and exposing the plaintext CRC32 checksum in the ZIP header and, for unseekable zip archives, in the datadescripter section, allowing an attacker who possesses the archive to brute-force candidate plaintexts for small or low-entropy files by comparing CRC32 values. This issue is fixed in version 0.4.0.

CVSS base score
6.2
Published
2026-07-17
OWASP
A02 Cryptographic Failures
Orthogonal defect classification
Assignment
Code defect classification
Incorrect Algorithm
Category
Cryptographic
Subcategory
Cryptographic Implementation Error
Accessibility scope
Remote
Impact
Information Disclosure
Affected component
pyzipper
Fixed by upgrading
Yes

Solution

Upgrade pyzipper to version 0.4.0 or later.

Vulnerable code sample

import io
import pyzipper

# This code requires a vulnerable version of pyzipper (e.g., 0.3.0).
# pip install pyzipper==0.3.0

def vulnerable_create_zip_with_lzma(password):
    """
    Demonstrates the vulnerability by creating an AES-encrypted zip file
    using LZMA compression with a vulnerable pyzipper version. The bug
    causes the library to select the AE-1 encryption format instead of the
    intended AE-2, exposing the CRC32 checksum in the header.
    """
    in_memory_zip = io.BytesIO()
    
    # The vulnerability occurs when AES encryption is used with LZMA compression.
    with pyzipper.AESZipFile(in_memory_zip, 'w',
                             compression=pyzipper.ZIP_LZMA,
                             encryption=pyzipper.WZ_AES) as zf:
        
        # Set the password for encryption.
        zf.setpassword(password)
        
        # Add a small, low-entropy file. The exposed CRC32 of this plaintext
        # makes a brute-force attack feasible.
        zf.writestr('file.txt', b'123')
        
    return in_memory_zip.getvalue()

# --- Execution ---
# Create a password-protected zip file using the vulnerable logic.
password = b"secret"
zip_data = vulnerable_create_zip_with_lzma(password)

# To verify the vulnerability, one would inspect the created zip_data.
# The 'extra field' for 'file.txt' would show the AES vendor version as 0x0001 (AE-1)
# instead of the correct 0x0002 (AE-2) for LZMA, and the CRC32 (0x884863d2 for '123')
# would be present in plaintext in the central directory header for the file.
# For example, a hex editor would reveal the CRC value in the file's header.
# (Verification step is not shown in this code).

Patched code sample

# Constants based on zipfile and pyzipper specifications
ZIP_DEFLATED = 8
ZIP_AES = 99
AES_MODE_AE1 = 1
AES_MODE_AE2 = 2

def _select_aes_mode_fixed(zinfo):
    """
    This function represents the fixed logic for CVE-2021-44722 in pyzipper > 0.4.0.

    The vulnerability was that the AE-2 encryption format was never selected
    because the code used an incorrect condition to check if a data descriptor
    was present. It incorrectly checked bit 0 (`0x1`) instead of bit 3 (`0x8`).
    This forced the use of AE-1, which leaves the CRC32 checksum unencrypted.

    The fix involves changing the condition to correctly check for the data
    descriptor flag (`0x8`), ensuring AE-2 is used when appropriate.

    Args:
        zinfo: A ZipInfo-like object with `flag_bits` and `compress_type` attributes.
    """
    # The vulnerable line of code was:
    # if zinfo.flag_bits & 0x1 and zinfo.compress_type == ZIP_DEFLATED:

    # The fixed code correctly checks for bit 3 (0x8), which is the flag
    # indicating a data descriptor follows the file data. This is the
    # condition that requires using AE-2 encryption.
    if (zinfo.flag_bits & 0x8) and zinfo.compress_type in (ZIP_DEFLATED, ZIP_AES):
        # This branch is now correctly entered for unseekable archives,
        # selecting AE-2 to encrypt the CRC32 value.
        zinfo.aes_encryption_mode = AES_MODE_AE2
    else:
        # AE-1 is used for standard cases where the CRC32 is in the header.
        zinfo.aes_encryption_mode = AES_MODE_AE1

    return zinfo.aes_encryption_mode

Cite this entry

@misc{vaitp:cve202644722,
  title        = {{pyzipper exposes plaintext CRC32 checksum, allowing brute-force attacks.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-44722},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-44722/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::