
Proof-of-concept che dimostra una vulnerabilità di path traversal (CVE-2026-35204) nell'installazione del plugin Helm, consentendo la scrittura arbitraria di file tramite metadati di plugin appositamente creati.
Questo repository rappresenta un PoC per la CVE-2026-35204
"Helm è un gestore di pacchetti per Charts per Kubernetes. Dalla versione 4.0.0 to 4.1.3, un plugin Helm appositamente creato, quando installato o aggiornato, causa la scrittura del contenuto del plugin in una posizione arbitraria del filesystem. Per prevenire ciò, è necessario convalidare che il file plugin.yaml del plugin Helm non includa un campo version: contenente separatori di percorso POSIX dot-dot, cioè "/../". Questa vulnerabilità è risolta nella versione 4.1.4."
Questo PoC dimostra che metadata.Version viene utilizzato per costruire il percorso dell'archivio del plugin, consentendo il path traversal e la scrittura del plugin.tgz scaricato al di fuori della directory del plugin prevista. I contenuti del plugin vengono ancora estratti nella directory cache designata prima di essere installati, quindi questo PoC non dimostra l'estrazione arbitraria o la sovrascrittura arbitraria dei file dell'host.
L'installer dei plugin Helm ha un'interfaccia con quattro implementazioni (HTTP, locale, VCS e OCI). Questo PoC si concentra sull'installer HTTP. A differenza degli installer HTTP/OCI, l'installer VCS non costruisce affatto un percorso del tarball da metadata.Version. Ecco un blocco di codice che prepara tarballPath:
filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version)
tarballPath := helmpath.DataPath("plugins", filename)
dove metadata.Version è la variabile che controlliamo in plugin.yaml. Per attivare la vulnerabilità, è necessario impacchettare il tuo plugin modificato in un archivio .tgz, poiché il suo metadata.Version è quello estratto da plugin.ExtractTgzPluginMetadata e scaricato tramite installer HTTP/OCI/locale. Pertanto, per ottenere il path traversal, possiamo usare http_installer, che recupera i plugin come archivio servito da un server web.
Nell'esempio seguente, il binario helm è stato ricostruito manualmente per abilitare i messaggi di log, dove puoi osservare due diversi flussi di dati. Primo è l'installazione del plugin con un nome di versione corretto. Secondo è l'installazione del plugin che ha separatori di percorso dot-dot nel nome della versione:
./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
Le tracce di runtime dimostrano che il path traversal influisce solo sulla posizione in cui viene posizionato l'archivio del plugin scaricato, che è fondamentalmente la variabile di ambiente HELM_PLUGINS. La cache (i.CacheDir) e l'estrazione finale (i.Path()) rimangono invariate. Con questo in mente, possiamo manipolare la destinazione temporanea dell'archivio e il suo nome, ottenendo una scrittura di file.
Non è necessario eseguire nulla né utilizzare webhook post-installazione; la semplice installazione del plugin è sufficiente per attivare la vulnerabilità.
Durante la mia analisi dell'installer HTTP, ho osservato che l'estrazione dell'archivio avviene in i.CacheDir, mentre l'installazione (fs.CopyDir) copia i file da i.CacheDir in i.Path(). Non ho trovato un percorso di codice in cui tarballPath influenzato dal traversal influisca sul contenuto dell'archivio. Pertanto, in base alle tracce di runtime analizzate, la destinazione dell'estrazione dell'archivio è indipendente da tarballPath controllato dal traversal.
L'avviso menziona sia l'installazione che l'aggiornamento. Per gli installer HTTP/locale/VCS, Update() non è implementato; l'installer OCI implementa l'aggiornamento invocando Install() dopo aver rimosso il plugin precedente.
Ecco il metodo Update() dell'installer HTTP:
func (i *HTTPInstaller) Update() error {
return fmt.Errorf("method Update() not implemented for HttpInstaller")
}
Per mitigare la vulnerabilità, è stata aggiunta la funzione isValidSemver, che viene ora chiamata quando viene invocato Validate() all'interno di metadata.go.
"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))
}
Come puoi vedere, se la versione non è un semver valido (e ../../../../ non lo è), riceveremo un errore nel processo di installazione del plugin dannoso.
Per eseguire il PoC puoi usare lo script scripts/pos.sh:
./scripts/poc.sh
Il risultato sarà simile a questo:

MIT