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

● CVE-2024-26642 · CVSS 7.8 · Alta
Pesquisa · FlawOpen

CVE-2024-26642: conjunto anônimo com flag de timeout no nf_tables do Linux

CVE-2024-26642 (CVSS 7.8 segundo o NIST): nf_tables_newset() aceitava conjuntos anônimos que também traziam a flag de timeout, uma combinação que nenhuma ferramenta de espaço de usuário cria, expondo conjuntos presos a uma regra à expiração de elementos e à coleta de lixo. O Linux 6.8 a rejeita com -EOPNOTSUPP.

💡 Explicação em Linguagem Simples (ELI5)

Uma chapelaria tem dois sistemas. Os ganchos numerados pertencem a um único convidado por uma noite e são liberados assim que ele vai embora. As fichas com horário são recolhidas a cada hora por um funcionário que retira tudo o que passou do prazo. O formulário permite marcar as duas opções para o mesmo casaco, e então o gancho é liberado quando o convidado sai e também recolhido no horário do funcionário. Duas pessoas acham que cuidam do mesmo gancho, e nenhuma foi treinada para o horário da outra. A correção é simples: o formulário passou a recusar esse par de opções.

Conceitos Centrais e Termos

nf_tables set
Estrutura do kernel com endereços, portas ou outras chaves que as regras do nftables comparam; é criada com uma mensagem netlink NFT_MSG_NEWSET e tratada por nf_tables_newset().
Conjunto anônimo (NFT_SET_ANONYMOUS)
Conjunto sem nome, como { 22, 80 } escrito dentro de uma regra. Fica preso a essa única regra e é destruído junto com ela na mesma transação.
NFT_SET_TIMEOUT
Flag que dá aos elementos do conjunto um prazo de expiração. Os elementos expirados são removidos pelo mecanismo de coleta de lixo do conjunto.
NFT_SET_EVAL
Flag para conjuntos atualizados a partir do caminho dos pacotes, usada pelas instruções meter legadas. A correção mantém anônimo + timeout + eval permitido para elas.
CAP_NET_ADMIN em um user namespace
O nf_tables aceita configuração de qualquer processo com CAP_NET_ADMIN no seu namespace de rede, que um usuário sem privilégios obtém criando um user namespace onde a distribuição permite.

Análise de Causa Raiz

nf_tables_newset() em net/netfilter/nf_tables_api.c verificava as flags do conjunto apenas quanto a bits desconhecidos e aos conflitos MAP+OBJECT e EVAL+OBJECT. Aceitava NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT, de modo que um conjunto cujo tempo de vida está ligado a uma única regra e transação também podia ter elementos que expiram e são recolhidos pela coleta de lixo. O espaço de usuário nunca produz essa combinação, e o código de tempo de vida não foi projetado para ela. A correção upstream a rejeita com -EOPNOTSUPP, a menos que NFT_SET_EVAL também esteja presente. O registro público não detalha o erro de memória resultante; o NIST atribui 7.8 (confidencialidade, integridade e disponibilidade altas) e a CNA do kernel, 5.5 (só disponibilidade).

Fluxo de Ataque Passo a Paso

Passo 1

Obter CAP_NET_ADMIN

Um usuário local cria um user namespace e um namespace de rede (onde user namespaces sem privilégio estão habilitados) e passa a ter CAP_NET_ADMIN sobre o estado do nf_tables desse namespace.

Passo 2

Pedir o conjunto inválido

Dentro de um lote netlink, o processo envia NFT_MSG_NEWSET com NFTA_SET_FLAGS = NFT_SET_ANONYMOUS | NFT_SET_TIMEOUT e liga o conjunto a uma nova regra por meio de uma expressão de lookup.

Passo 3

O kernel aceita

nf_tables_newset() não encontra nenhum par de flags proibido e cria um conjunto anônimo preso à regra cujos elementos têm prazo de expiração.

Passo 4

Caminhos de tempo de vida não testados

Confirmar, abortar ou excluir a regra passa a acionar ao mesmo tempo a destruição do conjunto e a coleta de lixo dos elementos, em caminhos que o código de transações do nf_tables não trata com segurança.

Código-Fonte: Vulnerável vs. Seguro

IMPLEMENTAÇÃO VULNERÁVEL
/* 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 SEGURO E 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 Verificação de Segurança para Engenharia

Fontes