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

● CVE-2024-26642 · CVSS 7.8 · Alta
Investigación · FlawOpen

CVE-2024-26642: conjunto anónimo con flag de timeout en nf_tables de Linux

CVE-2024-26642 (CVSS 7.8 según el NIST): nf_tables_newset() aceptaba conjuntos anónimos que además llevaban la flag de timeout, una combinación que ninguna herramienta de espacio de usuario crea, lo que exponía conjuntos ligados a una regla a la expiración de elementos y a la recolección de basura. Linux 6.8 la rechaza con -EOPNOTSUPP.

💡 Explicación en Lenguaje Sencillo (ELI5)

Un guardarropa tiene dos sistemas. Los ganchos numerados pertenecen a un solo invitado durante una noche y se vacían en cuanto se marcha. Los tiques con hora los recorre cada hora un empleado que retira todo lo caducado. El formulario permite marcar ambas casillas para el mismo abrigo, así que el gancho se vacía cuando el invitado se va y además en la ronda del empleado. Dos personas creen encargarse del mismo gancho y ninguna está preparada para el horario de la otra. La solución es sencilla: el formulario ya no acepta ese par de casillas.

Conceptos Clave y Términos

nf_tables set
Estructura del kernel con direcciones, puertos u otras claves que comparan las reglas de nftables; se crea con un mensaje netlink NFT_MSG_NEWSET y la gestiona nf_tables_newset().
Conjunto anónimo (NFT_SET_ANONYMOUS)
Conjunto sin nombre, como { 22, 80 } escrito dentro de una regla. Está ligado a esa única regla y se destruye con ella en la misma transacción.
NFT_SET_TIMEOUT
Flag que da a los elementos del conjunto una fecha de caducidad. Los elementos caducados los elimina el mecanismo de recolección de basura del conjunto.
NFT_SET_EVAL
Flag para conjuntos que se actualizan desde la ruta de paquetes, usada por las sentencias meter heredadas. La corrección mantiene permitido anónimo + timeout + eval para ellas.
CAP_NET_ADMIN en un user namespace
nf_tables acepta configuración de cualquier proceso con CAP_NET_ADMIN en su namespace de red, que un usuario sin privilegios obtiene creando un user namespace donde la distribución lo permite.

Análisis de Causa Raíz

nf_tables_newset() en net/netfilter/nf_tables_api.c solo comprobaba en las flags del conjunto los bits desconocidos y los conflictos MAP+OBJECT y EVAL+OBJECT. Aceptaba NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT, de modo que un conjunto cuya vida está ligada a una sola regla y transacción también podía tener elementos que caducan y recoge la recolección de basura. El espacio de usuario nunca produce esa combinación y el código de ciclo de vida no se diseñó para ella. La corrección upstream la rechaza con -EOPNOTSUPP salvo que también esté NFT_SET_EVAL. El registro público no detalla el error de memoria resultante; el NIST le da 7.8 (confidencialidad, integridad y disponibilidad altas) y la CNA del kernel, 5.5 (solo disponibilidad).

Flujo de Ataque Paso a Paso

Paso 1

Obtener CAP_NET_ADMIN

Un usuario local crea un user namespace y un namespace de red (donde los user namespaces sin privilegios están habilitados) y obtiene así CAP_NET_ADMIN sobre el estado de nf_tables de ese namespace.

Paso 2

Pedir el conjunto no válido

Dentro de un lote netlink, el proceso envía NFT_MSG_NEWSET con NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT y liga el conjunto a una regla nueva mediante una expresión de lookup.

Paso 3

El kernel lo acepta

nf_tables_newset() no encuentra ningún par de flags prohibido y crea un conjunto anónimo ligado a la regla cuyos elementos tienen caducidad.

Paso 4

Rutas de ciclo de vida sin probar

Confirmar, abortar o borrar la regla pone en marcha a la vez la destrucción del conjunto y la recolección de basura de elementos, en rutas que el código de transacciones de nf_tables no maneja de forma segura.

Código Fuente: Vulnerable vs. Seguro

IMPLEMENTACIÓN VULNERABLE
/* 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. */
	}
PARCHE SEGURO Y ROBUSTO
/* 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;
	}

Lista de Verificación de Seguridad para Ingeniería

Fuentes