Translated ['', 'src/generic-methodologies-and-resources/python/keras-mo

This commit is contained in:
Translator 2025-09-29 12:09:36 +00:00
parent ef4acb5199
commit 5940b8fe39

View File

@ -2,16 +2,16 @@
{{#include ../../banners/hacktricks-training.md}}
Ta strona podsumowuje praktyczne techniki eksploatacji przeciwko potokowi deserializacji modelu Keras, wyjaśnia wewnętrzne działanie formatu .keras oraz powierzchnię ataku, a także dostarcza narzędzi badawczych do znajdowania Wrażliwości Plików Modelu (MFV) i gadżetów po naprawie.
Ta strona podsumowuje praktyczne techniki eksploatacji przeciwko procesowi deserializacji modeli Keras, wyjaśnia wewnętrzną strukturę natywnego formatu .keras i powierzchnię ataku oraz dostarcza zestaw narzędzi dla badaczy do znajdowania Model File Vulnerabilities (MFVs) i post-fix gadgets.
## Wewnętrzne działanie formatu modelu .keras
## .keras model format internals
Plik .keras to archiwum ZIP zawierające przynajmniej:
- metadata.json ogólne informacje (np. wersja Keras)
- config.json architektura modelu (główna powierzchnia ataku)
Plik .keras to archiwum ZIP zawierające co najmniej:
- metadata.json informacje ogólne (np. wersja Keras)
- config.json architekturę modelu (główna powierzchnia ataku)
- model.weights.h5 wagi w HDF5
Plik config.json napędza rekurencyjną deserializację: Keras importuje moduły, rozwiązuje klasy/funkcje i rekonstruuje warstwy/obiekty z kontrolowanych przez atakującego słowników.
Plik config.json steruje rekursywną deserializacją: Keras importuje moduły, rozwiązuje klasy/funkcje i rekonstruuje warstwy/obiekty ze słowników kontrolowanych przez atakującego.
Przykładowy fragment dla obiektu warstwy Dense:
```json
@ -32,21 +32,21 @@ Przykładowy fragment dla obiektu warstwy Dense:
}
```
Deserializacja wykonuje:
- Import modułów i rozwiązywanie symboli z kluczy module/class_name
- wywołanie from_config(...) lub konstruktora z kontrolowanymi przez atakującego kwargs
- Rekursję w zagnieżdżonych obiektach (aktywacje, inicjalizatory, ograniczenia itp.)
- Import modułów i rozwiązywanie symboli na podstawie kluczy module/class_name
- Wywołanie from_config(...) lub konstruktora z kwargs kontrolowanymi przez atakującego
- Rekurencja w głąb zagnieżdżonych obiektów (activations, initializers, constraints, etc.)
Historycznie, to ujawniało trzy prymitywy atakującemu tworzącemu config.json:
- Kontrola nad tym, jakie moduły są importowane
- Kontrola nad tym, które klasy/funkcje są rozwiązywane
- Kontrola nad kwargs przekazywanymi do konstruktorów/from_config
Historycznie umożliwiało to atakującemu tworzącemu config.json kontrolę nad trzema prymitywami:
- Kontrolę nad tym, które moduły są importowane
- Kontrolę nad tym, które klasy/funkcje są rozwiązywane
- Kontrolę nad kwargs przekazywanymi do konstruktorów/from_config
## CVE-2024-3660 RCE z bajtkodem warstwy Lambda
## CVE-2024-3660 Lambda-layer bytecode RCE
Przyczyna:
- Lambda.from_config() używało python_utils.func_load(...), które dekoduje base64 i wywołuje marshal.loads() na bajtach atakującego; deserializacja w Pythonie może wykonać kod.
- Lambda.from_config() używało python_utils.func_load(...), które base64-dekoduje i wywołuje marshal.loads() na bajtach dostarczonych przez atakującego; odmarshalowywanie w Pythonie może wykonać kod.
Pomysł na exploit (uproszczony ładunek w config.json):
Pomysł exploita (uproszczony payload w config.json):
```json
{
"module": "keras.layers",
@ -61,16 +61,16 @@ Pomysł na exploit (uproszczony ładunek w config.json):
}
```
Mitigacja:
- Keras domyślnie wymusza safe_mode=True. Zserializowane funkcje Pythona w Lambda są zablokowane, chyba że użytkownik wyraźnie zdecyduje się na safe_mode=False.
- Keras narzuca safe_mode=True domyślnie. Serializowane funkcje Pythona w Lambda są blokowane, chyba że użytkownik wyraźnie zrezygnuje, ustawiając safe_mode=False.
Uwagi:
- Starsze formaty (starsze zapisy HDF5) lub starsze bazy kodu mogą nie wymuszać nowoczesnych kontroli, więc ataki w stylu „downgrade” mogą nadal mieć zastosowanie, gdy ofiary używają starszych loaderów.
- Starsze formaty (wcześniejsze zapisy HDF5) lub starsze bazy kodu mogą nie wymuszać nowoczesnych kontroli, więc ataki w stylu „downgrade” mogą nadal mieć zastosowanie, gdy ofiary używają starszych loaderów.
## CVE-2025-1550 Dowolny import modułu w Keras ≤ 3.8
Przyczyna:
- _retrieve_class_or_fn używał nieograniczonego importlib.import_module() z ciągami modułów kontrolowanymi przez atakującego z config.json.
- Wpływ: Dowolny import dowolnego zainstalowanego modułu (lub modułu umieszczonego przez atakującego na sys.path). Kod uruchamia się w czasie importu, a następnie następuje konstrukcja obiektu z kwargs atakującego.
- _retrieve_class_or_fn używał nieograniczonego importlib.import_module() z ciągami modułów kontrolowanymi przez atakującego pochodzącymi z config.json.
- Wpływ: Dowolny import dowolnego zainstalowanego modułu (lub modułu podstawionego przez atakującego na sys.path). Kod uruchamiany podczas importu zostaje wykonany, a następnie następuje konstrukcja obiektu z przekazanymi przez atakującego kwargs.
Pomysł na exploit:
```json
@ -80,16 +80,16 @@ Pomysł na exploit:
"config": {"arg": "val"}
}
```
Poprawki bezpieczeństwa (Keras ≥ 3.9):
- Lista dozwolonych modułów: importy ograniczone do oficjalnych modułów ekosystemu: keras, keras_hub, keras_cv, keras_nlp
- Domyślny tryb bezpieczny: safe_mode=True blokuje ładowanie niebezpiecznych funkcji zserializowanych Lambda
Ulepszenia bezpieczeństwa (Keras ≥ 3.9):
- Module allowlist: importy ograniczone do oficjalnych modułów ekosystemu: keras, keras_hub, keras_cv, keras_nlp
- Domyślny safe mode: safe_mode=True blokuje ładowanie niebezpiecznych Lambda serialized-function
- Podstawowe sprawdzanie typów: zdeserializowane obiekty muszą odpowiadać oczekiwanym typom
## Powierzchnia gadżetów po poprawce wewnątrz listy dozwolonych
## Powierzchnia post-fix gadget wewnątrz allowlist
Nawet z listą dozwolonych i trybem bezpiecznym, pozostaje szeroka powierzchnia wśród dozwolonych wywołań Keras. Na przykład, keras.utils.get_file może pobierać dowolne adresy URL do lokalizacji wybranych przez użytkownika.
Nawet przy allowlisting i safe mode, wśród dozwolonych Keras callables nadal istnieje szeroka powierzchnia ataku. Na przykład, keras.utils.get_file może pobierać dowolne URLs do lokalizacji wybranych przez użytkownika.
Gadżet przez Lambda, który odnosi się do dozwolonej funkcji (nie zserializowany bajtkod Pythona):
Gadget przez Lambda, który odwołuje się do dozwolonej funkcji (nie zserializowany Python bytecode):
```json
{
"module": "keras.layers",
@ -105,19 +105,19 @@ Gadżet przez Lambda, który odnosi się do dozwolonej funkcji (nie zserializowa
}
}
```
Important limitation:
- Lambda.call() dodaje tensor wejściowy jako pierwszy argument pozycyjny podczas wywoływania docelowego wywołania. Wybrane gadżety muszą tolerować dodatkowy argument pozycyjny (lub akceptować *args/**kwargs). Ogranicza to, które funkcje są wykonalne.
Ważne ograniczenie:
- Lambda.call() prepends the input tensor as the first positional argument when invoking the target callable. Chosen gadgets must tolerate an extra positional arg (or accept *args/**kwargs). This constrains which functions are viable.
Potential impacts of allowlisted gadgets:
- Dowolne pobieranie/zapisywanie (sadzenie ścieżek, zanieczyszczanie konfiguracji)
- Wywołania sieciowe/efekty podobne do SSRF w zależności od środowiska
- Łączenie do wykonania kodu, jeśli zapisane ścieżki są później importowane/wykonywane lub dodawane do PYTHONPATH, lub jeśli istnieje zapisywalna lokalizacja do wykonania przy zapisie
Potencjalne skutki dopuszczonych gadżetów:
- Dowolne pobieranie/zapisywanie (path planting, config poisoning)
- Network callbacks/SSRF-like effects depending on environment
- Możliwość doprowadzenia do wykonania kodu, jeśli zapisane ścieżki są później importowane/uruchamiane lub dodane do PYTHONPATH, albo jeśli istnieje zapisywalna lokalizacja wykonująca kod przy zapisie
## Researcher toolkit
## Zestaw narzędzi badawczych
1) Systematyczne odkrywanie gadżetów w dozwolonych modułach
Enumeruj kandydatów na wywołania w keras, keras_nlp, keras_cv, keras_hub i nadaj priorytet tym z efektami ubocznymi związanymi z plikami/siecią/procesem/środowiskiem.
Wypisz kandydackie callables w ramach keras, keras_nlp, keras_cv, keras_hub i nadaj priorytet tym, które mają skutki uboczne dotyczące plików/sieci/procesów/środowiska.
```python
import importlib, inspect, pkgutil
@ -160,9 +160,9 @@ candidates.append(text)
print("\n".join(sorted(candidates)[:200]))
```
2) Bezpośrednie testowanie deserializacji (nie jest potrzebny archiwum .keras)
2) Bezpośrednie testowanie deserializacji (archiwum .keras nie jest wymagane)
Wprowadź przygotowane słowniki bezpośrednio do deserializatorów Keras, aby poznać akceptowane parametry i obserwować efekty uboczne.
Podawaj przygotowane dicts bezpośrednio do Keras deserializers, aby poznać akceptowane params i obserwować efekty uboczne.
```python
from keras import layers
@ -178,30 +178,77 @@ cfg = {
layer = layers.deserialize(cfg, safe_mode=True) # Observe behavior
```
3) Probing między wersjami i formaty
3) Sondowanie międzywersyjne i formaty
Keras istnieje w wielu bazach kodu/epokach z różnymi zabezpieczeniami i formatami:
- Wbudowany Keras w TensorFlow: tensorflow/python/keras (legacy, planowane do usunięcia)
- tf-keras: utrzymywany osobno
- Multi-backend Keras 3 (oficjalny): wprowadzono natywny .keras
Keras występuje w wielu repozytoriach/epokach z różnymi zabezpieczeniami i formatami:
- TensorFlow built-in Keras: tensorflow/python/keras (legacy, slated for deletion)
- tf-keras: maintained separately
- Multi-backend Keras 3 (official): introduced native .keras
Powtarzaj testy w różnych bazach kodu i formatach (.keras vs legacy HDF5), aby odkryć regresje lub brakujące zabezpieczenia.
Powtarzaj testy w różnych repozytoriach i formatach (.keras vs legacy HDF5), aby wykryć regresje lub brakujące zabezpieczenia.
## Rekomendacje defensywne
## Zalecenia obronne
- Traktuj pliki modeli jako niezaufane dane wejściowe. Ładuj modele tylko z zaufanych źródeł.
- Utrzymuj Keras w najnowszej wersji; używaj Keras ≥ 3.9, aby skorzystać z list dozwolonych i sprawdzania typów.
- Nie ustawiaj safe_mode=False podczas ładowania modeli, chyba że w pełni ufasz plikowi.
- Rozważ uruchomienie deserializacji w piaskownicy, w środowisku o minimalnych uprawnieniach, bez dostępu do sieci i z ograniczonym dostępem do systemu plików.
- Wprowadź listy dozwolone/podpisy dla źródeł modeli i sprawdzania integralności, gdzie to możliwe.
- Traktuj pliki modeli jako niezaufane dane wejściowe. Ładuj modele tylko ze zaufanych źródeł.
- Utrzymuj Keras zaktualizowany; używaj Keras ≥ 3.9, aby skorzystać z allowlisting i kontroli typów.
- Nie ustawiaj safe_mode=False przy ładowaniu modeli, chyba że w pełni ufasz plikowi.
- Rozważ uruchamianie deserializacji w sandboxie z minimalnymi uprawnieniami, bez dostępu do sieci wychodzącej i z ograniczonym dostępem do systemu plików.
- Wymuszaj allowlists/sygnatury dla źródeł modeli i sprawdzanie integralności tam, gdzie to możliwe.
## Odniesienia
## ML pickle import allowlisting for AI/ML models (Fickling)
- [Hunting Vulnerabilities in Keras Model Deserialization (blog huntr)](https://blog.huntr.com/hunting-vulnerabilities-in-keras-model-deserialization)
- [Keras PR #20751 Dodano kontrole do serializacji](https://github.com/keras-team/keras/pull/20751)
Wiele formatów modeli AI/ML (PyTorch .pt/.pth/.ckpt, joblib/scikit-learn, starsze artefakty TensorFlow itp.) osadza dane Python pickle. Atakujący rutynowo nadużywają pickle GLOBAL imports i konstruktorów obiektów, aby osiągnąć RCE lub podmianę modelu podczas ładowania. Skanery oparte na czarnej liście często nie wykrywają nowych lub nieujętych niebezpiecznych importów.
Praktyczna, fail-closed strategia obronna polega na zahaczeniu deserializatora pickle Pythona i zezwoleniu tylko na przeglądany zestaw nieszkodliwych importów związanych z ML podczas unpicklingu. Trail of Bits Fickling implementuje tę politykę i dostarcza wyselekcjonowaną ML import allowlist zbudowaną na podstawie tysięcy publicznych pickli z Hugging Face.
Model bezpieczeństwa dla „bezpiecznych” importów (intuicje wyprowadzone z badań i praktyki): symbole importowane i używane przez pickle muszą jednocześnie:
- Nie wykonywać kodu ani nie powodować jego wykonania (np. brak obiektów reprezentujących kod, uruchamiania poleceń shell, hooków itp.)
- Nie pobierać ani nie ustawiać dowolnych atrybutów lub elementów
- Nie importować ani nie pozyskiwać referencji do innych obiektów Pythona z VM pickla
- Nie uruchamiać żadnych wtórnych deserializatorów (np. marshal, nested pickle), nawet pośrednio
Włącz ochrony Fickling jak najwcześniej podczas uruchamiania procesu, tak aby wszystkie ładowania pickle wykonywane przez frameworki (torch.load, joblib.load, itp.) były sprawdzane:
```python
import fickling
# Sets global hooks on the stdlib pickle module
fickling.hook.activate_safe_ml_environment()
```
Wskazówki operacyjne:
- Możesz tymczasowo wyłączyć/ponownie włączyć hooks tam, gdzie to potrzebne:
```python
fickling.hook.deactivate_safe_ml_environment()
# ... load fully trusted files only ...
fickling.hook.activate_safe_ml_environment()
```
Jeśli zablokowano sprawdzony model, rozszerz allowlist dla swojego środowiska po przejrzeniu symboli:
```python
fickling.hook.activate_safe_ml_environment(also_allow=[
"package.subpackage.safe_symbol",
"another.safe.import",
])
```
- Fickling udostępnia też ogólne runtime guards, jeśli wolisz bardziej granulowaną kontrolę:
- fickling.always_check_safety() aby wymusić sprawdzenia dla wszystkich wywołań pickle.load()
- with fickling.check_safety(): dla wymuszania w określonym zakresie
- fickling.load(path) / fickling.is_likely_safe(path) do jednorazowych kontroli
- W miarę możliwości preferuj formaty modeli inne niż pickle (np. SafeTensors). Jeśli musisz akceptować pickle, uruchamiaj ładowarki z najmniejszymi uprawnieniami, bez egressu sieciowego, i egzekwuj allowlistę.
Ta strategia oparta na allowliście dowodnie blokuje typowe ścieżki exploitów pickle w ML, zachowując przy tym wysoką kompatybilność. W benchmarku ToB, Fickling oznaczył 100% syntetycznych złośliwych plików i dopuścił ~99% czystych plików z czołowych repozytoriów Hugging Face.
## Źródła
- [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)
- [CVE-2024-3660 Keras Lambda deserialization RCE](https://nvd.nist.gov/vuln/detail/CVE-2024-3660)
- [CVE-2025-1550 Keras dowolny import modułu (≤ 3.8)](https://nvd.nist.gov/vuln/detail/CVE-2025-1550)
- [raport huntr dowolny import #1](https://huntr.com/bounties/135d5dcd-f05f-439f-8d8f-b21fdf171f3e)
- [raport huntr dowolny import #2](https://huntr.com/bounties/6fcca09c-8c98-4bc5-b32c-e883ab3e4ae3)
- [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 Ficklings 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}}