
Das writeToFile-Hilfsprogramm von Consul Template öffnete ein vom Operator angegebenes Ziel direkt und folgte verlinkten Pfadkomponenten, sodass gerenderte Ausgabe das vorgesehene Verzeichnis verlassen und eine bereits vorhandene Datei überschreiben konnte.
Der writeToFile-Helper von Consul Template öffnete ein vom Betreiber angegebenes Ziel direkt und folgte verlinkten Pfadkomponenten, wodurch gerenderte Ausgaben das vorgesehene Verzeichnis verlassen und eine bereits vorhandene Datei überschreiben konnten.
Ich fand dieses Problem bei der Überprüfung von HashiCorp Consul Template, mit einer direkten Dateisystem-Sicherheitsfrage im Hinterkopf:
Wenn writeToFile einen Pfad erhält, der scheinbar innerhalb des vorgesehenen Verzeichnisses liegt, wird dann geprüft, wohin das Dateisystem die Daten tatsächlich schreibt?
In diesem Fall lautete die Antwort nein.
Der Template-Helper writeToFile öffnete den endgültigen, vom Benutzer bereitgestellten Pfad direkt über os.Create() oder os.OpenFile().
Diese Operationen folgten symbolischen Links, Directory Junctions und gleichwertigen Dateisystem-Umleitungen, die bereits im Zielpfad vorhanden waren.
Das bedeutete, dass die Pfadzeichenkette innerhalb des vom Betreiber vorgesehenen Wurzelverzeichnisses bleiben konnte, während der tatsächliche Schreibvorgang woanders landete.
In meinem kontrollierten Proof of Concept leitete ein verlinktes Elternverzeichnis die gerenderte Ausgabe außerhalb des vorgesehenen Verzeichnisbaums um und führte dazu, dass eine bereits vorhandene Zieldatei überschrieben wurde.
Dieses Problem wurde zu CVE-2026-14361.
HashiCorp-Bulletin: HCSEC-2026-20
IBM-Bulletin: CVE-2026-14361 security bulletin
CVE: CVE-2026-14361
Behoben in: 0.42.1
photo0
vom Betreiber angegebenes Ziel scheint innerhalb des vorgesehenen Wurzelverzeichnisses zu liegen -> Angreifer platziert vorab ein verlinktes Eltern- oder Endpfad-Element -> writeToFile öffnet den Pfad direkt -> das Dateisystem löst den Schreibvorgang außerhalb des vorgesehenen Verzeichnisses auf -> gerendertes Geheimnis wird umgeleitet -> bereits vorhandenes Ziel kann überschrieben werden
Consul Template rendert Daten aus Quellen wie Consul und Vault.
Der Helper writeToFile ermöglicht es einem Template, ausgewählte Inhalte in eine separate lokale Datei zu schreiben, während ein gewünschter Besitzer, eine Gruppe und ein Berechtigungsmodus angewendet werden.
HashiCorps Dokumentation demonstriert den Helper ausdrücklich mit PKI-Material:
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem
Das macht dies zu mehr als einem gewöhnlichen Ausgabe-Helper.
Die Inhalte, die diese Grenze überschreiten, können umfassen:
Die wichtige Frage war nicht, ob writeToFile einen angeforderten Dateinamen erstellen konnte.
Die eigentliche Frage war:
Schreibt der Prozess an den vom Betreiber vorgesehenen Dateisystemort oder lediglich an das Objekt, auf das der Pfad zum Zeitpunkt des Öffnens aufgelöst wird?
In verwundbaren Versionen vertraute er Letzterem.
Schreib-Helper sind Sicherheitsflächen mit hohem Wert, weil sie von Anwendungsdaten zur Dateisystem-Mutation übergehen.
Die interessanten Fehler sind oft keine klassische ../-Traversal.
Es sind Auflösungsfehler:
Das ist besonders wichtig, wenn der Prozess mit mehr Dateisystemrechten läuft als der Angreifer.
Ein lokaler Angreifer mit niedrigen Rechten kann möglicherweise keine sensible Datei direkt überschreiben.
Aber wenn er eine Pfadkomponente unter einem vorgesehenen Schreibverzeichnis beeinflussen kann, könnte ein privilegierterer Consul-Template-Prozess den Schreibvorgang für ihn ausführen.
Das war die Grenze, auf die ich mich konzentriert habe.
Ich bin dies nicht als generische Path-Traversal-Überprüfung angegangen.
Der bereitgestellte Pfad benötigte keine ..-Segmente.
Er konnte die gesamte Zeit lexikalisch innerhalb des vorgesehenen Wurzelverzeichnisses bleiben.
Die stärkere Frage war:
Werden verlinkte Zielkomponenten abgelehnt, bevor sensible Inhalte geschrieben werden?
Diese Frage ist wichtig für beides:
Der zweite Fall ist besonders nützlich, weil Logs und Konfiguration weiterhin einen harmlos aussehenden Pfad unter dem erwarteten Verzeichnis zeigen.
Das Dateisystem löst ihn jedoch woanders auf.
Die Grundursache war eine direkte, pfadbasierte Dateierstellung ohne link-bewusste Validierung.
In der getesteten Revision wählte writeToFile() einen von zwei Öffnungspfaden.
Der Anhänge-Modus verwendete:
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
Der normale Schreibmodus verwendete:
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)
Keiner der beiden Pfade lehnte verlinkte Zielkomponenten ab, bevor die Datei geöffnet wurde.
Das ist wichtig, weil:
os.Create(path) bestehenden Dateisystem-Umleitungen folgt und die aufgelöste Datei kürztos.OpenFile(path, ...) verlinkten Pfadkomponenten im Anhänge-Modus folgtDie gleiche pfadbasierte Annahme setzte sich nach dem Schreiben fort.
Besitzer und Berechtigungen wurden erneut über den Pfad angewendet:
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
Das bedeutete, dass Metadaten-Operationen ebenfalls an einen veränderbaren Pfadnamen gebunden waren, statt an den bereits geöffneten Dateideskriptor.
Weil der Angreifer die Umleitung vorbereiten kann, bevor writeToFile ausgeführt wird.
Für den grundlegenden Angriff ist kein probabilistischer Wettlauf erforderlich.
Die Abfolge ist unkompliziert:
writeToFile öffnet sie direktDas ist die gesamte Verwundbarkeit.
Es stimmt, dass Betriebssysteme bei pfadbasierten Datei-Öffnungen normalerweise Symlinks folgen.
Das macht dies nicht zu einem sicheren Anwendungsverhalten.
Die Sicherheitsfrage ist nicht:
„Hat sich Go wie dokumentiert verhalten?"
Die eigentliche Frage ist:
Hat ein Helper, der sensible gerenderte Daten schreibt, verifiziert, dass das aufgelöste Ziel mit dem vom Betreiber vorgesehenen Ort übereinstimmt?
In verwundbaren Versionen tat er das nicht.
Diese Unterscheidung ist wichtig, weil Consul Template ausgeführt werden kann: