
Consul Template validierte, wohin ein Symlink während der Template-Auswertung zeigte, aber der spätere Abruf der Abhängigkeiten las den ursprünglichen Pfad. Wurde der Link zwischen diesen Vorgängen neu ausgerichtet, wurde aus einem Dateiverweis innerhalb der Sandbox eine Offenlegung von Dateien außerhalb der Sandbox.
Consul Template hat validiert, wohin ein Symlink während der Template-Auswertung zeigte, aber der spätere Abruf der Abhängigkeit las den ursprünglichen Pfad. Wenn der Link zwischen diesen Vorgängen umgebogen wurde, wurde aus einem Dateiverweis innerhalb der Sandbox eine Datei-Offenlegung außerhalb der Sandbox.
Ich habe dieses Problem bei der Überprüfung von HashiCorp Consul Template gefunden, mit einer ganz bestimmten Sicherheitsfrage im Hinterkopf:
Wenn sandbox_path einen Symlink während der Template-Auswertung validiert, bleibt der spätere Dateilesevorgang an dasselbe validierte Ziel gebunden?
In diesem Fall lautete die Antwort: nein.
Der file-Template-Helper löste den Pfad auf und erzwang die konfigurierte Sandbox während der Template-Auswertung. Aber nach dieser Prüfung erzeugte er eine Dateiabhängigkeit unter Verwendung des ursprünglichen Rohpfads.
Diese Abhängigkeit wurde später abgerufen.
Wenn ein Angreifer den Symlink in der Lücke zwischen Validierung und Abruf der Abhängigkeit umbog, konnte Consul Template eine Datei außerhalb der Sandbox lesen. Wenn der Link vor dem nächsten Rendern wiederhergestellt wurde, bestand die Sandbox-Validierung erneut und der zwischengespeicherte externe Inhalt wurde weiterhin gerendert.
Dieses Problem wurde CVE-2026-5061.
HashiCorp-Bulletin: HCSEC-2026-12
IBM-Bulletin: CVE-2026-5061 Security Bulletin
CVE: CVE-2026-5061
Behoben in: 0.42.0
photo0
vom Angreifer kontrollierter Symlink in der Sandbox -> Sandbox-Validierung löst zu sicherem Ziel auf -> Abhängigkeit speichert ursprünglichen Rohpfad -> Angreifer biegt Link außerhalb der Sandbox um -> Abruf der Abhängigkeit liest externe Datei -> Angreifer stellt sicheren Link wieder her -> nächste Validierung besteht -> zwischengespeicherter externer Inhalt wird gerendert
Consul Template ist ein Template-Rendering-Werkzeug für Consul- und Vault-Daten.
Es kann kontinuierlich laufen, Abhängigkeiten auf Änderungen überwachen und Daten in Dateien oder Umgebungsvariablen rendern, die von Anwendungen konsumiert werden können.
Der file-Template-Helper liest eine lokale Datei und fügt ihren Inhalt in die gerenderte Ausgabe ein.
Da lokale Dateilesevorgänge prozesslesbare Geheimnisse offenlegen können, stellt Consul Template sandbox_path als Grenze für diesen Helper bereit.
Die dokumentierte Sicherheitseigenschaft ist unkompliziert:
file übergebene Pfade müssen innerhalb der konfigurierten Sandbox liegenDas macht sandbox_path zu einer echten Sicherheitsgrenze.
Die wichtige Frage war nicht, ob der Pfad so aussah, als läge er in der Sandbox.
Die eigentliche Frage war:
Bleibt die Datei, die gelesen wird, dieselbe Datei, die die Sandbox-Validierung bestanden hat?
In verwundbaren Versionen war das nicht der Fall.
Dateisystem-Sandboxes scheitern oft an der Lücke zwischen Pfadvalidierung und Dateizugriff.
Das übliche Muster ist:
Diese Annahme ist unsicher, wenn ein Angreifer einen Symlink oder eine gleichwertige Dateisystem-Umleitung zwischen den beiden Vorgängen verändern kann.
Consul Template machte diese Angriffsfläche besonders interessant, weil Template-Auswertung und Abruf der Abhängigkeiten getrennte Phasen waren.
Diese Trennung erzeugte die richtige Sicherheitsfrage:
Ist der Abruf der Abhängigkeit an das validierte Ziel gebunden, oder löst er den vom Angreifer kontrollierten Pfad erneut auf?
Das war die Grenze, auf die ich mich konzentriert habe.
Die Grundursache war eine Time-of-Check-to-Time-of-Use-Diskrepanz zwischen:
template/funcs.godependency/file.goIn der getesteten Revision tat fileFunc() Folgendes:
normalized := strings.TrimSpace(s)
err := pathInSandbox(sandboxPath, normalized)
if err != nil {
return "", err
}
d, err := dep.NewFileQuery(s)
Der Validierungshelfer löste Symlinks vor der Containment-Prüfung korrekt auf:
sandboxResolved, err := filepath.EvalSymlinks(filepath.Clean(sandbox))
targetResolved, err := filepath.EvalSymlinks(filepath.Clean(path))
rel, err := filepath.Rel(sandboxResolved, targetResolved)
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return fmt.Errorf("'%s' is outside of sandbox", path)
}
Die Sandbox-Prüfung selbst verstand also das aufgelöste Ziel.
Aber dieses aufgelöste Ziel wurde verworfen.
NewFileQuery() speicherte stattdessen den ursprünglichen String:
return &FileQuery{
stopCh: make(chan struct{}, 1),
path: s,
}, nil
Der spätere Abruf der Abhängigkeit las diesen Pfad dann erneut:
data, err := os.ReadFile(d.path)
Das ist die gesamte Verwundbarkeit.
Der Code prüfte eine Dateisystemauflösung und verwendete später eine andere.
Weil ein Symlink veränderbar ist.
Der Angreifer muss kein Ziel außerhalb der Sandbox dazu bringen, pathInSandbox() direkt zu bestehen.
Er muss nur ändern, wohin der bereits genehmigte Rohpfad nach der Validierung, aber vor dem Fetch()-Lesevorgang auflöst.
Die Abfolge ist:
pathInSandbox() zu einer sicheren Datei aufFileQuery zeichnet den Rohpfad des Links aufos.ReadFile(d.path) löst den Link erneut aufDieser letzte Schritt ist wichtig.
Das Wiederherstellen des sicheren Links entfernte das bereits abgerufene Geheimnis nicht aus dem Abhängigkeits-Cache.
Der wichtige Unterschied ist die Umgehung einer expliziten Sicherheitskontrollmaßnahme.
Dies war nicht nur:
„die Datei hat sich geändert, während Consul Template sie beobachtet hat“
Das Beobachten von Dateien auf Änderungen ist erwartetes Verhalten.
Das eigentliche Problem war:
ein Pfad, der die dokumentierte Sandbox-Beschränkung bestanden hatte, konnte später verwendet werden, um eine Datei außerhalb dieser Sandbox zu lesen
Das ist ein direkter Vertrauensgrenzen-Fehler.
Die Anwendung hatte bereits eine Sicherheitsentscheidung getroffen:
Aber der spätere Abruf war nicht an das Ziel gebunden, das diese Entscheidung rechtfertigte.
Das ist es, was gewöhnliche Dateisystem-Veränderlichkeit in eine Verwundbarkeit verwandelte.
Ich habe einen eigenständigen Reproducer um die exakte Auswertungs- und Abhängigkeitsabruf-Sequenz herum gebaut.
Der kontrollierte Aufbau verwendete:
Der Reproduktionsablauf war:
file-Helper auswerten, sodass die Sandbox-Validierung erfolgreich ist und der Rohpfad als Abhängigkeit registriert wird.os.ReadFile(d.path) durch den umgeleiteten Link liest.Das beobachtete Verhalten war:
Dieser Hash-Vergleich war wichtig.
Er bewies, dass der endgültig gerenderte Wert kein veralteter sicherer Inhalt, kein Dateinamen-Artefakt oder ein Nebenprodukt eines Fehlerpfads war.
Es war der bytegenaue Inhalt der Datei außerhalb der Sandbox.
Der stärkste Beweis für ein TOCTOU-Problem muss die Timeline kontrollieren.
Einfach zu zeigen, dass ein Symlink aus der Sandbox herauszeigen kann, wäre schwächer, weil pathInSandbox() ein externes Ziel bereits ablehnte, wenn es eines beobachtete.
Die Sicherheitsbehauptung hing davon ab, alle diese Zustände der Reihe nach zu beweisen:
Deshalb trennte der Reproducer Template-Validierung, Abruf der Abhängigkeit, Link-Wiederherstellung und das nächste Rendern explizit.
Der PoC verließ sich nicht auf zufälliges Timing oder wiederholtes Raten.
Er steuerte den verwundbaren Lebenszyklus deterministisch.
Das machte die Grundursache und die Auswirkungen viel leichter vertretbar.
Dieses Problem erfordert Einfluss auf das lokale Dateisystem.
Ein Angreifer benötigt genügend Zugriff, um den relevanten Symlink oder einen gleichwertigen verlinkten Pfad während des verwundbaren Fensters zu erstellen, zu ersetzen oder umzubiegen.
Der Consul-Template-Prozess muss außerdem:
Diese Voraussetzungen sind wichtig.
Dies war kein nicht authentifiziertes Remote-Arbitrary-File-Read aus einer Standardbereitstellung.
Aber innerhalb des betroffenen lokalen Vertrauensmodells war die Auswirkung erheblich:
sandbox_path-BeschränkungDas Risiko steigt, wenn der Prozess mit Zugriff auf sensible Anmeldedaten, Dienstkonfigurationen, Tokens oder private Schlüssel läuft.
HashiCorp klassifizierte das Problem wie folgt:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
Der veröffentlichte Basis-Score ist:
4.7 / Mittel
Diese Bewertung spiegelt die realen Einschränkungen wider:
Das ist eine angemessene Klassifizierung.
Das Problem ist eng begrenzt in Bezug auf die Voraussetzungen für Angreifer, aber die Sandbox-Umgehung und die daraus resultierende Datei-Offenlegung sind beide konkret.
Das HashiCorp-Bulletin listet auf:
Betroffen: consul-template bis einschließlich 0.41.4
Behoben: consul-template 0.42.0
Der Fix wurde im Release 0.42.0 am 15. April 2026 ausgeliefert.
Der Fix war klein und adressierte direkt die unterbrochene Bindung.
Anstatt das aufgelöste Ziel zu validieren und die Abhängigkeit dann aus der Roh-Eingabe zu erstellen, gibt der gepatchte Code den aufgelösten Pfad zurück und übergibt diesen Pfad an NewFileQuery():
resolvedPath, err := resolveSandboxedPath(
sandboxPath,
strings.TrimSpace(s),
)
if err != nil {
return "", err
}
d, err := dep.NewFileQuery(resolvedPath)
Die Sicherheitseigenschaft änderte sich von:
zu:
Das schließt den gemeldeten Symlink-Umbiegepfad, weil das Ändern des ursprünglichen Links den von der Abhängigkeit gespeicherten Pfad nicht mehr ändert.
Der Patch fügte außerdem einen fokussierten Regressionstest hinzu, der die exakte Sequenz abdeckt:
Das ist die Art von Behebung, die man für diesen Bug möchte:
Ich habe dieses Problem am 20. März 2026 privat an HashiCorp Security gemeldet.
Der Bericht enthielt:
HashiCorp behob das Problem in 0.42.0 und veröffentlichte HCSEC-2026-12 am 12. Mai 2026.
IBM veröffentlichte ein entsprechendes Security Bulletin für dieselbe CVE.
Beide offiziellen Bulletins erkannten den Bericht an als:
Mohamed Abdelaal (0xmrma)
Die wichtigste Lektion ist einfach:
einen Pfad zu validieren ist nicht genug, wenn die spätere Dateioperation diesen Pfad zu einem anderen Objekt auflösen kann
Diese Regel gilt weit über Consul Template hinaus.
Sie ist überall dort relevant, wo Code Folgendes tut:
Die tiefere Sicherheitseigenschaft ist nicht:
„der Pfadstring sah einmal sicher aus“
Sondern:
das Objekt, das von der sensiblen Operation verwendet wird, muss das Objekt sein, das die Validierung bestanden hat
In diesem Fall validierte Consul Template eine Auflösung und rief eine andere ab.
Diese Lücke war genug.
sandbox_path war eine explizite Sicherheitsgrenze für lokale DateienpathInSandbox() löste das Symlink-Ziel korrekt auf und validierte esFileQuery behielt den ursprünglichen, vom Angreifer beeinflussten Pfad beios.ReadFile auf0.42.0 behob den Bug, indem sie die Abhängigkeit an den aufgelösten, validierten Pfad bandBei dieser Verwundbarkeit ging es nicht um die Umgehung einer String-Präfix-Prüfung.
Es ging um Zeit und Identität.
Consul Template prüfte, wohin der Link während der Template-Auswertung zeigte. Der Abruf der Abhängigkeit stellte dem Dateisystem später dieselbe Frage erneut. Ein Angreifer konnte die Antwort zwischen diesen beiden Momenten ändern.
Deshalb wurde dies zu CVE-2026-5061.
Behoben in consul-template 0.42.0.