ModelScan Bypass PoC β Keras Native Format (Nested Lambda)
Summary
This repository demonstrates a bypass of ProtectAI's modelscan scanner using a nested Lambda layer in Keras native (.keras) format.
Vulnerability
- Scanner: modelscan v0.7.6 / v0.8.8 (latest)
- Format: Keras Native (.keras) β premium format on Huntr MFV ($4000)
- Technique: Lambda layer nested inside TimeDistributed wrapper
- Bug type: Insufficient parsing depth β scanner only checks top-level layers
- Scan result: 0 issues found
- CWE: CWE-1285 (Improper Validation of Specified Index, Position, or Offset in Input)
Impact
An attacker can upload a malicious .keras model file where a Lambda layer containing arbitrary Python code is hidden inside a wrapper layer (TimeDistributed, Bidirectional, etc.). modelscan reports "No issues found" because it only inspects config.layers[].class_name at the top level, never recursing into nested layer configs.
When a victim loads with keras.models.load_model('model.keras', safe_mode=False), the serialized Python function in the nested Lambda executes arbitrary code.
Root Cause
In modelscan/scanners/keras/scan.py, _get_keras_operator_names():
for layer in model_config_data.get("config", {}).get("layers", {}):
if layer.get("class_name", {}) == "Lambda": # Only top-level!
This only checks the immediate class_name of each layer. Wrapper layers like TimeDistributed store their inner layer under config.layer, which is never inspected.
Reproduction
# Scan β reports 0 issues
modelscan scan -p model.keras
# Load triggers RCE
python3 -c "import tensorflow as tf; m = tf.keras.models.load_model('model.keras', safe_mode=False); import numpy; m.predict(numpy.zeros((1,5,10)))"
Affected Wrapper Layers
Any Keras wrapper that stores an inner layer in config:
TimeDistributedBidirectional- Nested
Sequentialmodels - Custom wrapper layers
Fix
The scanner should recursively traverse all nested layer configs, not just top-level layers.
- Downloads last month
- -