YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Keras .keras duplicate model.weights.h5 β€” PoC

Summary

A .keras file is a ZIP archive. When it contains two entries both named model.weights.h5, Python's zipfile.ZipFile.read() resolves to the last entry. keras.saving.load_model() therefore loads trained weights from the later entry. safe_mode=True does not prevent this. ModelScan 0.8.8 reports 0 issues.

Technical distinction

This issue affects the weight archive member, model.weights.h5, rather than model architecture metadata. The crafted archive contains a single unchanged config.json; only the weight file member is duplicated. The model architecture remains the same, but the trained parameters selected by the loader change.

Architecture/config duplicate This report
Duplicate member config.json model.weights.h5
Attack target model architecture / config trained weights (parameters)
Manipulation architecture/config behavior direct parameter replacement
config.json in this PoC (not applicable) single and unchanged
Required mitigation reject duplicate config metadata reject duplicate weight archive members

Requirements

  • Python 3.9+
  • KERAS_BACKEND=torch (no TensorFlow required)
pip install -r requirements.txt

Reproduce

KERAS_BACKEND=torch python make_duplicate_weights_h5.py
KERAS_BACKEND=torch python check_duplicate_weights_h5.py

The scripts confirm that Keras loads the second model.weights.h5 entry and produces different outputs from the baseline model.

Artifacts

File Description
benign_weights_single.keras Baseline model (single model.weights.h5, benign weights)
dup_model_weights_h5.keras Crafted file: first entry benign, second entry malicious weights
make_duplicate_weights_h5.py Generates both files
check_duplicate_weights_h5.py Verifies loader behavior and inference differential

Impact

A reviewer or tool that inspects only one model.weights.h5 archive member would see benign trained weights:

kernel = [[1.0], [1.0]]   bias = [0.0]   β†’ output = x0 + x1

Loading the file with keras.saving.load_model() β€” including safe_mode=True β€” produces different output, because Keras resolves to the last duplicate entry:

kernel = [[-1.0], [-1.0]]   bias = [5.0]   β†’ output = -(x0+x1) + 5

Observed inference differential:

Input Benign output Crafted model output
[1.0, 0.0] 1.0 4.0
[0.0, 1.0] 1.0 4.0
[1.0, 1.0] 2.0 3.0
Downloads last month
18
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support