
Una explicación estructurada de CVE-2026-31431 (Copy Fail), que conecta los tres cambios del kernel que introdujeron la vulnerabilidad y permitieron su explotación.
Un fallo lógico en authencesn encadenado a través de AF_ALG y splice() hasta una escritura controlada de 4 bytes en el page cache de cualquier archivo legible del sistema. Sin condición de carrera, sin offsets, sin payload compilado. El mismo script de 732 bytes obtiene root en todas las distribuciones de Linux desde 2017.
CVE-2026-31431 - Copy Fail es un fallo lógico en la plantilla criptográfica authencesn del kernel de Linux. Permite a un usuario local sin privilegios realizar una escritura controlada de 4 bytes en el page cache de cualquier archivo legible del sistema, sin modificar el archivo en disco.
El fallo no existe en ninguno de los tres componentes individualmente. Surge de su interacción:``` 2011 ────────────────────────────────────────────────────────────────────── - authencesn added to the kernel (a5079d084f8b). - Uses the caller's destination scatterlist as scratch space. - Reorder ESN bytes before HMAC computation. - Only caller: internal xfrm layer. Harmless.
2015 ────────────────────────────────────────────────────────────────────── - algif_aead.c gains AEAD support with splice() path (104880a6b470). - splice() can deliver page cache pages to the TX scatterlist. - AF_ALG uses out-of-place operation: req->src != req->dst. - Page cache pages remain read-only. Not exploitable.
2017 ────────────────────────────────────────────────────────────────────── - In-place optimization in algif_aead.c (72548b093ee3). - Copies AAD+CT to RX buffer but chains authentication tag pages via sg_chain(). - Sets req->src = req->dst. - Page cache pages now reside in WRITABLE dst. - authencesn writes past boundary → page cache corruption.
2026 ────────────────────────────────────────────────────────────────────── - Copy Fail - CVE-2026-31431. Discovered by Theori / Xint Code. - Exploitable across all distros since 2017.
---
---
---
<div id='root-cause'/>
## ***🧬 Análisis de causa raíz***
<div id='primitive'/>
### ***La primitiva AF_ALG + splice()***
AF_ALG (*[AF_ALG = 38](https://docs.kernel.org/crypto/userspace-if.html#user-space-api-general-remarks)*) es un tipo de socket que expone la API criptográfica del kernel al espacio de usuario no privilegiado. Un proceso no privilegiado puede:
1. Abrir un socket AF_ALG / SOCK_SEQPACKET.
2. Hacer bind() a cualquier plantilla AEAD disponible expuesta por la API criptográfica del kernel.
3. Establecer una clave criptográfica mediante setsockopt(SOL_ALG, ALG_SET_KEY, ...) en el algoritmo configurado.
4. Llamar a accept() para obtener un socket de operación dedicado que manejará las solicitudes de cifrado y descifrado
5. Enviar datos especialmente diseñados mediante sendmsg() y recibir el resultado procesado a través de recvmsg(), interactuando plenamente con el subsistema criptográfico del kernel.
Está habilitado por defecto en la configuración del kernel de todas las distribuciones principales (CONFIG_CRYPTO_USER_API_AEAD=y).
**[splice(2)](https://man7.org/linux/man-pages/man2/splice.2.html)** transfiere datos entre descriptores de archivo sin copiarlos - pasa referencias a páginas, no copias. El flujo relevante:```
open("/usr/bin/su") -> fd_file
pipe() -> pipe_rd, pipe_wr
# moves N bytes from the file into the pipe
# the pipe buffer now contains a reference to the same physical page in the page cache
splice(fd_file, pipe_wr, N)
# delivers that reference to the AF_ALG socket
# the TX scatterlist of algif_aead now points to the page cache page of /usr/bin/su
splice(pipe_rd, alg_fd, N)
La scatterlist TX del socket AF_ALG contiene referencias directas a las mismas páginas físicas usadas por el kernel para cada read(), mmap() y execve() del archivo. No se realiza ninguna copia.
Commit 72548b093ee3, algif_aead.c. Para el descifrado, la implementación:
In-place operation: RX SGL (req->dst): [ user buffer: AAD (copy) || CT (copy) ] --sg_chain--> [ Tag (page cache pages) ] req->src = req->dst = RX SGL
Result: page cache pages from /usr/bin/su are now part of the WRITABLE scatterlist passed to the crypto algorithm.
<div id='authencesn'/>
### ***La escritura fuera de límites en authencesn***
authencesn es el envoltorio AEAD del kernel utilizado por IPsec con Números de Secuencia Extendidos (RFC 4303). IPsec utiliza números de secuencia de 64 bits:
- seqno_hi - 32 bits superiores (bytes 0-3 del AAD)
- seqno_lo - 32 bits inferiores (bytes 4-7 del AAD)
Solo seqno_lo se transmite por el cable; seqno_hi es contexto implícito. Para el cálculo de HMAC, authencesn necesita reordenar estos bytes: seqno_hi al principio y seqno_lo al final de la entrada del hash.