flawopen.com/Teardowns/cve-2024-5321-kubernetes-windows-container-log-permissions
CVE-2024-5321: Berechtigungen der Container-Logs auf Windows-Knoten in Kubernetes
CVE-2024-5321 (CVSS 6.1, mittel): Auf Windows-Knoten legte das Kubelet die Pod-Log-Verzeichnisse mit os.MkdirAll an, das unter Windows den Berechtigungsmodus ignoriert; die Logs erbten eine ACL, die BUILTIN\Users Lesen und Authenticated Users Ändern erlaubte.
Ein Krankenhaus bewahrt jede Patientenakte in einem Aktenschrank auf. Wer einen neuen Schrank bestellt, schreibt „nur Ärzte“ aufs Bestellformular – doch in diesem Gebäude wird das Schloss-Feld stillschweigend ignoriert, und der Schrank übernimmt die Standardregel des Raums: Jeder Angestellte darf lesen, jeder mit Ausweis darf hineinschreiben. Die Lösung ist ein Mitarbeiter, der jedem neuen Schrank ein eigenes Schloss einbaut und für diesen Schrank die Raumregel außer Kraft setzt.
Kernkonzepte & Begriffe
DACL / ACE- Die Discretionary Access Control List von Windows: die Einträge, die festlegen, welche Konten eine Datei oder ein Verzeichnis lesen, schreiben oder ausführen dürfen.
ACL-Vererbung- Standardmäßig übernimmt eine neue Windows-Datei bzw. ein neues Verzeichnis die vererbbaren Einträge der DACL des übergeordneten Verzeichnisses. Eine geschützte DACL verhindert das.
BUILTIN\Users / Authenticated Users- Breite Windows-Gruppen: alle lokalen Konten bzw. alle Konten, die sich überhaupt angemeldet haben. Kubernetes ordnet Container, die als ContainerUser laufen, beim Zugriff auf Host-Dateien BUILTIN\Users zu.
os.MkdirAll unter Windows- Gos
os.MkdirAll(path, perm)wertetpermnur unter Unix aus. Unter Windows wird der Modus ignoriert und das Verzeichnis erhält die geerbte ACL.
Ursachenanalyse
setupDataDirs() in pkg/kubelet/kubelet.go legte das Pod-Log-Stammverzeichnis (C:\var\log\pods) mit os.MkdirAll(dir, 0750) an. Go ignoriert den Modus unter Windows, daher erbten das Verzeichnis und die darin geschriebenen Logdateien die ACL des Elternverzeichnisses, die BUILTIN\Users Lese- und NT AUTHORITY\Authenticated Users Änderungsrechte gab. PR #126104 ergänzte in pkg/util/filesystem die Funktionen MkdirAll() und Chmod(), die den Unix-Modus in eine explizite DACL übersetzen und mit PROTECTED_DACL_SECURITY_INFORMATION setzen, und stellte die Log-, Plugin- und Checkpoint-Verzeichnisse des Kubelets darauf um.
Schritt-für-Schritt Angriffsablauf
Das Kubelet legt das Log-Verzeichnis an
Auf einem Windows-Knoten ruft setupDataDirs() os.MkdirAll( auf. Die kl.getPodLogsDir(), 0750)0750 wird ignoriert, das Verzeichnis erbt die ACL des Elternverzeichnisses.
Breite Gruppen erben Zugriff
Die geerbten Einträge geben BUILTIN\Users Lesezugriff und NT AUTHORITY\Authenticated Users Änderungszugriff auf die Pod-Log-Verzeichnisse und die Dateien, die die Runtime dort schreibt.
Fremde Workload-Logs lesen
Jedes niedrig privilegierte lokale Konto auf dem Knoten oder ein Workload, der den Log-Pfad per hostPath einbindet, liest stdout und stderr anderer Pods – oft mit Tokens, Verbindungszeichenfolgen oder Kundendaten.
Beweise manipulieren
Dieselben Konten können Logdateien bearbeiten oder kürzen, bevor ein Log-Shipper sie einsammelt, und so Aktivitäten vor dem zentralen Logging verbergen.
Quellcode: Verwundbar vs. Sicher
// pkg/kubelet/kubelet.go (kubelet v1.30.2)
func (kl *Kubelet) setupDataDirs() error {
if err := os.MkdirAll(kl.getRootDir(), 0750); err != nil {
return fmt.Errorf("error creating root directory: %v", err)
}
// BUG on Windows: os.MkdirAll ignores the 0750 mode. The directory inherits
// its parent's ACL, which lets BUILTIN\Users read and
// NT AUTHORITY\Authenticated Users modify every pod's log files.
if err := os.MkdirAll(kl.getPodLogsDir(), 0750); err != nil {
return fmt.Errorf("error creating pod logs root directory %q: %w", kl.getPodLogsDir(), err)
}
if err := os.MkdirAll(kl.getPodsDir(), 0750); err != nil {
return fmt.Errorf("error creating pods directory: %v", err)
}
return nil
}
// pkg/kubelet/kubelet.go (fixed in v1.30.3, PR #126104)
func (kl *Kubelet) setupDataDirs() error {
if err := os.MkdirAll(kl.getRootDir(), 0750); err != nil {
return fmt.Errorf("error creating root directory: %v", err)
}
// FIX: utilfs.MkdirAll is plain os.MkdirAll on Linux. On Windows it also
// calls utilfs.Chmod, which replaces the inherited ACL with an explicit one.
if err := utilfs.MkdirAll(kl.getPodLogsDir(), 0750); err != nil {
return fmt.Errorf("error creating pod logs root directory %q: %w", kl.getPodLogsDir(), err)
}
if err := os.MkdirAll(kl.getPodsDir(), 0750); err != nil {
return fmt.Errorf("error creating pods directory: %v", err)
}
return nil
}
// pkg/util/filesystem/util_windows.go
func MkdirAll(path string, perm os.FileMode) error {
if err := os.MkdirAll(path, perm); err != nil {
return fmt.Errorf("Error creating directory %s: %v", path, err)
}
return Chmod(path, perm)
}
func Chmod(path string, filemode os.FileMode) error {
// Maps owner, group and other mode bits to ACEs for the file's owner SID,
// its group SID and BUILTIN\Users (BU). For 0750 the result is
// "D:(A;OICI;FA;;;<owner>)(A;OICI;FRFX;;;<group>)(A;OICI;;;;BU)".
// (Abridged: the real function builds this string inline.)
dacl, err := daclForMode(path, filemode)
if err != nil {
return err
}
newSD, err := windows.SecurityDescriptorFromString(dacl)
if err != nil {
return fmt.Errorf("Error creating new security descriptor from DACL string: %v", err)
}
newDACL, _, err := newSD.DACL()
if err != nil {
return fmt.Errorf("Error getting DACL from new security descriptor: %v", err)
}
// PROTECTED_DACL_SECURITY_INFORMATION blocks inheritance from the parent,
// so the BUILTIN\Users and Authenticated Users entries no longer apply.
return windows.SetNamedSecurityInfo(path, windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
nil, nil, newDACL, nil)
}
Checkliste für Engineering & Systemsicherheit
- ✓Das Kubelet auf allen Windows-Knoten auf 1.30.3, 1.29.7, 1.28.12, 1.27.16 oder neuer aktualisieren.
- ✓Windows-Knoten mit
kubectl get nodes -l kubernetes.io/auflisten und auf jedemos=windowsicacls C:\var\log\podsausführen: Kein Eintrag für BUILTIN\Users oder Authenticated Users darf Lesen oder Ändern erlauben. - ✓In Go-Code, der unter Windows läuft, nie auf das
perm-Argument vonos.Mkdir,os.MkdirAll,os.WriteFileoderos.Chmodvertrauen; eine geschützte DACL mitwindowssetzen und in einem Windows-CI-Job prüfen..SetNamedSecurityInfo() - ✓
C:\var\logund andere Log-Pfade des Knotens nicht per hostPath in Workloads einbinden, außer bei einem vertrauenswürdigen Log-Agenten. - ✓Secrets aus stdout und stderr von Containern heraushalten und Tokens im Anwendungslogging maskieren, weil Log-Verzeichnisse des Knotens für mehr Beteiligte lesbar sind als der Pod selbst.