YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ModelScan Keras Scanner Bypass PoC
Vulnerability
ModelScan v0.8.8's Keras scanner (KerasLambdaDetectScan) only checks for Lambda layers at the top level of model_config["config"]["layers"]. Lambda layers nested inside sub-models (Functional, Sequential) or wrapper layers (TimeDistributed, Bidirectional) are not detected.
Bypass Techniques
1. Nested Model (nested_lambda_bypass.keras)
Lambda layer hidden inside a nested Functional sub-model. ModelScan sees the outer layer as class_name: "Functional" and skips it.
2. Wrapper Layer (wrapper_lambda_bypass.keras)
Lambda layer wrapped in a TimeDistributed wrapper. ModelScan sees class_name: "TimeDistributed" and skips it.
Root Cause
In modelscan/scanners/keras/scan.py lines 119-131:
lambda_layers = [
layer.get("config", {}).get("function", {})
for layer in model_config_data.get("config", {}).get("layers", {})
if layer.get("class_name", {}) == "Lambda"
]
This only iterates the top-level layers list. It does NOT recursively check:
- Nested model configs (layers inside
config.layers[n].config.layers) - Wrapper layer configs (the
layerfield in TimeDistributed/Bidirectional)
Impact
When a user loads a .keras file that passes ModelScan with 0 issues:
import keras
model = keras.models.load_model("nested_lambda_bypass.keras", safe_mode=False)
# Arbitrary code executes via marshal.loads in Lambda.from_config()
The Lambda function is deserialized via:
Functional.from_config()βprocess_layer()β recursivedeserialize_keras_object()Lambda.from_config()βpython_utils.func_load()func_load()βcodecs.decode(base64)βmarshal.loads()βtypes.FunctionType()β ACE
Reproduction
pip install modelscan[keras]
modelscan -p nested_lambda_bypass.keras
# Output: No issues found (0 Lambda layers detected)
# But the file contains a malicious Lambda layer with marshal.loads code execution
- Downloads last month
- 4