29.3.3. Az ONNX formátum sebezhetőségei

2025.10.06.
AI Biztonság Blog

A lényeg röviden: Az ONNX (Open Neural Network Exchange) célja, hogy a modellek keretrendszerek közötti hordozhatóságát biztosítsa. Ez a „közös nyelv” azonban egyben közös támadási felületet is teremt. A sebezhetőség nem magában a statikus modellfájlban, hanem a modellt futtató környezet (runtime) operátor-értelmezési képességeiben rejlik.

Kapcsolati űrlap

AI Biztonság kérdésed van? Itt elérsz minket:

Miért más az ONNX, mint a pickle vagy a SavedModel?

Gondolj az ONNX-ra úgy, mint a mesterséges intelligencia modellek „PDF-jére”. Míg a PyTorch .pt vagy a TensorFlow SavedModel formátumai egy adott ökoszisztémához kötődnek (mint a .docx a Wordhöz), az ONNX egy univerzális, keretrendszer-független leíró nyelv. Nem Python-objektumokat szerializál, mint a pickle, és nem is egy komplex könyvtárstruktúra, mint a SavedModel.

Az ONNX lényegében egy számítási gráfot definiál, amely operátorokból (pl. Conv, Relu, MatMul) és a köztük lévő adatfolyamot leíró tenzorokból áll. Ez a deklaratív jelleg elméletben biztonságosabbnak tűnik, hiszen „csak” matematikai műveleteket ír le. A probléma a részletekben, pontosabban az operátorok implementációjában rejlik.

PyTorch / TF Modell Konverzió .onnx Fájl (Mérgezett operátorral) Betöltés ONNX Runtime Végrehajtás Rendszer kompromittálása A támadás az ONNX futtatókörnyezetben (Runtime) realizálódik, nem a statikus fájlban.

Hol a sebezhetőség? Az egyéni operátorok (Custom Ops)

Az ONNX specifikáció lehetővé teszi az úgynevezett „custom operator”-ok használatát. Ezek olyan, a standard készletben nem szereplő műveletek, amelyeket a fejlesztők saját maguk implementálhatnak a specifikus igényeikhez. A támadási vektor itt nyílik meg: mi történik, ha egy támadó egy olyan egyéni operátort hoz létre, amely nem matematikai műveletet, hanem rendszerszintű parancsot hajt végre?

Az ONNX futtatókörnyezetek (mint az onnxruntime) feladata, hogy a gráfban definiált operátorokat leképezzék a tényleges, végrehajtható kódra. Ha a futtatókörnyezet nincs megfelelően „homokozóba” (sandbox) zárva, vagy ha naivan megbízik a modellben definiált operátorokban, egy rosszindulatúan preparált .onnx fájl tetszőleges kódfuttatást (Arbitrary Code Execution – ACE) érhet el a gyanútlan áldozat rendszerén.

Példa: A parancsfuttató operátor

Tételezzük fel, hogy egy támadó létrehoz egy látszólag ártalmatlan modellt. A modell belsejébe azonban elrejt egy egyéni operátort, mondjuk ShellExecute néven. A támadás két lépésből áll:

1. A rosszindulatú ONNX modell létrehozása

A támadó egy Python szkript segítségével legenerálja a mérgezett modellt. Nem kell hozzá komplex neurális háló, elég egyetlen csomópont.

# támadó oldali kód
import onnx
from onnx import helper, TensorProto

# Létrehozunk egy csomópontot (node), amely egy nem létező,
# de beszédes nevű egyéni operátort használ.
# A 'command' attribútumban adjuk meg a futtatandó parancsot.
node = helper.make_node(
 'ShellExecute', # Operátor neve, amit a runtime majd keres
 inputs=['input'], # Bemeneti tenzor neve
 outputs=['output'], # Kimeneti tenzor neve
 domain='com.redteam.ops', # Egyéni domain a névütközések elkerülésére
 command='touch /tmp/pwned_by_onnx' # A VÉGREHAJTANDÓ PAYLOAD
)

