# Keras has a Local File Disclosure via HDF5 External Storage During Keras Weight Loading

Malicious HDF5 weights with external storage/link datasets make Keras load_weights read arbitrary host files into tensors, leaking secrets via inference or re-saved artifacts.

- Payout: Research
- Severity: high
- Program: Keras
- Found by (vendor credit): Giuseppe Massaro (https://github.com/N3mes1s)
- Found by: N3mes1s
- Disclosed: 2026-02-18
- CVE: CVE-2026-1669
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-3m4q-jmj6-r34q
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-3m4q-jmj6-r34q
- Tags: area:ai domain:ai domain:oss impact:data-leak kind:research platform:ghsa sev:high tech:python technique:chained technique:source-review vuln:info-disclosure vuln:path-traversal

**Target:** Keras HDF5 weight loader (load_weights / load_model)
**Root cause:** Keras honors HDF5 external storage and ExternalLink references when materializing weight tensors, so dataset paths controlled by the file author are opened and read from the host filesystem. Safe mode only guards object deserialization, not weight I/O.
**Steps:**
1. Build a small Dense model and save weights to `weights_demo.h5` via `model.save_weights()`.
2. Open the file with h5py in `r+` mode, locate the dataset ending in `bias:0`, delete it.
3. Recreate it with `parent.create_dataset(name, shape=(units,), dtype='float32', external=[(host_file.as_posix(), 0, nbytes)])` pointing at e.g. `/etc/hostname`.
4. Call `model.load_weights('weights_demo.h5')`; HDF5 follows the external reference and reads the host file.
5. Run `model.predict(tf.zeros((1,1)))` and view the output floats as bytes to recover the file contents; re-saving persists the secret.
**How found:** Reporter recognized that Keras safe mode only covers object/lambda deserialization, then tested whether HDF5 external storage and ExternalLink features are still honored during weight loading; confirmed with strace showing openat/read of /etc/hosts, /etc/passwd, /etc/hostname.
**Exploitation:** Attacker ships a crafted `.weights.h5` or `.keras` archive (published model, OSS contribution, bug report attachment). Victim loads it; HDF5 external storage/ExternalLink pulls bytes from arbitrary readable host files into tensors. Attacker recovers data via inference outputs on controlled inputs or by obtaining the re-saved artifact pushed to a registry/repo.
**Escalation:** Chaining external storage through a helper HDF5 (indirect link) and using re-save to persist secrets into shared artifacts, enabling exfiltration without continued access.
**Insight:** When a framework advertises a 'safe mode', check which code path it actually guards: weight/array I/O formats (HDF5 external storage, ExternalLink, numpy memmap) can bypass deserialization-only protections and read host files.

## Proof of concept (copied exactly from the original report)

**External storage dataset creation call**

```
parent.create_dataset(
            dset_name,
            shape=(units,),
            dtype="float32",
            external=[(host_file.as_posix(), 0, nbytes)],
        )
```

