
POC de exploit para CVE_2026_31431
Detector e prova de conceito de escalonamento local de privilégios (LPE) para a falha de escrita scratch no page cache do Linux envolvendo algif_aead / authencesn, divulgada em 29/04/2026.
Artigo de divulgação: https://xint.io/blog/copy-fail-linux-distributions
Use apenas em hosts que você possui ou em que tenha autorização explícita para avaliar. O LPE modifica o estado em memória (page cache), mas a técnica é uma escalada de privilégios real — executá-lo em sistemas sem autorização é ilegal na maioria das jurisdições.
algif_aead executa operações AEAD in-place (req->src == req->dst). Quando os dados de origem são fornecidos via splice() a partir de um arquivo regular, a scatterlist de destino contém referências às páginas do page cache do arquivo — ou seja, o kernel escreverá nelas. O algoritmo authencesn(hmac(sha256), cbc(aes)) executa então uma escrita "scratch" de 4 bytes do campo seqno_lo do AAD (bytes 4–7 do AAD fornecido via sendmsg) nesse destino, corrompendo a cópia do arquivo no page cache.
Como o arquivo em disco nunca é modificado, não há assinatura em disco; a corrupção é observada apenas por leitores que compartilham o page cache. /etc/passwd e /usr/bin/su são legíveis por qualquer usuário (world-readable), de modo que um usuário local sem privilégios pode corromper a visão do kernel em execução sobre qualquer um deles.
Afetados: kernels que contêm o commit 72548b093ee3 (AEAD in-place, 2017) sem a reversão upstream. A divulgação confirmou Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3 e SUSE 16, mas a primitiva subjacente é anterior a esse intervalo.
| Arquivo | Finalidade |
|---|---|
test_cve_2026_31431.py | Detector não destrutivo. Opera em um arquivo sentinela em um diretório temporário; nunca toca em binários do sistema. |
exploit_cve_2026_31431.py | LPE. Altera o UID do usuário em execução para 0 no page cache do /etc/passwd e então invoca su para obter um shell root. |
Ambos os scripts são Python 3.10+ puro, apenas stdlib.
# 1. Detectar
python3 test_cve_2026_31431.py
# exit 0 = não vulnerável, 2 = vulnerável, 1 = erro de teste
# 2. Exploit (interativo — o su solicitará a sua senha)
python3 exploit_cve_2026_31431.py --shell
python3 test_cve_2026_31431.py
O que ele faz:
AF_ALG e o algoritmo authencesn(hmac(sha256),cbc(aes)) são acessíveis a partir de um processo sem privilégios.sendmsg+cmsg com seqno_lo definido como o marcador PWND e então aplica os.splice() em 32 bytes da página do page cache do sentinela para o socket de operação AF_ALG.recv() para conduzir a descriptografia. A verificação de autenticação falha com EBADMSG; a escrita scratch ocorre mesmo assim.Classes de saída:
Precondition not met — AF_ALG ou authencesn indisponível. Exit 0.VULNERABLE to CVE-2026-31431 — o marcador PWND foi gravado na página passada por splice. Exit 2.Page cache MODIFIED via in-place AEAD splice path — a página foi gravada, mas o marcador não foi gravado na posição esperada. Trate como vulnerável. Exit 2.Page cache intact — corrigido. Exit 0.O detector nunca toca em /usr/bin/su, /etc/passwd ou qualquer outro arquivo fora do diretório temporário que ele cria, e esse arquivo é removido ao sair.
python3 exploit_cve_2026_31431.py # apenas aplica o patch, exibe os próximos passos
python3 exploit_cve_2026_31431.py --shell # aplica o patch e executa `su <user>`
O que ele faz:
/etc/passwd e encontra o deslocamento em bytes do campo UID de 4 caracteres.write4 nesse deslocamento, substituindo o UID por 0000.pwd.getpwnam(user) para confirmar que a libc agora informa UID 0.--shell, executa execvp("su", ["su", user]). Digite sua própria senha. O PAM valida contra /etc/shadow (inalterado) e então setuid(getpwnam(user).pw_uid) resulta em 0.write4 de acordo.nscd, sssd, systemd-userdbd) pode estar mascarando as leituras de /etc/passwd. Se getpwnam ainda retornar o UID real após o patch, reinicie ou ignore o cache, ou escolha um usuário diferente./etc/passwd deve permanecer no cache entre o patch e a execução do su. Na prática, isso é confiável em qualquer sistema com pressão de memória normal.O /etc/passwd em disco permanece inalterado.
Dry-run (exploit_cve_2026_31431.py sem --shell) remove automaticamente a página corrompida do cache ao sair via POSIX_FADV_DONTNEED, de modo que as consultas UID→nome voltam ao normal imediatamente.
Após --shell, a página permanece corrompida até você limpá-la. Enquanto estiver corrompida, qualquer coisa que resolva UID 1000 → nome (ex.: ls, gerenciadores de arquivos, verificações de propriedade em scp/sftp) falhará ou mostrará IDs numéricos. Para limpar:
# sem privilégios - solicita a remoção da página do page cache do /etc/passwd:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); \
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
# a partir do shell root:
echo 3 > /proc/sys/vm/drop_caches
Uma reinicialização também limpa isso.
write4 funcionasendmsg([8-byte AAD], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd) # EBADMSG; a escrita scratch já ocorreu
Os 4 bytes das posições 4–7 do AAD (seqno_lo) são gravados por authencesn na scatterlist de destino, que neste caminho de código é a página do page cache da qual fizemos splice a partir de target_fd. O offset resultante dentro da página corresponde ao offset_src que passamos para splice().
Até que o kernel corrigido chegue à sua distribuição:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
Após a aplicação, test_cve_2026_31431.py deve informar Precondition not met e sair com exit 0.
A correção upstream reverte as operações AEAD in-place para out-of-place, mantendo as páginas do page cache fora de scatterlists graváveis.