# Definiáljuk a gráf be- és kimeneteit
graph = helper.make_graph(
 [node],
 'malicious-graph',
 [helper.make_tensor_value_info('input', TensorProto.FLOAT, [1])],
 [helper.make_tensor_value_info('output', TensorProto.FLOAT, [1])]
)

# Létrehozzuk és elmentjük a modellt
model = helper.make_model(graph)
onnx.save(model, 'malicious_model.onnx')

2. Az áldozat végrehajtja a modellt

Az áldozat letölti a malicious_model.onnx fájlt egy megbízhatatlan forrásból (pl. egy kevésbé ismert modell-zoológiai kertből). A kódja, amivel futtatni próbálja, teljesen standard és ártalmatlannak tűnik. A sebezhetőség kihasználásához a támadónak rá kell vennie az áldozatot, hogy egy olyan módosított vagy sebezhető onnxruntime-ot használjon, amely implementálja a ShellExecute operátort. Ez társadalmi mérnökösködéssel vagy egy kompromittált futtatókörnyezet terjesztésével érhető el.

# áldozat oldali kód (feltételezve egy kompromittált runtime-ot)
import onnxruntime as ort
import numpy as np

try:
 # A gyanútlan felhasználó betölti a modellt.
 # A háttérben a runtime felismeri a 'ShellExecute' op-t és előkészíti.
 session = ort.InferenceSession('malicious_model.onnx')

 # Bemeneti adat létrehozása
 input_data = np.array([1.0], dtype=np.float32)

 # A modell futtatása. EZ A PONT, AHOL A PAYLOAD VÉGREHAJTÓDIK!
 # A 'session.run' hívás aktiválja a ShellExecute operátort.
 session.run(None, {'input': input_data})

 print("A modell lefutott... vagy mégsem?")
 # Ekkor a /tmp/pwned_by_onnx fájl már létrejött a rendszeren.
except Exception as e:
 print(f"Hiba történt: {e}")
 # Valószínűleg a runtime panaszkodni fog, ha nem ismeri az operátort.
 # Egy kifinomultabb támadás ezt is elrejtheti.

Védekezési stratégiák

Az ONNX-alapú támadások elleni védekezés a bizalom és az ellenőrzés elvén alapul. Mivel a formátum maga csak egy leírás, a felelősség a futtatókörnyezetre és a felhasználóra hárul.

Védekezési Technika Leírás Nehézségi Szint
Operátorok engedélyezőlistája (Whitelisting) A futtatókörnyezet konfigurálása, hogy csak egy előre meghatározott, biztonságosnak ítélt standard operátorlistát hajtson végre. Minden egyéni vagy ismeretlen operátort elutasít. Közepes
Statikus analízis A .onnx fájl betöltés előtti vizsgálata. Egy egyszerű szkript végigiterálhat a modell csomópontjain, és megvizsgálhatja az operátorok neveit és domainjeit, gyanús elemeket keresve. Könnyű
Futtatás korlátozott környezetben (Sandboxing) Az inferencia folyamat futtatása egy elszigetelt konténerben (pl. Docker) vagy virtuális gépen, minimális jogosultságokkal. Még ha a kód végre is hajtódik, a károkozás mértéke korlátozott. Közepes
Forrásellenőrzés és aláírások Kizárólag megbízható, ellenőrzött forrásokból (pl. hivatalos modell hubok) származó modellek használata. Digitális aláírások ellenőrzése, ha elérhetőek. Változó (a forrástól függ)

Összefoglalva, az ONNX interoperabilitása kétélű fegyver. Red Teamerként ezt a felületet kell vizsgálnunk: nem a modellt magát, hanem azt, ahogyan a célrendszer értelmezi és életre kelti azt. A védekező oldalon pedig a „soha ne bízz a bemenetben” elvét kell kiterjeszteni a gépi tanulási modellekre is.