Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-12638

CVE-2025-12638: Keras Path Traversal Vulnerability

CVE-2025-12638 is a path traversal flaw in Keras 3.11.3 affecting keras.utils.get_file() during tar extraction. Attackers can write files outside cache directories. This article covers technical details, impact, and mitigation.

Published:

CVE-2025-12638 Overview

CVE-2025-12638 is a path traversal vulnerability [CWE-22] in Keras version 3.11.3. The flaw resides in the keras.utils.get_file() function during tar archive extraction. Keras calls Python's tarfile.extractall() without the security-critical filter='data' parameter. A pre-extraction path filter (filter_safe_paths()) exists, but a PATH_MAX symlink resolution bug bypasses this check at extraction time. Attackers can craft a malicious tar archive that writes arbitrary files outside the intended cache directory. Successful exploitation can lead to arbitrary file writes and potential code execution on systems that process attacker-supplied archives via get_file().

Critical Impact

Arbitrary file writes outside the cache directory enable code execution and full system compromise when Keras processes an attacker-controlled tar archive.

Affected Products

  • Keras 3.11.3
  • Machine learning pipelines using keras.utils.get_file() for tar extraction
  • Downstream applications bundling the affected Keras release

Discovery Timeline

  • 2025-11-28 - CVE-2025-12638 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2025-12638

Vulnerability Analysis

The vulnerability affects the archive extraction path within keras.utils.get_file(). This helper downloads and extracts data files used by Keras workflows. When the downloaded resource is a tar archive, Keras delegates extraction to Python's tarfile.extractall().

Starting in Python 3.12, tarfile.extractall() accepts a filter argument to reject unsafe members. Keras invokes the function without filter='data', so member sanitization defaults to permissive legacy behavior. Keras attempts to compensate by running filter_safe_paths() before extraction to reject entries containing traversal sequences or absolute paths.

The compensating check does not survive extraction. A PATH_MAX symlink resolution bug triggers when member paths exceed the system limit, causing symlink resolution to fail silently. The failure bypasses the pre-check and permits writes outside the target directory. Attackers combine long path components with symlinks to redirect file creation to attacker-chosen locations.

Root Cause

The root cause is missing use of the filter='data' tarfile extraction filter combined with reliance on a pre-extraction sanitizer that cannot enforce constraints once PATH_MAX limits break symlink resolution. The check-then-use gap [CWE-22] enables path traversal during member extraction.

Attack Vector

Exploitation requires a user or automated pipeline to fetch and extract an attacker-controlled tar archive via get_file(). The archive contains crafted members that use long paths and symlinks to escape the cache directory. Successful extraction writes files to arbitrary locations, including Python site-packages, cron directories, or startup scripts, enabling code execution under the running user's privileges.

No verified public exploit code is available. Refer to the Huntr Security Bounty Listing for additional technical details.

Detection Methods for CVE-2025-12638

Indicators of Compromise

  • Unexpected files written outside the Keras cache directory (typically ~/.keras/)
  • Tar archives containing members with paths approaching or exceeding PATH_MAX (4096 bytes on Linux)
  • Archive entries mixing symlinks with unusually long target paths
  • Modified Python site-packages, shell profile files, or scheduled task directories following a Keras data download

Detection Strategies

  • Inspect tar archives fetched by ML pipelines for symlink members and oversized path names before extraction
  • Monitor file creation events originating from Python processes running Keras workflows
  • Audit installed Keras versions across build, training, and inference environments to identify version 3.11.3
  • Compare extraction output paths against the expected cache directory and alert on deviations

Monitoring Recommendations

  • Enable filesystem auditing on directories used by ML training hosts, including user home directories and system paths
  • Log all outbound downloads initiated by keras.utils.get_file() and correlate URLs against allowlists
  • Track process ancestry to detect Python interpreters writing to unexpected directories after archive extraction

How to Mitigate CVE-2025-12638

Immediate Actions Required

  • Upgrade Keras to a fixed release once published by the maintainers
  • Restrict keras.utils.get_file() inputs to trusted, signed archives only
  • Run ML training and data preparation jobs under least-privileged, non-root accounts
  • Isolate archive extraction inside containers or sandboxes with read-only host mounts

Patch Information

Consult the Huntr Security Bounty Listing for the fix status. The recommended remediation is to invoke tarfile.extractall() with filter='data' and to reject archive members whose resolved paths fall outside the intended extraction root.

Workarounds

  • Pre-validate tar archives with an out-of-process tool that enforces path length and symlink policy
  • Replace get_file() tar extraction with a hardened wrapper that calls extractall(filter='data')
  • Block automated retrieval of untrusted archives at the network egress layer
  • Extract archives inside ephemeral containers with no write access to sensitive host paths
bash
# Configuration example - hardened extraction wrapper
python - <<'PY'
import tarfile, os, sys

archive, dest = sys.argv[1], os.path.abspath(sys.argv[2])
with tarfile.open(archive) as tf:
    for m in tf.getmembers():
        target = os.path.abspath(os.path.join(dest, m.name))
        if not target.startswith(dest + os.sep):
            raise SystemExit(f"blocked traversal: {m.name}")
        if m.issym() or m.islnk():
            raise SystemExit(f"blocked link member: {m.name}")
    tf.extractall(dest, filter='data')
PY

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.