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

● CVE-2024-26642 · CVSS 7.8 · Высокий
Исследования · FlawOpen

CVE-2024-26642: анонимное множество с флагом таймаута в nf_tables Linux

CVE-2024-26642 (CVSS 7.8 по оценке NIST): nf_tables_newset() принимала анонимные множества с флагом таймаута — сочетание, которое не создаёт ни один инструмент пространства пользователя, — и тем самым подвергала множества, привязанные к правилу, истечению элементов и сборке мусора. Linux 6.8 отклоняет его с -EOPNOTSUPP.

💡 Простыми словами (ELI5)

В гардеробе две системы. Номерные крючки принадлежат одному гостю на один вечер и освобождаются, как только он уходит. Талоны с временем каждый час обходит служащий и убирает всё просроченное. Бланк позволяет отметить обе галочки для одного пальто: крючок освобождается, когда гость уходит, и ещё раз — по графику служащего. Двое считают, что отвечают за один и тот же крючок, и никто не знает расписания другого. Исправление простое: бланк теперь не принимает эту пару галочек.

Ключевые понятия и термины

nf_tables set
Структура ядра с адресами, портами или другими ключами, с которыми сравнивают правила nftables; создаётся netlink-сообщением NFT_MSG_NEWSET и обрабатывается nf_tables_newset().
Анонимное множество (NFT_SET_ANONYMOUS)
Безымянное множество вроде { 22, 80 }, записанное прямо в правиле. Оно привязано к этому единственному правилу и уничтожается вместе с ним в той же транзакции.
NFT_SET_TIMEOUT
Флаг, задающий элементам множества срок жизни. Истёкшие элементы удаляет механизм сборки мусора множества.
NFT_SET_EVAL
Флаг для множеств, обновляемых из пути обработки пакетов; используется устаревшими инструкциями meter. Исправление оставляет для них сочетание анонимное + таймаут + eval допустимым.
CAP_NET_ADMIN в user namespace
nf_tables принимает настройку от любого процесса с CAP_NET_ADMIN в его сетевом namespace; непривилегированный пользователь получает это право, создав user namespace, если дистрибутив это разрешает.

Анализ первопричины

nf_tables_newset() в net/netfilter/nf_tables_api.c проверяла флаги множества только на неизвестные биты и конфликты MAP+OBJECT и EVAL+OBJECT. Она принимала NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT, поэтому множество, чья жизнь привязана к одному правилу и одной транзакции, могло ещё и иметь элементы с истекающим сроком, которые собирает сборщик мусора. Пространство пользователя такого сочетания не создаёт, и код управления временем жизни на него не рассчитан. Исправление в основной ветке отклоняет его с -EOPNOTSUPP, если не задан ещё и NFT_SET_EVAL. Публичные источники не описывают возникающую ошибку памяти подробно; NIST оценивает её в 7.8 (высокие конфиденциальность, целостность и доступность), CNA ядра — в 5.5 (только доступность).

Пошаговый сценарий атаки

Шаг 1

Получить CAP_NET_ADMIN

Локальный пользователь создаёт user namespace и сетевой namespace (где разрешены непривилегированные user namespaces) и получает CAP_NET_ADMIN над состоянием nf_tables этого namespace.

Шаг 2

Запросить недопустимое множество

В пакете netlink-сообщений процесс отправляет NFT_MSG_NEWSET с NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT и привязывает множество к новому правилу через выражение lookup.

Шаг 3

Ядро соглашается

nf_tables_newset() не находит запрещённой пары флагов и создаёт привязанное к правилу анонимное множество, элементы которого имеют срок жизни.

Шаг 4

Непроверенные пути времени жизни

Фиксация, откат или удаление правила теперь одновременно запускают разрушение множества и сборку мусора элементов — на путях, которые транзакционный код nf_tables не обрабатывает безопасно.

Исходный код: Уязвимый vs Защищённый вариант

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
/* 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. */
	}
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
/* 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;
	}

Чек-лист по защите системы для инженеров

Источники