
Le helper writeToFile de Consul Template ouvrait directement une destination fournie par l'opérateur et suivait les composants de chemins liés, permettant à la sortie rendue de s'échapper du répertoire prévu et d'écraser un fichier préexistant.
Le helper writeToFile de Consul Template ouvrait directement une destination fournie par l'opérateur et suivait les composants de chemin liés, permettant à la sortie rendue de s'échapper du répertoire prévu et d'écraser un fichier préexistant.
J'ai trouvé ce problème en examinant HashiCorp Consul Template, avec une question directe de sécurité du système de fichiers en tête :
Si writeToFile reçoit un chemin qui semble être à l'intérieur du répertoire prévu, vérifie-t-il où le système de fichiers écrira réellement les données ?
Dans ce cas, la réponse était non.
Le helper de template writeToFile ouvrait directement le chemin final fourni par l'utilisateur via os.Create() ou os.OpenFile().
Ces opérations suivaient les liens symboliques, les jonctions de répertoires et les redirections de système de fichiers équivalentes déjà présentes dans le chemin de destination.
Cela signifiait que la chaîne de chemin pouvait rester sous la racine prévue par l'opérateur pendant que l'écriture réelle aboutissait ailleurs.
Dans ma preuve de concept contrôlée, un répertoire parent lié redirigeait la sortie rendue hors de l'arborescence prévue et provoquait l'écrasement d'un fichier cible préexistant.
Ce problème est devenu CVE-2026-14361.
Bulletin HashiCorp : HCSEC-2026-20
Bulletin IBM : Bulletin de sécurité CVE-2026-14361
CVE : CVE-2026-14361
Corrigé dans : 0.42.1
photo0
destination fournie par l'opérateur apparaît dans la racine prévue -> l'attaquant pré-positionne un parent lié ou un composant final de chemin -> writeToFile ouvre le chemin directement -> le système de fichiers résout l'écriture hors du répertoire prévu -> le secret rendu est redirigé -> une cible préexistante peut être écrasée
Consul Template rend des données provenant de sources telles que Consul et Vault.
Le helper writeToFile permet à un modèle d'écrire un contenu sélectionné dans un fichier local séparé tout en appliquant un propriétaire, un groupe et un mode de permission demandés.
La documentation de HashiCorp démontre spécifiquement ce helper avec du matériel PKI :
clé privée -> writeToFile /my/path/to/cert.key
autorité de certification -> writeToFile /my/path/to/cert.pem
certificat -> ajout à /my/path/to/cert.pem
Cela en fait plus qu'un simple helper de sortie.
Le contenu qui franchit cette frontière peut inclure :
La question importante n'était pas de savoir si writeToFile pouvait créer un nom de fichier demandé.
La vraie question était :
Le processus écrit-il à l'emplacement du système de fichiers prévu par l'opérateur, ou simplement à l'objet vers lequel le chemin pointe au moment de l'ouverture ?
Dans les versions vulnérables, il se fiait à la seconde option.
Les helpers d'écriture sont des surfaces de sécurité à forte valeur car ils font passer des données d'application à une mutation du système de fichiers.
Les défaillances intéressantes ne sont souvent pas la traversée classique ../.
Ce sont des défaillances de résolution :
C'est particulièrement important lorsque le processus s'exécute avec plus de privilèges sur le système de fichiers que l'attaquant.
Un attaquant local peu privilégié peut ne pas être en mesure d'écraser directement un fichier sensible.
Mais s'il peut influencer un composant de chemin sous un répertoire d'écriture prévu, un processus Consul Template plus privilégié peut effectuer l'écriture à sa place.
C'est la frontière sur laquelle je me suis concentré.
Je n'ai pas abordé cela comme une revue générique de traversée de chemin.
Le chemin fourni n'avait pas besoin de segments ...
Il pouvait rester lexicalement à l'intérieur de la racine prévue tout du long.
La question la plus forte était :
Les composants de destination liés sont-ils rejetés avant que le contenu sensible ne soit écrit ?
Cette question concerne à la fois :
Le deuxième cas est particulièrement utile car les journaux et la configuration montrent toujours un chemin d'apparence innocente sous le répertoire attendu.
Le système de fichiers le résout ailleurs.
La cause racine était la création directe de fichier basée sur un chemin sans validation tenant compte des liens.
À la révision testée, writeToFile() sélectionnait l'un des deux chemins d'ouverture.
Le mode ajout utilisait :
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
Le mode écriture normale utilisait :
dirPath := filepath.Dir(path)
if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}
f, err = os.Create(path)
Aucun des deux chemins ne rejetait les composants de destination liés avant l'ouverture du fichier.
Cela importe parce que :
os.Create(path) suit les redirections existantes du système de fichiers et tronque le fichier résoluos.OpenFile(path, ...) suit les composants de chemin liés en mode ajoutLa même hypothèse basée sur le chemin se poursuivait après l'écriture.
La propriété et les permissions étaient appliquées en utilisant à nouveau le chemin :
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
Cela signifiait que les opérations de métadonnées étaient également liées à un nom de chemin mutable au lieu du descripteur de fichier déjà ouvert.
Parce que l'attaquant peut préparer la redirection avant que writeToFile ne s'exécute.
Aucune course probabiliste n'est requise pour l'attaque de base.
La séquence est simple :
writeToFile l'ouvre directementC'est toute la vulnérabilité.
Il est vrai que les systèmes d'exploitation suivent normalement les liens symboliques lors des ouvertures de fichiers basées sur un chemin.
Cela ne fait pas de ce comportement un comportement sûr pour une application.
La question de sécurité n'est pas :
« Go s'est-il comporté comme documenté ? »
La vraie question est :
Le helper qui écrivait des données sensibles rendues a-t-il vérifié que la destination résolue correspondait à l'emplacement prévu par l'opérateur ?
Dans les versions vulnérables, ce n'était pas le cas.
Cette distinction importe car Consul Template peut s'exécuter :