flawopen.com/Teardowns/cve-2024-26642-linux-nftables-anonymous-set-timeout

● CVE-2024-26642 · CVSS 7.8 · Haute
Recherche · FlawOpen

CVE-2024-26642 : ensemble anonyme avec drapeau de timeout dans nf_tables de Linux

CVE-2024-26642 (CVSS 7.8 selon le NIST) : nf_tables_newset() acceptait des ensembles anonymes portant aussi le drapeau de timeout, une combinaison qu'aucun outil de l'espace utilisateur ne crée, ce qui exposait des ensembles liés à une règle à l'expiration d'éléments et au ramasse-miettes. Linux 6.8 la refuse avec -EOPNOTSUPP.

💡 Explication en Langage Simple (ELI5)

Un vestiaire a deux systèmes. Les crochets numérotés appartiennent à un seul invité pour une soirée et sont libérés dès qu'il part. Les tickets horodatés sont ramassés chaque heure par un employé qui retire tout ce qui a expiré. Le formulaire permet de cocher les deux cases pour le même manteau : le crochet est alors libéré au départ de l'invité et aussi lors de la tournée de l'employé. Deux personnes pensent gérer le même crochet et aucune n'a été formée au planning de l'autre. Le correctif est simple : le formulaire refuse désormais cette paire de cases.

Concepts Clés et Termes

nf_tables set
Structure du noyau contenant des adresses, ports ou autres clés que les règles nftables comparent ; créée par un message netlink NFT_MSG_NEWSET et traitée par nf_tables_newset().
Ensemble anonyme (NFT_SET_ANONYMOUS)
Ensemble sans nom, comme { 22, 80 } écrit directement dans une règle. Il est lié à cette seule règle et détruit avec elle dans la même transaction.
NFT_SET_TIMEOUT
Drapeau qui donne aux éléments de l'ensemble une date d'expiration. Les éléments expirés sont retirés par le ramasse-miettes de l'ensemble.
NFT_SET_EVAL
Drapeau des ensembles mis à jour depuis le chemin des paquets, utilisé par les instructions meter historiques. Le correctif garde anonyme + timeout + eval autorisé pour elles.
CAP_NET_ADMIN dans un user namespace
nf_tables accepte la configuration de tout processus détenant CAP_NET_ADMIN dans son namespace réseau, ce qu'un utilisateur non privilégié obtient en créant un user namespace là où la distribution le permet.

Analyse de Cause Racine

nf_tables_newset() dans net/netfilter/nf_tables_api.c ne vérifiait dans les drapeaux de l'ensemble que les bits inconnus et les conflits MAP+OBJECT et EVAL+OBJECT. Elle acceptait NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT : un ensemble dont la durée de vie est liée à une seule règle et à une transaction pouvait donc aussi avoir des éléments qui expirent et sont récupérés par le ramasse-miettes. L'espace utilisateur ne produit jamais cette combinaison et le code de gestion de durée de vie n'a pas été conçu pour elle. Le correctif amont la refuse avec -EOPNOTSUPP, sauf si NFT_SET_EVAL est aussi présent. Le dossier public ne détaille pas l'erreur mémoire qui en découle ; le NIST la note 7.8 (confidentialité, intégrité et disponibilité élevées), la CNA du noyau 5.5 (disponibilité seule).

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

Étape 1

Obtenir CAP_NET_ADMIN

Un utilisateur local crée un user namespace et un namespace réseau (là où les user namespaces non privilégiés sont activés) et détient ainsi CAP_NET_ADMIN sur l'état nf_tables de ce namespace.

Étape 2

Demander l'ensemble invalide

Dans un lot netlink, le processus envoie NFT_MSG_NEWSET avec NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT et lie l'ensemble à une nouvelle règle via une expression de lookup.

Étape 3

Le noyau l'accepte

nf_tables_newset() ne trouve aucune paire de drapeaux interdite et crée un ensemble anonyme lié à la règle, dont les éléments portent une date d'expiration.

Étape 4

Chemins de durée de vie non testés

Valider, annuler ou supprimer la règle déclenche alors à la fois la destruction de l'ensemble et le ramassage des éléments, dans des chemins que le code transactionnel de nf_tables ne gère pas de façon sûre.

Code Source : Vulnérable vs Sécurisé

IMPLÉMENTATION VULNÉRABLE
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), before the fix */
	if (nla[NFTA_SET_FLAGS] != NULL) {
		flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
		if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
			      NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
			      NFT_SET_MAP | NFT_SET_EVAL |
			      NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
			return -EOPNOTSUPP;
		/* Only one of these operations is supported */
		if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
			     (NFT_SET_MAP | NFT_SET_OBJECT))
			return -EOPNOTSUPP;
		if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
			     (NFT_SET_EVAL | NFT_SET_OBJECT))
			return -EOPNOTSUPP;
		/* BUG: NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT is accepted. A rule-bound
		 * anonymous set now also gets per-element expiry and garbage
		 * collection, a combination nft never creates and the set
		 * lifetime code was not written for. */
	}
PATCH SÉCURISÉ ET ROBUSTE
/* net/netfilter/nf_tables_api.c: nf_tables_newset(), commit 16603605b667 */
	if (nla[NFTA_SET_FLAGS] != NULL) {
		flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS]));
		if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
			      NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
			      NFT_SET_MAP | NFT_SET_EVAL |
			      NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
			return -EOPNOTSUPP;
		/* Only one of these operations is supported */
		if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
			     (NFT_SET_MAP | NFT_SET_OBJECT))
			return -EOPNOTSUPP;
		if ((flags & (NFT_SET_EVAL | NFT_SET_OBJECT)) ==
			     (NFT_SET_EVAL | NFT_SET_OBJECT))
			return -EOPNOTSUPP;
		/* FIX: anonymous sets are never used with timeouts from userspace,
		 * so reject the combination before the set exists. NFT_SET_EVAL
		 * stays allowed so legacy meters keep working. */
		if ((flags & (NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT | NFT_SET_EVAL)) ==
			     (NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT))
			return -EOPNOTSUPP;
	}

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

Sources