net: fix __dst_negative_advice() race
Priorize a correção. Ela está sob exploração confirmada pelo CISA e tem prova de conceito pública.
Apply mitigations per vendor instructions or discontinue use of the product if mitigations are unavailable.
Resumo
Use-after-free no subsistema de rede do kernel Linux, na função __dst_negative_advice(), que é chamada quando o kernel decide que a rota (dst_entry) associada a um socket está degradada e precisa ser descartada. A ordem errada de operações RCU permite que outra thread, lendo concorrentemente o cache de rota do socket, obtenha um ponteiro para uma estrutura já liberada. A falha exige execução local (AV:L) com privilégio baixo para gerar a concorrência necessária — não é explorável remotamente sem já ter presença na máquina — mas está confirmada em exploração ativa (catálogo KEV da CISA) e foi reportada por um pesquisador da Google associado a investigações de spyware, o que eleva sua relevância para escalonamento de privilégios local.
Detalhamento técnico
O kernel mantém em cada struct sock um ponteiro sk_dst_cache para a rota (dst_entry) usada para rotear pacotes daquele socket, protegido por regras RCU: para trocar ou remover esse ponteiro com segurança, é preciso primeiro zerar/reatribuir sk->sk_dst_cache via rcu_assign_pointer() e só depois chamar dst_release() no dst_entry antigo. Isso garante que um leitor concorrente sob rcu_read_lock() nunca veja um ponteiro válido para uma estrutura cujo refcount já foi decrementado a zero.
A implementação de __dst_negative_advice() violava essa ordem: os callbacks de negative_advice (ipv4_negative_advice, ip6_negative_advice, xfrm_negative_advice) chamavam ip_rt_put()/dst_release() sobre o dst antigo dentro do próprio callback e retornavam um novo ponteiro (ou NULL) para a função central, que só então fazia rcu_assign_pointer(sk->sk_dst_cache, ndst). Ou seja, a liberação acontecia antes da atualização do ponteiro visível a outras threads — o inverso do que sk_dst_reset() (a função irmã, que já implementava a ordem correta) fazia. Isso é uma condição de corrida clássica entre um caminho de escrita (invalidação de rota) e um caminho de leitura (uso do dst em outra thread do mesmo socket), classificável como CWE-416 (Use-After-Free) com componente de CWE-362 (race condition).
O bug em si é antigo — existe desde a implementação original do mecanismo negative_advice — mas, segundo o commit de correção, 'se tornou visível' após o commit a87cb3e48ee8 ('net: Facility to report route quality of connected sockets'), que passou a acionar esse caminho de código com sockets UDP em cenários que antes não o exercitavam na prática.
A correção reestrutura a interface: o ponteiro de função dst_ops->negative_advice deixa de retornar um novo dst_entry* e passa a receber o struct sock* diretamente, chamando sk_dst_reset(sk) internamente em cada implementação (IPv4, IPv6, xfrm) sempre que decide invalidar a rota. Isso centraliza a ordem correta de RCU (zera o ponteiro antes de liberar) em um único ponto reaproveitado por todos os callbacks, eliminando a duplicação de lógica que causava a inconsistência.
Como é explorada
O gatilho para __dst_negative_advice() ocorre quando o kernel marca uma rota como degradada — por exemplo, rota obsoleta, marcada como redirecionada (RTCF_REDIRECTED) por um ICMP redirect, ou com cache expirado. Para explorar a race, um atacante precisa de um socket UDP local sob seu controle e de duas operações concorrentes: uma thread que force a invalidação da rota do socket (fazendo o kernel entrar no caminho negative_advice) enquanto outra thread, no mesmo processo ou em thread relacionada, acessa o dst_cache do mesmo socket (por exemplo, enviando dados). A janela de corrida é estreita e depende de timing, mas é uma primitiva clássica de UAF em kernel — controlável com técnicas de heap grooming para tentar realocar a memória liberada com dados controlados pelo atacante, obtendo potencial escrita/leitura arbitrária ou escalonamento de privilégios a partir de um processo sem privilégios.
O vetor é estritamente local: o CVSS (AV:L/AC:L/PR:L/UI:N) reflete que é preciso já ter capacidade de executar código no host com privilégio baixo (criar sockets, gerar tráfego, eventualmente induzir a invalidação de rota), sem depender de interação do usuário. Não há componente de rede remota na exploração — um atacante remoto não consegue disparar a race sem já ter um processo executando na máquina vítima.
A CVE está no catálogo KEV da CISA, indicando exploração confirmada em campo, e foi reportada por um pesquisador da Google (Clement Lecigne) historicamente associado à identificação de bugs usados em cadeias de exploração de spyware/comercial. Isso sugere que a falha foi encontrada como parte de uma cadeia de escalonamento de privilégios local, não como vulnerabilidade explorada isoladamente por scanners em massa — consistente com o EPSS baixo (0.027), que mede probabilidade de exploração oportunista de internet e não captura bem ataques direcionados de escalonamento local.
Versões
Como se proteger
A correção definitiva é atualizar o kernel para uma versão que inclua o commit upstream 92f1655aa2b2294d0b49925f3b875a634bd3b59e ('net: fix __dst_negative_advice() race'). Esse commit foi retroportado para múltiplos ramos estáveis do kernel (visíveis nos hashes de backport listados nas referências); a versão exata de destino depende da distribuição e do ramo LTS em uso — confira o changelog do kernel da sua distribuição para confirmar se o patch já foi incorporado, em vez de assumir por número de versão.
Não existe flag de configuração, módulo a desabilitar ou parâmetro de sysctl que mitigue a falha sem o patch — o código afetado é central ao caminho de roteamento IPv4/IPv6/xfrm de qualquer socket UDP (e potencialmente outros tipos que usam negative_advice), e não pode ser contornado por WAF ou configuração de aplicação, já que é uma falha interna ao kernel. O único controle compensatório real é reduzir a superfície de exposição local: evitar execução de código não confiável (contêineres, VMs, usuários múltiplos) em kernels sem o patch, já que a exploração exige presença local com privilégio baixo.
Mitigações de hardening de kernel como KASLR, SMEP/SMAP ou KASAN não impedem a exploração — no máximo, KASAN ajuda a detectar o UAF em ambientes de teste/debug, mas não é viável em produção pela sobrecarga de desempenho. Não há paliativo de runtime equivalente ao patch; a única ação eficaz é a atualização do kernel.
Como detectar
Não há assinatura de rede ou log de aplicação confiável para detectar tentativas de exploração — a falha é uma condição de corrida interna ao kernel, sem tráfego de rede distintivo. Em ambientes com KASAN habilitado (tipicamente apenas em builds de debug/teste, não produção), a exploração ou o acionamento acidental da race pode gerar relatórios de use-after-free apontando para net/ipv4/route.c, net/ipv6/route.c ou net/xfrm/xfrm_policy.c envolvendo dst_entry. Em produção, o sintoma mais provável de exploração falha ou tentativa é instabilidade do kernel (panics/oops) associada ao subsistema de roteamento sob carga de sockets UDP — sinal indireto e não conclusivo, sem correlação direta a um atacante específico.