Prowler: Remote Code Execution auf den Workern durch eine kubeconfig mit auth-provider cmd-path

Ein in der Standardkonfiguration selbst registriertes Konto legt einen Kubernetes-Provider mit einer kubeconfig an, die einen legacy auth-provider mit cmd-path trägt, und führt beim Verbindungstest beliebige Befehle auf dem für alle Mandanten geteilten Celery-Worker aus.

Advisory-ID: TP-2026-040
Produkt: Prowler (Open-Source-CSPM-Plattform; Prowler App besteht aus einer Django-REST-API, Celery-Workern und einer Next.js-Oberfläche)
Schwachstellentyp: OS-Command-Injection über eine unvollständige Blockliste (CWE-78, CWE-94)
CVE: CVE-2026-73263
CVSS 3.1: 9.9 (Kritisch) · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Hersteller-Advisory: GHSA-ccqh-6cjc-wp4j
Betroffene Versionen: < 5.36.0
Behoben in: 5.36.0
Gemeldet: 10. Juli 2026

Zusammenfassung

Prowler ist eine Cloud-Security-Posture-Management-Plattform; die Prowler App verwaltet die Cloud-Zugangsdaten mehrerer Mandanten über eine Django-REST-API und Celery-Worker. Beim Anlegen eines Kubernetes-Providers speichert die API eine vom Nutzer gelieferte kubeconfig als Secret, und der Verbindungstest lädt diese kubeconfig später über den kubernetes-python-Client. Der Validator, der Kommandoausführung aus kubeconfigs unterbinden soll, prüft nur auf exec-Blöcke und übersieht den legacy auth-provider mit cmd-path, sodass diese Variante die Prüfung passiert und unverändert gespeichert wird. Beim Laden ruft der Client den in cmd-path hinterlegten Befehl über subprocess.Popen auf. Da die Selbstregistrierung in der Standardkonfiguration offen ist, führt ein beliebiger anonymer Besucher in drei API-Aufrufen beliebige Befehle als Nutzer prowler auf dem für alle Mandanten geteilten Worker aus, liest dessen Secrets und kompromittiert damit sämtliche Mandanten. turingpoint hat die Kette Ende-zu-Ende live verifiziert und an den Hersteller gemeldet, der sie in 5.36.0 behoben hat.

Ursache

Beim POST /api/v1/providers/{id}/connection wird das gespeicherte kubeconfig-Secret über config.load_kube_config_from_dict geladen (prowler/providers/kubernetes/kubernetes_provider.py:258), und der kubernetes-python-Client (v32.0.1) führt den Wert aus cmd-path in CommandTokenSource.token über subprocess.Popen aus, bevor überhaupt ein Cluster kontaktiert wird. Die einzige Schranke ist der Validator kubeconfig_contains_exec_auth (api/src/backend/api/v1/serializers.py:1591, aufgerufen aus validate_kubeconfig_content :1789), der einen Eintrag ausschließlich dann ablehnt, wenn er einen exec-Block enthält, und den legacy auth-provider (name: gcp) mit config.cmd-path/cmd-args nie betrachtet. Die Fehlermeldung des Validators (:1571) hält fest, dass kubeconfig-Kommando-Authentifizierung aus Sicherheitsgründen abgelehnt wird, sodass der auth-provider-Pfad eine Lücke in einer beabsichtigten Blockliste ist und kein zugestandenes Verhalten. Die Selbstregistrierung ist in der Standardkonfiguration offen (POST /api/v1/users liefert ein nutzbares Konto, /api/v1/tokens ein JWT ohne E-Mail-Bestätigung), sodass ein beliebiger anonymer Besucher zum Mandanten-Admin wird und den Sink in drei API-Aufrufen erreicht. Der eingeschleuste Befehl läuft als Nutzer prowler (uid 1000) auf dem von allen Mandanten geteilten Celery-Worker, dessen Prozessumgebung DJANGO_SECRETS_ENCRYPTION_KEY und DJANGO_TOKEN_SIGNING_KEY enthält.

Proof of Concept

# 1) Selbstregistrierung und Token
POST /api/v1/users        -> Konto anlegen
POST /api/v1/tokens       -> JWT erhalten

# 2) Kubernetes-Provider anlegen, dann kubeconfig-Secret mit legacy auth-provider setzen
POST /api/v1/providers/{id}/secrets
{
  "...": {
    "users": [{"name": "u", "user": {"auth-provider": {"name": "gcp",
      "config": {"cmd-path": "/bin/sh", "cmd-args": "-c${IFS}<befehl>"}}}}]
  }
}

# 3) Verbindungstest loest das Laden der kubeconfig aus
POST /api/v1/providers/{id}/connection

Der Validator lässt das Secret durch, weil es keinen exec-Block enthält; der Verbindungstest lädt die kubeconfig, und der kubernetes-python-Client führt cmd-path mit den whitespace-getrennten cmd-args (daher ${IFS}) über subprocess.Popen aus. turingpoint hat die Ausführung live über einen Netzwerk-Beacon und eine im Container geschriebene Datei als uid=1000(prowler) auf dem Worker bestätigt.

Auswirkung

  • Beliebige Betriebssystembefehle als Nutzer prowler (uid 1000) auf dem von allen Mandanten geteilten Celery-Worker.
  • Auslesen von DJANGO_SECRETS_ENCRYPTION_KEY und damit Entschlüsselung der gespeicherten Cloud-Zugangsdaten jedes Mandanten.
  • Auslesen von DJANGO_TOKEN_SIGNING_KEY und damit Fälschung des JWT beliebiger Nutzer.
  • In der Standardkonfiguration ohne vorherige Einladung erreichbar, da die Selbstregistrierung offen ist.

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.