flawopen.com/Teardowns/cve-2024-5321-kubernetes-windows-container-log-permissions

● CVE-2024-5321 · CVSS 6.1 · Moyenne
Recherche · FlawOpen

CVE-2024-5321 : permissions des journaux de conteneurs sur les nœuds Windows de Kubernetes

CVE-2024-5321 (CVSS 6.1, moyenne) : sur les nœuds Windows, le kubelet créait les répertoires de journaux des pods avec os.MkdirAll, qui ignore le mode de permissions sous Windows ; les journaux héritaient d'une ACL permettant à BUILTIN\Users de les lire et à Authenticated Users de les modifier.

💡 Explication en Langage Simple (ELI5)

Un hôpital range le dossier de chaque patient dans une armoire de classement. Quand le personnel commande une nouvelle armoire, il écrit « médecins uniquement » sur le bon de commande, mais dans ce bâtiment la case serrure est ignorée sans prévenir : l'armoire reçoit la règle par défaut de la pièce, où tout employé peut lire et toute personne munie d'un badge peut écrire. Le correctif, c'est un agent qui, à chaque nouvelle armoire, pose sa propre serrure et supprime pour ce meuble la règle par défaut de la pièce.

Concepts Clés et Termes

DACL / ACE
Liste de contrôle d'accès discrétionnaire de Windows : les entrées qui indiquent quels comptes peuvent lire, écrire ou exécuter un fichier ou un répertoire.
Héritage d'ACL
Par défaut, un nouveau fichier ou répertoire Windows copie les entrées héritables de la DACL de son parent. Une DACL protégée l'en empêche.
BUILTIN\Users / Authenticated Users
Groupes Windows très larges : tous les comptes locaux, et tous les comptes qui se sont déjà connectés. Kubernetes associe à BUILTIN\Users les conteneurs exécutés en ContainerUser lorsqu'ils accèdent aux fichiers de l'hôte.
os.MkdirAll sous Windows
os.MkdirAll(path, perm) en Go n'utilise perm que sous Unix. Sous Windows, le mode est ignoré et le répertoire reçoit l'ACL héritée.

Analyse de Cause Racine

setupDataDirs() dans pkg/kubelet/kubelet.go créait la racine des journaux des pods (C:\var\log\pods) avec os.MkdirAll(dir, 0750). Go ignore le mode sous Windows : le répertoire et les journaux écrits dedans héritaient de l'ACL du parent, qui donnait la lecture à BUILTIN\Users et la modification à NT AUTHORITY\Authenticated Users. La PR #126104 a ajouté à pkg/util/filesystem des fonctions MkdirAll() et Chmod() qui traduisent le mode Unix en DACL explicite appliquée avec PROTECTED_DACL_SECURITY_INFORMATION, et y a basculé les répertoires de journaux, de plugins et de checkpoints du kubelet.

Déroulement de l'Attaque Étape par Étape

Étape 1

Le kubelet crée la racine des journaux

Sur un nœud Windows, setupDataDirs() appelle os.MkdirAll(kl.getPodLogsDir(), 0750). Le 0750 est ignoré et le répertoire hérite de l'ACL de son parent.

Étape 2

Des groupes larges héritent de l'accès

Les entrées héritées donnent la lecture à BUILTIN\Users et la modification à NT AUTHORITY\Authenticated Users sur les répertoires de journaux des pods et les fichiers que le runtime y écrit.

Étape 3

Lecture des journaux des autres charges

N'importe quel compte local peu privilégié du nœud, ou une charge qui monte le chemin des journaux via hostPath, lit le stdout et le stderr des autres pods, qui contiennent souvent des jetons, des chaînes de connexion ou des données clients.

Étape 4

Altération des preuves

Les mêmes comptes peuvent modifier ou tronquer les journaux avant leur collecte par l'agent d'envoi, masquant l'activité à la journalisation centralisée.

Code Source : Vulnérable vs Sécurisé

IMPLÉMENTATION VULNÉRABLE
// 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
}
PATCH SÉCURISÉ ET ROBUSTE
// 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)
}

Liste de Contrôle de Sécurité pour l'Ingénierie

Sources