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
ef4acb5199
commit
5940b8fe39
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#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:
|
Plik .keras to archiwum ZIP zawierające co najmniej:
|
||||||
- metadata.json – ogólne informacje (np. wersja Keras)
|
- metadata.json – informacje ogólne (np. wersja Keras)
|
||||||
- config.json – architektura modelu (główna powierzchnia ataku)
|
- config.json – architekturę modelu (główna powierzchnia ataku)
|
||||||
- model.weights.h5 – wagi w HDF5
|
- 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:
|
Przykładowy fragment dla obiektu warstwy Dense:
|
||||||
```json
|
```json
|
||||||
@ -32,21 +32,21 @@ Przykładowy fragment dla obiektu warstwy Dense:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
Deserializacja wykonuje:
|
Deserializacja wykonuje:
|
||||||
- Import modułów i rozwiązywanie symboli z kluczy module/class_name
|
- Import modułów i rozwiązywanie symboli na podstawie kluczy module/class_name
|
||||||
- wywołanie from_config(...) lub konstruktora z kontrolowanymi przez atakującego kwargs
|
- Wywołanie from_config(...) lub konstruktora z kwargs kontrolowanymi przez atakującego
|
||||||
- Rekursję w zagnieżdżonych obiektach (aktywacje, inicjalizatory, ograniczenia itp.)
|
- 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:
|
Historycznie umożliwiało to atakującemu tworzącemu config.json kontrolę nad trzema prymitywami:
|
||||||
- Kontrola nad tym, jakie moduły są importowane
|
- Kontrolę nad tym, które moduły są importowane
|
||||||
- Kontrola nad tym, które klasy/funkcje są rozwiązywane
|
- Kontrolę nad tym, które klasy/funkcje są rozwiązywane
|
||||||
- Kontrola nad kwargs przekazywanymi do konstruktorów/from_config
|
- 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:
|
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
|
```json
|
||||||
{
|
{
|
||||||
"module": "keras.layers",
|
"module": "keras.layers",
|
||||||
@ -61,16 +61,16 @@ Pomysł na exploit (uproszczony ładunek w config.json):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
Mitigacja:
|
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:
|
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
|
## CVE-2025-1550 – Dowolny import modułu w Keras ≤ 3.8
|
||||||
|
|
||||||
Przyczyna:
|
Przyczyna:
|
||||||
- _retrieve_class_or_fn używał nieograniczonego importlib.import_module() z ciągami modułów kontrolowanymi przez atakującego z config.json.
|
- _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 umieszczonego przez atakującego na sys.path). Kod uruchamia się w czasie importu, a następnie następuje konstrukcja obiektu z kwargs atakującego.
|
- 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:
|
Pomysł na exploit:
|
||||||
```json
|
```json
|
||||||
@ -80,16 +80,16 @@ Pomysł na exploit:
|
|||||||
"config": {"arg": "val"}
|
"config": {"arg": "val"}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Poprawki bezpieczeństwa (Keras ≥ 3.9):
|
Ulepszenia bezpieczeństwa (Keras ≥ 3.9):
|
||||||
- Lista dozwolonych modułów: importy ograniczone do oficjalnych modułów ekosystemu: keras, keras_hub, keras_cv, keras_nlp
|
- Module allowlist: 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
|
- Domyślny safe mode: safe_mode=True blokuje ładowanie niebezpiecznych Lambda serialized-function
|
||||||
- Podstawowe sprawdzanie typów: zdeserializowane obiekty muszą odpowiadać oczekiwanym typom
|
- 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
|
```json
|
||||||
{
|
{
|
||||||
"module": "keras.layers",
|
"module": "keras.layers",
|
||||||
@ -105,19 +105,19 @@ Gadżet przez Lambda, który odnosi się do dozwolonej funkcji (nie zserializowa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Important limitation:
|
Ważne ograniczenie:
|
||||||
- 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.
|
- 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:
|
Potencjalne skutki dopuszczonych gadżetów:
|
||||||
- Dowolne pobieranie/zapisywanie (sadzenie ścieżek, zanieczyszczanie konfiguracji)
|
- Dowolne pobieranie/zapisywanie (path planting, config poisoning)
|
||||||
- Wywołania sieciowe/efekty podobne do SSRF w zależności od środowiska
|
- Network callbacks/SSRF-like effects depending on environment
|
||||||
- Łą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
|
- 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
|
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
|
```python
|
||||||
import importlib, inspect, pkgutil
|
import importlib, inspect, pkgutil
|
||||||
|
|
||||||
@ -160,9 +160,9 @@ candidates.append(text)
|
|||||||
|
|
||||||
print("\n".join(sorted(candidates)[:200]))
|
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
|
```python
|
||||||
from keras import layers
|
from keras import layers
|
||||||
|
|
||||||
@ -178,30 +178,77 @@ cfg = {
|
|||||||
|
|
||||||
layer = layers.deserialize(cfg, safe_mode=True) # Observe behavior
|
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:
|
Keras występuje w wielu repozytoriach/epokach z różnymi zabezpieczeniami i formatami:
|
||||||
- Wbudowany Keras w TensorFlow: tensorflow/python/keras (legacy, planowane do usunięcia)
|
- TensorFlow built-in Keras: tensorflow/python/keras (legacy, slated for deletion)
|
||||||
- tf-keras: utrzymywany osobno
|
- tf-keras: maintained separately
|
||||||
- Multi-backend Keras 3 (oficjalny): wprowadzono natywny .keras
|
- 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ł.
|
- Traktuj pliki modeli jako niezaufane dane wejściowe. Ładuj modele tylko ze zaufanych źródeł.
|
||||||
- Utrzymuj Keras w najnowszej wersji; używaj Keras ≥ 3.9, aby skorzystać z list dozwolonych i sprawdzania typów.
|
- Utrzymuj Keras zaktualizowany; używaj Keras ≥ 3.9, aby skorzystać z allowlisting i kontroli typów.
|
||||||
- Nie ustawiaj safe_mode=False podczas ładowania modeli, chyba że w pełni ufasz plikowi.
|
- Nie ustawiaj safe_mode=False przy ładowaniu 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.
|
- Rozważ uruchamianie deserializacji w sandboxie z minimalnymi uprawnieniami, bez dostępu do sieci wychodzącej 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.
|
- 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)
|
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.
|
||||||
- [Keras PR #20751 – Dodano kontrole do serializacji](https://github.com/keras-team/keras/pull/20751)
|
|
||||||
|
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-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)
|
- [CVE-2025-1550 – Keras arbitrary module import (≤ 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)
|
- [huntr report – arbitrary 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)
|
- [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}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user