732 octets pour passer de simple user à root sur la quasi-totalité des kernels Linux. Une optimisation crypto de 2017 qui laisse écrire dans le page cache d'un setuid. Diagnostic, mitigation et patch.
Hier, 29 avril 2026, la liste oss-security a vu débarquer la divulgation officielle de Copy Fail (CVE-2026-31431), accompagnée d’un site dédié copy.fail. 732 octets de Python en stdlib pure, et un uid=1000 se transforme en uid=0 sur Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1 et SUSE 16. Tous testés sur des kernels 6.x récents, pas des fossiles.
L’auteur du site le résume sans fioriture : “Copy Fail is a straight-line logic flaw”, sans race condition ni offset kernel-spécifique. Pas de timing, pas d’offset à calibrer. Le bug a vécu 9 ans dans authencesn, exposé via AF_ALG et splice(), et c’est l’agent IA Xint Code qui l’a déterré.
Si vous administrez un host multi-tenant, un cluster Kubernetes, un runner CI/CD ou n’importe quel environnement où plusieurs utilisateurs ont accès au shell, vous êtes concerné. Le commit de fix est dans la mainline depuis le 1er avril, mais le backport sur les branches stables des distros est encore en cours au 30 avril. Ne dormez pas dessus.
Le mécanisme en 3 lignes
Le kernel expose AF_ALG, une socket interface qui permet à un programme normal de causer aux primitives crypto sans aucun privilège. Une optimisation in-place de 2017 dans algif_aead laisse une page du page cache (donc un fichier read-only du disque, par exemple /usr/bin/su) atterrir dans la destination scatterlist d’une opération de chiffrement, qui est accessible en écriture. Un splice() bien placé écrit 4 octets dans cette page.
On boucle, on patch progressivement le binaire setuid en mémoire, on l’exécute, et on récupère un shell root. Pas besoin de toucher au disque.
Chronologie
| Date | Événement |
|---|---|
| ~2017 | Commit de l’optimisation algif_aead (bug introduit) |
| 2026-03-23 | Signalement à la security@kernel.org par l’équipe à l’origine de la découverte |
| 2026-04-01 | Patch mainline (commit a664bf3d603d) |
| 2026-04-22 | CVE-2026-31431 attribuée |
| 2026-04-29 | Divulgation publique + PoC GitHub |
Suis-je vulnérable ?
Trois critères, vérifiables en 30 secondes.
# 1. Le kernel intègre-t-il le patch a664bf3d603d ?
uname -r
# Tout kernel non rebooté depuis le 1er avril 2026 sur une distro à jour
# OU tout kernel d'avant cette date est exposé
# 2. Le module algif_aead est-il chargeable ?
lsmod | grep algif_aead
grep algif_aead /lib/modules/$(uname -r)/modules.builtin
# Présent dans modules.builtin = compilé en dur, pas déchargeable
# 3. Test direct contre le PoC officiel (sur VM clone uniquement)
git clone https://github.com/theori-io/copy-fail-CVE-2026-31431
cd copy-fail-CVE-2026-31431
# Hash officiel publié sur copy.fail :
echo "a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9 copy_fail_exp.py" | sha256sum -c
Si le hash matche, vous tenez bien le PoC officiel. Lancer python3 copy_fail_exp.py sur un host vulnérable rend un shell root sans mot de passe. Sur un host patché, le script échoue à l’écriture dans le page cache.
⚠️ Ne lancez jamais ce PoC sur une machine que vous ne possédez pas, même pour “tester”. L’exploit altère réellement /usr/bin/su en mémoire pendant l’exécution. Et même sur votre propre machine : le code est volontairement obscurci, lisez-le avant de l’exécuter. Un PoC public peut très bien dériver vers une charge malveillante après un fork.
Piège fréquent : un
lsmod | grep algif_aeadqui ne retourne rien ne veut pas dire que vous êtes safe. Le module est chargé à la demande dès qu’un programme appellesocket(AF_ALG, ...). Tant que la blacklist/etc/modprobe.d/disable-algif.confn’est pas en place (ou que le module est builtin), la surface est ouverte.
Mitigation immédiate (avant reboot)
Cas 1 : module chargeable
Sur Ubuntu desktop, Debian et la plupart des VM cloud généralistes, algif_aead est un module à part. On le sort et on le blacklist :
# Blacklist permanente (nom de fichier conforme à copy.fail)
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif.conf
# Déchargement immédiat
sudo rmmod algif_aead 2>/dev/null && echo "Module déchargé" \
|| echo "Module non chargé ou compilé en dur, voir cas 2"
Ce que ça fait :
install ... /bin/falseempêche toutmodprobe algif_aeadfuturrmmoddécharge l’instance courante- Au prochain reboot, le module reste hors circuit
Impact fonctionnel : quasi nul. Seul OpenSSL avec le moteur afalg (accélération crypto via le kernel) ou certains chemins crypto embarqués utilisent vraiment cette interface. Si vous ne savez pas, vous ne l’utilisez pas.
Cas 2 : module compilé en dur
Sur RHEL, Amazon Linux 2023 et la plupart des kernels d’entreprise, algif_aead est dans modules.builtin. rmmod échoue, le blacklist modprobe est sans effet. Trois voies :
# Option A : désactiver l'init du sous-système au boot via grub (RHEL & dérivés)
sudo grubby --update-kernel ALL --args='initcall_blacklist=algif_aead_init'
sudo reboot
# Option B : restreindre la famille de socket par service (systemd)
# À ajouter dans une unit ou un drop-in :
[Service]
RestrictAddressFamilies=~AF_ALG
Pour les conteneurs, l’équivalent passe par un seccomp profile Docker qui retourne EAFNOSUPPORT sur socket(AF_ALG, ...). C’est aussi la recommandation explicite de copy.fail : “For untrusted workloads (containers, sandboxes, CI), block AF_ALG socket creation via seccomp regardless of patch state.”
# Option C (recommandée) : patch kernel mainline
# Ubuntu / Debian (adaptez le paquet à votre variante : -aws, -azure, -gcp, -amd64...)
sudo apt update && sudo apt full-upgrade
sudo reboot
# Amazon Linux 2023 / RHEL 10.1
sudo dnf update kernel && sudo reboot
# SUSE 16
sudo zypper patch && sudo reboot
Vérifiez après reboot que le commit a664bf3d603d est bien présent (les distros sérieuses backportent même sur leurs branches LTS) :
uname -r
# Pour les distros qui exposent le changelog kernel :
rpm -q --changelog kernel | grep -i "31431\|copy.fail" | head -5 # RHEL/Amazon
zcat /usr/share/doc/linux-image-$(uname -r)/changelog.Debian.gz | grep -i "31431" # Debian/Ubuntu
Vérifier l’exposition sur un parc
Un one-liner SSH suffit pour faire le tour d’un fleet :
for host in $(cat hosts.txt); do
echo "=== $host ==="
ssh "$host" 'uname -r; lsmod | grep -c algif_aead; \
grep -c algif_aead /lib/modules/$(uname -r)/modules.builtin 2>/dev/null'
done
Trois colonnes en sortie : version kernel, module chargé (1 ou 0), module builtin (1 ou 0). Tous les hôtes avec un module présent et un kernel non patché remontent au top de la pile de patch.
Périmètre réel
CVSS officiel : 7.8/10, vecteur AV:L (local). Élevé sans être critique au sens strict, parce que l’exploit reste local : il faut déjà avoir un shell sur la machine. Le risque est concentré sur :
- Hôtes multi-utilisateurs (universités, hébergeurs partagés)
- Conteneurs partageant le kernel hôte : escalade root dans le conteneur garantie ; évasion vers l’hôte théorique mais sans PoC public à date
- Runners CI/CD qui exécutent du code arbitraire (GitHub Actions self-hosted, GitLab runners)
- Sandboxes type Jupyter ou code execution as a service
Si votre serveur n’a qu’un seul utilisateur SSH (vous), le rideau tombe : pas de chaînage trivial depuis l’extérieur. Mais pour tout host où un user non-trusté peut ouvrir un shell, c’est game over jusqu’au patch.
Comparaisons avec Dirty COW et Dirty Pipe : Copy Fail joue dans la même cour, sans les contraintes de timing qui rendaient ses ancêtres délicats à exploiter en pratique.
État du patching au 30 avril 2026
Le rythme de backport varie selon les distros. Au moment de la rédaction :
| Distro | Statut |
|---|---|
| Debian Sid / Forky (testing) | ✅ Patch publié |
| Ubuntu (canaux à jour) | ✅ Patch publié |
| Debian stable (Bookworm) | ⏳ En cours |
| RHEL / Alma / Rocky | ⏳ En cours |
| SUSE | ⏳ En cours |
Tant que votre branche n’a pas reçu le commit a664bf3d603d, le workaround (blacklist module ou initcall_blacklist) reste obligatoire. Vérifiez l’état du paquet kernel sur le tracker sécurité de votre distro avant tout reboot.
Et l’IA dans tout ça ?
Le détail qui mérite qu’on s’y arrête : Xint Code n’a pas fuzzé tout le kernel pendant 6 mois. Les chercheurs lui ont glissé un prompt ciblé (examine tous les chemins accessibles depuis un programme user dans le sous-système crypto, en insistant sur le fait que splice() peut faire atterrir des fichiers read-only dans des zones modifiables), et l’agent a déroulé en une heure environ.
L’équipe à l’origine de la découverte précise au passage que le même scan a remonté d’autres CVE encore sous embargo. Ce n’est pas l’IA qui remplace l’humain : c’est l’humain qui sait où regarder, et l’IA qui couvre vite la surface. La différence avec un audit kernel manuel des années 2010, c’est un facteur 100 sur le débit.
Attendez-vous à voir d’autres bugs anciens ressortir des cartons par cette voie dans les mois qui viennent.
Avant : Kernel Linux non patché, algif_aead exposé → escalade root en 732 octets de Python
Après : Patch a664bf3d603d appliqué (ou module blacklisté + seccomp) → PoC échoue à l’écriture page cache
Une optimisation crypto vieille de 9 ans qui se transforme en escalade triviale en 2026. Patchez avant que le PoC ne sorte de GitHub vers les exploit kits.
Références
- copy.fail : site officiel de la divulgation
- PoC officiel
- Port C indépendant : exécutable statique de 1,7 Ko, sans dépendance
