
Une explication structurée de la CVE-2026-31431 (Copy Fail), reliant les trois modifications du noyau qui ont introduit la vulnérabilité et permis son exploitation.
Un défaut logique dans authencesn enchaîné via AF_ALG et splice() en une écriture contrôlée de 4 octets dans le cache de pages de tout fichier lisible du système. Pas de condition de course, pas d'offsets, pas de payload compilé. Le même script de 732 octets obtient les droits root sur toutes les distributions Linux depuis 2017.
CVE-2026-31431 - Copy Fail est un défaut logique dans le modèle cryptographique authencesn du noyau Linux. Il permet à un utilisateur local non privilégié d'effectuer une écriture contrôlée de 4 octets dans le cache de pages de n'importe quel fichier lisible du système, sans modifier le fichier sur le disque.
Le bug n'existe dans aucun des trois composants pris individuellement. Il émerge de leur interaction :``` 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'/>
## ***🧬 Analyse des causes racines***
<div id='primitive'/>
### ***La primitive AF_ALG + splice()***
AF_ALG (*[AF_ALG = 38](https://docs.kernel.org/crypto/userspace-if.html#user-space-api-general-remarks)*) est un type de socket qui expose l'API cryptographique du noyau à l'espace utilisateur non privilégié. Un processus non privilégié peut :
1. Ouvrir une socket AF_ALG / SOCK_SEQPACKET.
2. Faire un bind() sur n'importe quel modèle AEAD disponible exposé par l'API crypto du noyau.
3. Définir une clé cryptographique via setsockopt(SOL_ALG, ALG_SET_KEY, ...) sur l'algorithme configuré.
4. Appeler accept() pour obtenir une socket d'opération dédiée qui gérera les requêtes de chiffrement et de déchiffrement.
5. Envoyer des données élaborées à l'aide de sendmsg() et recevoir le résultat traité via recvmsg(), en interagissant pleinement avec le sous-système crypto du noyau.
Il est activé par défaut dans la configuration du noyau de toutes les distributions majeures (CONFIG_CRYPTO_USER_API_AEAD=y).
**[splice(2)](https://man7.org/linux/man-pages/man2/splice.2.html)** transfère des données entre descripteurs de fichiers sans copie - il transmet des références à des pages, pas des copies. Le flux pertinent :```
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)
Le scatterlist TX de la socket AF_ALG contient des références directes aux mêmes pages physiques utilisées par le noyau pour chaque read(), mmap() et execve() du fichier. Aucune copie n'est impliquée.
Commit 72548b093ee3, algif_aead.c. Pour le déchiffrement, l'implémentation :
sg_chain(), en conservant les références du cache de pages dans le SGL RX.req->src = req->dst, les deux pointant vers le SGL RX combiné.```
TX SGL (input from splice):
[ page cache page: AAD || CT || Tag ]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'/>
### ***L'écriture hors limites dans authencesn***
authencesn est le wrapper AEAD du noyau utilisé par IPsec avec les numéros de séquence étendus (RFC 4303). IPsec utilise des numéros de séquence de 64 bits :
- seqno_hi - 32 bits supérieurs (octets 0-3 de l'AAD)
- seqno_lo - 32 bits inférieurs (octets 4-7 de l'AAD)