flawopen.com/Teardowns/cve-2024-26642-linux-nftables-anonymous-set-timeout
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.
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_NEWSETe tratada pornf_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
meterlegadas. 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
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.
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.
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.
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
/* 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;
}
Lista de Verificação de Segurança para Engenharia
- ✓Use um kernel que contenha o commit 16603605b667 (Linux 6.8 ou um kernel estável ou de distribuição cujo changelog cite a
CVE-2024-26642). - ✓Onde as cargas não precisarem, desative user namespaces sem privilégio (
kernel.no Debian e Ubuntu,unprivileged_userns_clone=0user.ou a restrição de userns do AppArmor no Ubuntu) para que usuários locais não alcancem o nf_tables.max_user_namespaces=0 - ✓Em hosts que não usam nftables, impeça o carregamento do módulo
nf_tables. - ✓Em código de kernel e de API, expresse as combinações de flags aceitas como uma lista explícita de permissões e rejeite tudo o que nenhum cliente suportado produz, em vez de rejeitar só os pares ruins conhecidos.
- ✓Faça fuzzing das interfaces netlink com descrições do syzkaller que cubram todos os bits de flag, para que combinações incomuns atinjam o código de tempo de vida dos objetos primeiro nos testes.