
pedit COW
CVE-2026-46331 (apelidada de “pedit COW”) é uma falha local de escalonamento de privilégio no kernel do Linux no subsistema de controle de tráfego. Um usuário não privilegiado (em um namespace de rede não privilegiado) pode configurar o filtro act_pedit (editor de pacotes) para acionar uma escrita parcial copy-on-write (COW) no cache de páginas. Efetivamente, o kernel escreve dados controlados pelo atacante na imagem em memória de um arquivo sem marcar a página como privada, corrompendo a cópia em cache desse arquivo. Crucialmente, o exploit requer apenas CAP_NET_ADMIN (obtível em um namespace de usuário) e não modifica o arquivo em disco. Na prática, uma prova de conceito (PoC) funcional chamada packet_edit_meme foi publicada em 17 de junho de 2026, demonstrando como sobrescrever a imagem do cache de páginas de um binário setuid (ex.: /bin/su) para obter um shell root. A vulnerabilidade decorre de um cálculo incorreto do intervalo COW em tcf_pedit_act() e foi corrigida upstream (4 de junho de 2026) movendo a verificação da região gravável para dentro do loop por chave.
act_pedit. Versões estáveis sem correção (incluindo muitos kernels de distribuições) são vulneráveis.tc e sobrescreve o ponto de entrada ELF de um binário setuid na memória com shellcode.skb_ensure_writable() para dentro do loop de chaves). Como solução alternativa, bloqueie ou descarregue o módulo act_pedit ou desabilite namespaces de usuário não privilegiados (ex.: sysctl user.max_user_namespaces=0). Após a mitigação, limpe os caches (echo 3 > /proc/sys/vm/drop_caches) para remover quaisquer páginas envenenadas.Este relatório fornece uma análise técnica detalhada do CVE-2026-46331: sua causa, exploração, detecção e estratégias de remediação, com referências a avisos de fornecedores, CVEs e o exploit público.
Definição: CVE-2026-46331 é um bug de escrita fora dos limites no subsistema Traffic Control (net/sched) do kernel do Linux, especificamente na ação act_pedit (editor de pacotes). A função tcf_pedit_act() calcula um intervalo “copy-on-write” para operações de edição de pacotes antes de iterar sobre as chaves tipadas, usando uma dica estática tcfp_off_max_hint. No entanto, algumas chaves (ex.: edições de cabeçalho TCP/UDP) determinam seu deslocamento final de byte apenas em tempo de execução. O código nunca reverifica a capacidade de escrita para esses deslocamentos dinâmicos. Como resultado, escritas podem ocorrer fora da região pré-COW: parte da escrita do pacote nunca é tornada privada, levando a um COW parcial. Essa escrita errônea se propaga para a memória do cache de páginas compartilhada de um arquivo (se os buffers do pacote referenciarem páginas de arquivo), corrompendo a imagem em cache do arquivo.
Contexto: A ação editor de pacotes (pedit) do Linux permite que administradores reescrevam bytes arbitrários dentro de cabeçalhos de pacotes (camadas de enlace, rede ou transporte) à medida que os pacotes atravessam um filtro tc configurado. Funciona especificando um deslocamento (possivelmente ancorado a um cabeçalho) e um valor/máscara de 32 bits. Internamente, pedit opera em socket-buffers (sk_buff) e deve tornar a memória do pacote alvo gravável antes de modificá-la (via skb_ensure_writable() no estilo COW). Idealmente, o kernel deve clonar (cópia privada) quaisquer páginas compartilhadas antes de escrever para evitar alterar memória usada em outro lugar.
Causa Raiz: Em tcf_pedit_act(), o código calcula erroneamente o intervalo gravável apenas uma vez no início, usando tcfp_off_max_hint (o deslocamento estático máximo). Essa dica não inclui nenhum deslocamento de cabeçalho em tempo de execução que as chaves tipadas adicionam quando o pacote está sendo processado. Chaves como TCP ou UDP podem calcular um deslocamento com base na posição do cabeçalho IP em tempo de execução (por exemplo, se uma chave anterior deslocar o cabeçalho de rede). Assim, durante o loop por chave, o deslocamento real para uma chave pode exceder o intervalo que foi pré-alocado como gravável. O código então escreve na memória do pacote via skb_store_bits(), mas como a página além da região pré-COW não foi tornada privada, a escrita corrompe uma página que ainda está compartilhada com o cache de páginas. Em resumo, “calcular o intervalo gravável do pacote muito cedo” causa uma escrita fora dos limites entre páginas. Deslocamentos negativos (ex.: editar cabeçalhos Ethernet na entrada) também são maltratados, e até mesmo offset_valid() carecia de uma proteção para INT_MIN, agravando a falha.
Por Que Isso Acontece: Esse bug é essencialmente um erro de lógica no cálculo do intervalo copy-on-write. O kernel assumiu que o deslocamento estático máximo (conhecido no momento do carregamento) era suficiente para todas as edições. Ele não atualizou o intervalo COW quando chaves com deslocamentos dinâmicos eram realmente aplicadas. Após uma série de edições em fila, a escrita final poderia ficar fora da região pré-verificada. Como os buffers de pacotes podem referenciar páginas de arquivo mapeadas em memória (ex.: via mecanismos de cópia zero), essa escrita “COW parcial” pode alcançar o cache de páginas de um arquivo em disco. Na prática, a ação do editor de pacotes pode receber páginas de um sendfile ou splice; assim, uma única operação de filtro de pacotes pode indiretamente escrever dados escolhidos pelo atacante na imagem em memória de um arquivo, sem alterar o disco.
Componentes e Fluxo de Dados: O código vulnerável reside no subsistema net/sched do Linux (act_pedit.c). Quando um pacote corresponde a uma regra pedit configurada, tcf_pedit_act() é invocada. Internamente, ela chama skb_ensure_writable(skb, X) exatamente uma vez, onde X = tcfp_off_max_hint. Isso torna os primeiros X bytes do pacote privados (COW). Então, em um loop sobre cada chave (operação de edição), ela calcula o deslocamento real da chave somando o deslocamento do cabeçalho em tempo de execução ao deslocamento especificado da chave, e escreve um valor de 32 bits no pacote. Em pseudocódigo:```c
u32 off_max = action->tcfp_off_max_hint;
skb_ensure_writable(skb, off_max);
for (i = 0; i < num_keys; i++) {
u32 hdr_off = compute_header_offset(skb, key[i].hdr_type);
u32 write_off = hdr_off + key[i].offset;
skb_store_bits(skb, write_off, &key[i].value, 4);
}
Porque `hdr_off` só é calculado ao processar cada chave, a chamada inicial `skb_ensure_writable()` não levou isso em conta. Se `hdr_off + key[i].offset` exceder `off_max`, o código recorre a `skb_store_bits()` em fragmentos em vez da área linear principal, ou seja, escreve em uma página que não foi tornada privada. Esse é o ponto de falha.