CVE-2025-8747 Overview
CVE-2025-8747 is a safe mode bypass vulnerability in the Model.load_model method of Keras versions 3.0.0 through 3.10.0. An attacker can achieve arbitrary code execution by convincing a user to load a specially crafted .keras model archive. The flaw defeats the safe_mode=True protection that Keras added to block unsafe deserialization from Lambda layers and similar constructs. Because model files circulate freely through public hubs, forums, and shared research artifacts, the vulnerability creates a supply-chain risk for machine learning pipelines. The issue is tracked under CWE-502: Deserialization of Untrusted Data.
Critical Impact
Loading a malicious .keras archive results in arbitrary code execution in the context of the user or service running the model, even when safe_mode is enabled.
Affected Products
- Keras 3.0.0 through 3.10.0 (inclusive)
- Python environments using keras.models.load_model on untrusted .keras archives
- Downstream frameworks and MLOps pipelines that ingest third-party Keras model files
Discovery Timeline
- 2025-08-11 - CVE-2025-8747 published to the National Vulnerability Database (NVD)
- 2026-06-17 - Last updated in the NVD database
- Vendor fix - Tracked in Keras GitHub Pull Request #21429
- Public analysis - Documented in the JFrog Blog Post on Keras Vulnerability
Technical Details for CVE-2025-8747
Vulnerability Analysis
Keras stores models in a .keras archive that bundles a config.json describing the model graph and layer configuration. During Model.load_model, Keras reconstructs layers from that configuration. Keras 3 introduced safe_mode=True as the default to block deserialization of Lambda layers and other constructs that can execute arbitrary Python. CVE-2025-8747 shows that the safe mode filter can be bypassed through layer configurations that reference callable objects the loader still resolves and invokes. The impact is arbitrary code execution in the loading process, matching the classification under CWE-502. Attack success requires user interaction: the victim must load the attacker-supplied archive.
Root Cause
The root cause is incomplete validation of deserialized layer configurations. Safe mode filters known dangerous layer types but does not fully constrain the set of Python callables that configuration entries can resolve during model reconstruction. An attacker who controls the config.json inside a .keras archive can direct the loader to instantiate or invoke code paths that were intended to be blocked. See Keras Pull Request #21429 for the code-level fix.
Attack Vector
Exploitation follows a supply-chain pattern. An attacker publishes a crafted .keras file on a model hub, in a repository, or as an email attachment. A data scientist or automated pipeline calls keras.models.load_model("attacker.keras"). During deserialization, the malicious configuration triggers arbitrary code execution under the identity of the loading process. The technical details of the bypass primitive are described in the JFrog Blog Post on Keras Vulnerability.
Detection Methods for CVE-2025-8747
Indicators of Compromise
- .keras archives obtained from untrusted or unverified sources loaded by data science workstations or training jobs
- Python processes spawning shells, network clients, or package installers shortly after calls to keras.models.load_model
- Unexpected outbound connections from Jupyter kernels, MLOps runners, or model-serving containers
- Modifications to ~/.ssh, cron entries, or startup scripts following model load operations
Detection Strategies
- Inspect the config.json inside .keras archives for references to callables outside the vetted Keras layer set before loading
- Alert on child processes of Python interpreters that historically only ran model training or inference workloads
- Track file integrity of model directories and flag .keras files introduced from external sources
- Use software composition analysis to enumerate Keras versions across development, training, and inference environments
Monitoring Recommendations
- Log all invocations of keras.models.load_model with the source path and hash of the loaded archive
- Monitor MLOps runners and notebook servers for anomalous process trees, especially interpreters spawning sh, bash, curl, or wget
- Correlate model-loading events with network egress from GPU nodes and model registries
How to Mitigate CVE-2025-8747
Immediate Actions Required
- Upgrade Keras to a version later than 3.10.0 that contains the fix from PR #21429
- Inventory .keras files in shared storage, model registries, and notebooks; quarantine any of unknown provenance
- Restrict model-loading workloads to isolated, non-privileged service accounts with limited network egress
- Require cryptographic signatures or checksum verification for internal model artifacts
Patch Information
The fix is merged in Keras Pull Request #21429. Upgrade beyond Keras 3.10.0 across all environments that call load_model, including training clusters, inference services, notebooks, and CI pipelines. Rebuild container images that pin an affected Keras version.
Workarounds
- Do not load .keras archives from untrusted sources, even with safe_mode=True, until the patched version is deployed
- Execute model loading inside sandboxed containers with read-only filesystems and no outbound network access
- Prefer alternate serialization formats such as saved weights loaded into code-defined architectures, which avoid configuration-driven callable resolution
- Enforce allow-lists on model registries so only signed, reviewed artifacts can be pulled by production systems
# Upgrade Keras to a fixed release across environments
pip install --upgrade "keras>3.10.0"
# Verify installed version
python -c "import keras; print(keras.__version__)"
# Inspect a .keras archive before loading (archives are zip files)
unzip -l suspicious_model.keras
unzip -p suspicious_model.keras config.json | jq .
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

