Downloads · 30 days
9
9% of all-time downloads
th3-j0k3r/modelscan-keras-nested-lambda-bypass
modelscan-keras-nested-lambda-bypass is a machine learning model from th3-j0k3r. Use it for the machine learning task on the model card, and read the license before you ship it in a product. It is set up for keras.
Proof-of-concept for a Huntr Model File Format report.
Downloads · 30 days
9
9% of all-time downloads
All-time downloads
97
Public
Repo size
—
Likes
0
Public
Click a slice to open those files.
.keras18.2 KB · 68%
From the Hugging Face model README
.keras via Nested Lambda LayerProof-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.
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 bothREADME.md — this fileUse 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.
# 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
modelscan/scanners/keras/scan.py (_get_keras_operator_names):
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.
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.
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.