Portainer: Compose-Felder configs und secrets umgehen die Bind-Mount-Sperre

Der Stack-Pruefer lehnt Bind-Mounts in volumes ab, sieht aber die Felder configs und secrets nicht an, deren file jeden Host-Pfad benennt.

Advisory-ID: TP-2026-092
Produkt: Portainer (Verwaltungsoberflaeche fuer Docker und Kubernetes, Go)
Schwachstellentyp: Pfaddurchquerung in Compose-Dateifeldern (CWE-22)
CVE: beantragt (GitHub CNA), Vergabe ausstehend
CVSS 4.0: 9.4 (Kritisch) · CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
Betroffene Versionen: >= 2.39.0 bis <= 2.39.8 sowie >= 2.40.0 bis <= 2.45.1
Hersteller-Advisory: GHSA-9gxj-xvxm-77rj
Behoben in: 2.45.2
Gemeldet: 07. Juni 2026

Zusammenfassung

Portainer laesst auch Nutzer ohne Administratorrechte Compose-Stacks in eine Umgebung ausrollen. Fuer diesen Fall gibt es je Umgebung die Einstellung, Bind-Mounts fuer Nichtadministratoren zu verbergen, und der Pruefer lehnt sie dann serverseitig ab, sowohl im obersten Abschnitt volumes als auch in den volumes jedes Dienstes. Die Abschnitte configs und secrets sieht er sich nicht an, obwohl deren Feld file einen Pfad auf dem Host benennt, den die Compose-Engine als Bind-Mount in den Container einhaengt. Ein Standardnutzer reicht damit genau die Anfrage ein, die Portainer als direkten Bind-Mount zurueckweist, und bekommt den Inhalt der Host-Datei in seinen Container. Wer auf demselben Weg den Docker-Socket einbindet, uebernimmt den Daemon des Hosts.

Ursache

Die Pruefroutine IsValidStackFile weist Bind-Mounts von Nichtadministratoren im obersten Abschnitt volumes und in den volumes jedes Dienstes zurueck, untersucht die obersten Abschnitte configs und secrets jedoch nie. Deren Feld file benennt einen Pfad auf dem Host, den die Compose-Engine als Bind-Mount in den referenzierenden Container materialisiert, sowohl als absoluter Pfad wie /etc/shadow als auch ueber ../ aus dem Stack-Verzeichnis heraus. Dieselbe Luecke betrifft weitere Compose-Felder, die eine Datei benennen, etwa env_file: bei einem Ausrollen in Swarm liest der Portainer-Server diese Datei selbst und schreibt ihren Inhalt in die Dienstdefinition. Portainer arbeitet mit einem geteilten Verantwortungsmodell, in dem die Administration festlegt, wie freizuegig eine Umgebung ist, und Portainer diese Festlegung durchsetzt; dieser Fund liegt auf Portainers Seite dieser Linie. Umgebungen, in denen die Einstellung abgeschaltet ist, erlauben Nichtadministratoren Bind-Mounts ohnehin und gewinnen durch den Fund keine zusaetzliche Angriffsflaeche.

Proof of Concept

# Als Standardnutzer mit Stack-Recht, bei aktivierter Bind-Mount-Sperre:
configs:
  gestohlen:
    file: /etc/shadow
services:
  app:
    image: alpine
    command: ["sleep", "infinity"]
    configs:
      - source: gestohlen
        target: /beute

# Der Pruefer sieht den Abschnitt configs nicht an, die Compose-Engine
# haengt die Host-Datei ein, der Container liest /beute.
# Mit file: /var/run/docker.sock wird daraus die Kontrolle ueber den Daemon.

Dieselbe Anfrage als direkter Eintrag unter volumes wird von Portainer zurueckgewiesen, was den Unterschied zwischen gepruefter und ungepruefter Flaeche zeigt.

Auswirkung

  • Lesen beliebiger Dateien des Host-Dateisystems durch einen Nutzer in der Standardrolle.
  • Vollstaendige Kontrolle ueber den Docker-Daemon des Hosts, sobald der Socket auf demselben Weg eingebunden wird.
  • Lesen von Dateien des Portainer-Servers selbst, einschliesslich seiner Datenbank, ueber env_file bei einem Ausrollen in Swarm.
  • Umgehung einer Einstellung, die die Administration ausdruecklich gesetzt hat, um genau das zu verhindern.

Referenzen

Steckt so etwas in Ihrer Software?

Diese Schwachstelle hat unser Team im Rahmen seiner Arbeit gefunden. Lassen Sie Ihre Anwendungen von denselben Spezialisten prüfen, mit einem Penetrationstest von turingpoint.