Ein KI-Modell ist ein neuronales Netz: eine Architektur, die festlegt, wie gerechnet wird, und Millionen bis Milliarden gelernter Zahlenwerte, die Gewichte. Die Datei, in der es geliefert wird, kann aber mehr enthalten: Anweisungen, die beim Laden ausgeführt werden. Wer eine fremde .pt-Datei ungeprüft lädt, startet im schlimmsten Fall ein Programm, das jemand anderes geschrieben hat.
Auf Hugging Face liegen mehr als zwei Millionen öffentliche Modelle, und die meisten Teams laden sie mit demselben Vertrauen wie ein npm-Paket. Dieser Post ist der Deep Dive zu Modelldateien: welche Formate was ausführen können, was Scanner übersehen und wie du ein fremdes Modell prüfst, bevor es in Produktion geht. Den Überblick über die ganze Lieferkette, von Daten bis zu Agenten-Werkzeugen, gibt es in Die neue Gefahr der AI Supply Chain.
Wann eine Modelldatei zum Programm wird
Viele Modellformate basieren auf Pythons Pickle. Pickle speichert nicht nur Werte, sondern eine Folge von Anweisungen, mit denen ein Objekt wieder aufgebaut wird. Über die Methode __reduce__ darf ein Objekt dabei eine beliebige Funktion mit beliebigen Argumenten benennen, und der Loader ruft sie auf. Die Python-Dokumentation sagt es ohne Umschweife: Das Modul ist nicht sicher, entpackt werden sollen nur Daten, denen man vertraut.
import os, pickle
class Model:
def __reduce__(self):
# beliebiger Befehl
return (os.system, ("id",))
# sieht aus wie ein Modell
pickle.dump(Model(), open("model.pkl", "wb"))
# führt den Befehl aus
pickle.load(open("model.pkl", "rb"))
Das Format entscheidet also, was beim Laden passieren kann:
| Format | Was beim Laden passiert | Was bleibt |
|---|---|---|
| .pkl, .pt, .bin Pickle |
Beliebiger Code kann laufen, sobald die Datei geladen wird. | hoch Nur laden, was du wie eigenen Code behandeln würdest. |
| .safetensors | JSON-Header plus rohe Tensor-Daten, nichts Ausführbares. Ein Audit von Trail of Bits fand 2023 keine kritische Lücke, die zu Code-Ausführung führt. | niedrig Die Gewichte selbst können trotzdem einen Backdoor tragen. |
| .h5, .keras Keras |
Lambda-Layer// Lambda-LayerBaustein in Keras-Modellen, der beliebigen Python-Code als Rechenschritt einbettet. Gespeichert wird er als Bytecode, der schon beim Laden des Modells ausgeführt werden kann.BeispielEin Layer mit dem harmlosen Namen scale_inputs, der nebenbei eine Verbindung nach außen öffnet. bringen Python-Code in die Architektur (CVE-2024-3660). |
mittel Layer prüfen, Keras aktuell halten. |
| .gguf// GGUFDateiformat für Sprachmodelle, die lokal laufen, zum Beispiel mit llama.cpp. | Kein Pickle. Aber die Datei enthält ein Chat-Template: In llama-cpp-python führte das zu Code-Ausführung (CVE-2024-34359), und Pillar Security zeigte 2025 Templates, die zur Laufzeit Anweisungen einschleusen. | mittel Template lesen, Runtime patchen. |
Zwei Dinge gehen dabei gern unter. Erstens: weights_only=True ist seit PyTorch 2.6 der Default und schränkt den Unpickler ein. Eine Garantie ist das nicht: CVE-2025-32434 erlaubte bis Version 2.5.1 Code-Ausführung trotz gesetztem Flag. Zweitens: Safetensors schützt vor Code beim Laden, nicht vor dem, was in den Gewichten gelernt wurde.
Code in der Architektur: Keras Lambda-Layer
Pickle ist nicht der einzige Weg, Code in ein Modell zu bringen. Keras kennt Lambda-Layer: Bausteine, die einen beliebigen Python-Ausdruck als Rechenschritt ins Modell einhängen. Gespeichert werden sie laut Keras-Dokumentation als Python-Bytecode, und die nennt das selbst „potentially unsafe“.
Die Folge beschreibt CERT/CC nüchtern: Wer ein fremdes Keras-Modell lädt, führt unter Umständen fremden Code aus. Keras 3 weigert sich deshalb standardmäßig, Lambda-Layer zu laden, deren Funktion als Bytecode gespeichert ist (safe_mode). Zwei Lücken bleiben. Bei Keras 2 und bei Keras 3 vor Version 3.12 greift diese Prüfung für das klassische .h5 nicht, so beschreibt es auch CERT/CC. Erst seit 3.12 vom Oktober 2025 reicht Keras safe_mode an den H5-Loader durch. Und der Schutz selbst wurde 2025 umgangen: CVE-2025-1550 erlaubte Code-Ausführung trotz eingeschaltetem safe_mode.
Ein Formatwechsel hilft hier nicht: Safetensors speichert nur Gewichte, die Architektur steckt weiter in der Keras-Datei oder im Code. Was hilft, ist ein Blick auf die Layer-Liste, bevor das Modell geladen wird. ModelScan sucht in .h5-Dateien gezielt nach Lambda-Layern (Installation mit den Extras modelscan[tensorflow,h5py]). Jeder Lambda- oder Custom-Layer in einem Modell, das du nicht selbst gebaut hast, ist ein Grund nachzufragen.
Was mit Modelldateien schon passiert ist
JFrog fand im Februar 2024 rund 100 Modelle auf Hugging Face, die beim Laden Code ausführten, darunter eines mit Reverse Shell// Reverse ShellDer befallene Rechner baut selbst eine Verbindung zum Angreifer auf und gibt ihm eine Kommandozeile.. JFrog merkt selbst an, dass die meisten dieser Payloads// PayloadDer Teil eines Angriffs, der den eigentlichen Schaden anrichtet. Hier: der eingeschleuste Code. Tests von Forschern oder Bug-Bounty-Jägern sein dürften. Das Prinzip funktioniert trotzdem.
Drei Entwicklungen seit 2025 machen die Lage schwieriger.
1 // Scanner lassen sich austricksen
Hugging Face scannt hochgeladene Dateien, sagt aber selbst, das sei nicht hundertprozentig sicher. ReversingLabs beschrieb im Februar 2025 zwei Modelle (nullifAI), die mit 7z statt ZIP gepackt waren und deren Pickle-Stream direkt nach dem Payload abbricht. Als unsicher markiert wurden sie nicht, der Payload lief trotzdem. Im selben Jahr meldeten Sonatype vier und JFrog drei weitere Umgehungen des Scanners picklescan. Ein grüner Haken auf der Plattform ist ein Hinweis, kein Befund.
2 // Namen sind kein Herkunftsnachweis
PoisonGPT zeigte 2023 das Prinzip: ein gezielt verfälschtes Modell, hochgeladen unter EleuterAI statt EleutherAI. Unit 42 beschrieb 2025 Model Namespace Reuse: Wird ein Autor-Account gelöscht oder übertragen, kann jemand den alten Namen neu registrieren, und jede Pipeline, die Modelle nur per Name lädt, zieht das fremde Modell. Betroffen waren laut Unit 42 auch Googles Vertex AI und Microsofts Azure AI Foundry. Im Mai 2026 erreichte ein Repository, das ein OpenAI-Release imitierte, Platz 1 der Hugging-Face-Trends, bevor es gesperrt wurde. Die Downloadzahlen waren laut HiddenLayer so gut wie sicher künstlich aufgebläht.
3 // Adapter und Konverter
Ein Modell kommt selten allein. LoRA-Adapter// LoRA-AdapterKleine Zusatzdatei, die ein Basismodell für eine Aufgabe anpasst, ohne es neu zu trainieren.BeispielEin Adapter, der ein allgemeines Sprachmodell auf Support-Antworten trimmt. werden wie Modelle geteilt, und Forscher zeigten, dass sich ein Backdoor einmal in einen Adapter trainieren und dann ohne weiteres Training mit anderen Adaptern zusammenführen lässt. Die OWASP Top 10 für LLM-Anwendungen führen verwundbare LoRA-Adapter deshalb als eigenes Supply-Chain-Risiko. HiddenLayer zeigte 2024, dass sich der Safetensors-Konvertierungsdienst von Hugging Face kapern ließ, um Pull Requests im Namen des offiziellen Bots zu stellen. Alles, was durch eine fremde Pipeline gelaufen ist, ist ein neues Artefakt.
Risiko, Maßnahme, Tool
Die Kurzfassung als Tabelle. Alle genannten Tools sind Open Source.
| Risiko | Maßnahme | Tools |
|---|---|---|
| Code in der Modelldatei | Safetensors als einziges erlaubtes Format. Pickle nie ungeprüft laden. PyTorch ab 2.6. | safetensors, Fickling, ModelAudit, ModelScan, picklescan |
| Logik in der Architektur | Layer vor dem Laden auflisten. Keras safe_mode an lassen und Keras aktuell halten. |
ModelScan, h5py |
| Ausgetauschte Datei | Hash und Revision pinnen, Signatur prüfen. | sha256sum, revision= mit vollem Commit-Hash, OpenSSF Model Signing |
| Gefälschte oder übernommene Repos | Allowlist für Organisationen. Produktion lädt nur aus der eigenen Registry, nie per Name vom Hub. | Interne Model Registry, Commit-Pinning |
| Payload feuert beim Laden | Erstes Laden nur in einer Sandbox ohne Netzwerk, mit Telemetrie. | Container ohne Egress, sys.addaudithook |
| Backdoor in den Gewichten, vergiftete Daten | Eigene Evals mit Fällen, deren richtige Antwort du kennst. Red Teaming vor dem Rollout. | garak, promptfoo |
| Adapter und konvertierte Modelle | Wie ein neues Modell behandeln: dieselben Gates, eigener Scan, eigene Freigabe. | Dieselben Scanner wie oben |
Pakete, Anbieter und Agenten-Werkzeuge bringen eigene Risiken mit. Die stehen im Überblick: Die neue Gefahr der AI Supply Chain.
Drei Einschränkungen, die in keiner Tool-Liste stehen
Kein Scanner ist vollständig. Fickling warnt selbst, dass eine Datei trotz bestandenem Check unsicher sein kann, und das letzte ModelScan-Release stammt vom Februar 2026. Plane zwei Scanner ein, nicht einen. Audit-Hooks sind laut Python-Doku keine Sandbox, bösartiger Code kann sie sogar abschalten: Sie liefern Telemetrie, die Isolation kommt vom Container. Und pip-audit findet bekannte Schwachstellen, keine bösartigen Pakete. Die Doku sagt das ausdrücklich.
Eine Intake-Pipeline in fünf Gates
Einzelne Tools helfen wenig, wenn ein Modell an ihnen vorbei in Produktion kommt. Das Gegenmittel ist ein fester Aufnahmeprozess: fünf Gates, durch die jedes Artefakt muss, egal wie gut der Ruf der Quelle ist.
Fünf Gates vom Download bis zur Freigabe. Produktion lädt nur, was Gate 5 passiert hat.
- 1. Quarantäne. Download in eine isolierte Staging-Umgebung, nie direkt in Produktion. Das erste Laden passiert ohne Netzwerkzugang.
- 2. Herkunft. Organisation, Account-Alter, Historie, Model Card. Geladen wird per Commit-Hash, nicht per Name.
- 3. Integrität. SHA-256 gegen den veröffentlichten Wert und, wo vorhanden, die Signatur. OpenSSF Model Signing signiert ein Manifest aller Dateien eines Modells. NVIDIA signiert damit nach eigenen Angaben seit März 2025 alle eigenen Modelle im NGC-Katalog.
- 4. Scan. Statischer Scan der Dateien, Layer-Liste bei Keras, Chat-Template bei GGUF, Audit der Abhängigkeiten.
- 5. Freigabe. Erst jetzt wandert das Modell in die interne Registry.
Selbst ausprobieren: der Scan in drei Minuten
Du brauchst ein Terminal (macOS oder Linux, unter Windows WSL) und Python 3.10 bis 3.12. Die drei Scanner kommen per pip, am besten in eine frische virtuelle Umgebung:
python3 -m venv .venv
source .venv/bin/activate
pip install fickling modelscan modelaudit
Dann baust du eine harmlose Testdatei. Sie nutzt denselben Trick wie oben, der Befehl gibt aber nur ein Wort aus:
python3 - <<'EOF'
import os, pickle
class Model:
def __reduce__(self):
return (os.system, ("echo pwned",))
pickle.dump(Model(), open("test.pkl", "wb"))
EOF
Jetzt prüfst du die Datei, ohne sie zu laden:
# Bordmittel: zeigt die Anweisungen im Pickle
python3 -m pickletools test.pkl
# übersetzt das Pickle zurück in Python und bewertet es
fickling --check-safety -p test.pkl
# scannen und Schweregrad vergeben
modelscan -p test.pkl
modelaudit test.pkl
Alle drei Scanner schlagen an. Fickling nennt die Datei „overtly malicious“, ModelScan meldet:
Unsafe operator found:
- Severity: CRITICAL
- Description: Use of unsafe operator 'system' from module 'posix'
posix ist das Modul, das auf macOS und Linux hinter os.system steckt. Entscheidend ist, was du dabei nicht getan hast: Du hast die Datei nie mit pickle.load() geöffnet.
Am echten Projekt
Die Werkzeuge bleiben dieselben. Geladen wird mit festgenagelter Version, hier mit der Bibliothek transformers. org/model steht für den Namen des Repositories auf Hugging Face, den Commit-Hash findest du in dessen Historie:
from transformers import AutoModel
model = AutoModel.from_pretrained(
"org/model",
# voller Commit-Hash, nicht "main"
revision="877b84a8f93f2d619faa2a6e514a32beef88ab0a",
use_safetensors=True, # kein Pickle
trust_remote_code=False, # Default, bewusst so lassen
)
Der Commit-Hash ist dabei mehr als Kosmetik. Fehlt im Repository eine Safetensors-Datei und ist die Revision nicht gepinnt, versucht transformers eine automatische Konvertierung über den Pull-Request-Branch des Konvertierungs-Bots von Hugging Face, also genau den Dienst aus „Silent Sabotage“. Mit festem Commit-Hash passiert das nicht. Und ohne use_safetensors=True fällt die Bibliothek still auf die Pickle-Datei zurück.
Die Prüfungen aus Gate 3 und 4 laufen im Projektordner, in dem die requirements.txt liegt:
# Gate 3: Hash mit dem veröffentlichten Wert vergleichen
sha256sum model.safetensors
# Gate 4: Repository vom Hub holen, statisch scannen
modelaudit hf://org/model
# Gate 4: Abhängigkeiten auf bekannte Lücken prüfen
pip install pip-audit
pip-audit -r requirements.txt
# Gate 4: Stückliste erzeugen und prüfen
syft . -o cyclonedx-json=sbom.json
grype sbom:./sbom.json
Drei Hinweise dazu. Syft und Grype sind eigene Programme, kein Python-Paket. Auf dem Mac installierst du sie mit brew install syft grype. Auf älteren macOS-Versionen heißt sha256sum stattdessen shasum -a 256. Und sobald jede Zeile deiner requirements.txt einen Hash trägt, erzwingt pip-audit --require-hashes -r requirements.txt den strengen Modus. Ohne Hashes bricht dieser Aufruf ab.
Eine Signatur lässt sich nur prüfen, wenn der Herausgeber eine veröffentlicht hat. Dann brauchst du die Signaturdatei, die erwartete Identität des Herausgebers und dessen Login-Dienst:
pip install model-signing
model_signing verify ./model --signature model.sig \
--identity "releases@example.org" \
--identity_provider "https://accounts.example.org"
Für Adapter und konvertierte Modelle gilt derselbe Weg. Was sich bei API-Modellen, MCP-Servern und Prompt-Templates ändert, steht im Überblick: Die neue Gefahr der AI Supply Chain.
Was kein Tool löst
Drei Dinge bleiben übrig, egal wie gut die Pipeline ist.
- Gewichte lassen sich nicht lesen. Einen antrainierten Backdoor findet kein statischer Scanner. Dagegen helfen nur eigene Tests und der Blick auf das Verhalten im Betrieb.
- Richtige Antworten beweisen nichts. Ein Payload läuft beim Laden. Was das Modell danach antwortet, kann völlig unauffällig sein.
- Vertrauen ist keine Kontrolle. Die unangenehmsten Fälle kamen über legitime Kanäle: ein Repository auf Platz 1 der Trends, ein Modell unter dem Namen eines gelöschten Autors, der Konvertierungsdienst der Plattform selbst.
Womit ich anfangen würde, wenn es noch gar nichts gibt:
- Safetensors als einziges erlaubtes Format festlegen und Ausnahmen begründen lassen.
- Jedes Modell per Commit-Hash pinnen, jede Abhängigkeit per Hash sperren.
- Einen Scanner in die CI hängen, bevor ein Modell in die Registry darf.
- Ausgehenden Traffic vom Inferenz-Server standardmäßig sperren. Ein Modell-Server hat keinen Grund, unbekannte Hosts anzurufen.
- Prompts, Adapter und MCP-Server in dieselbe Review-Schiene schicken wie Code.
Quellen:
Du willst wissen, was in eurer Modell- und Agenten-Lieferkette wirklich steckt? Lass uns sprechen. In 30 Minuten gehen wir unverbindlich die fünf Gates für euren Stack durch.