Dirty Frag : Nouvelle Vulnérabilité Zero-Copy d'Élévation de Privilèges du Noyau Linux
Dirty Frag : Une chaîne de vulnérabilités sur le chemin zero-copy du noyau Linux qui empoisonne le cache de pages, enchaînant xfrm-ESP et RxRPC. Affecte presque toutes les distributions majeures depuis 2017 et permet l’élévation de privilèges à root sans mot de passe.
Encore ? On N’a Jamais la Paix ?
Honnêtement, je ne pensais pas devoir réécrire un article sur l’élévation de privilèges du noyau si tôt.
Seulement huit jours depuis l’article Copy Fail. Huit jours ! La liste noire algif_aead de Copy Fail venait à peine d’être tapée dans le terminal, le patch n’était même pas chaud, et voilà — Dirty Frag qui tombe.
Et le plus absurde : les mitigations de Copy Fail sont totalement inefficaces contre Dirty Frag. Cette fois, l’attaque cible des sous-systèmes complètement différents — les chemins de chiffrement xfrm-ESP et RxRPC — donc que vous désactiviez algif_aead ou non ne change absolument rien.
Dirty Pipe (2022), Copy Fail (2026.04), Dirty Frag (2026.05)… À chaque fois c’est « affecte presque toutes les distributions depuis 2017 ». Vous savez ce que ça signifie ? Que pendant près de dix ans, n’importe qui connecté à un compte non-privilégié sur votre système a eu la possibilité de devenir silencieusement root.
Mais est-ce entièrement la faute des développeurs du noyau ? Pas forcément.
L’explosion des outils d’audit de code assistés par IA permet aux chercheurs de scanner en quelques heures des vulnérabilités cachées depuis près d’une décennie. Des bugs qui exigeaient une review manuelle ligne par ligne sont maintenant balayés en masse par l’IA. Copy Fail a été localisé par Xint Code en une heure. Dirty Frag vient de l’audit manuel du chercheur coréen Hyunwoo Kim, mais les chemins de code exploités appartiennent à la même famille que Copy Fail — écriture du cache de pages sur le chemin zero-copy — ce qui montre que ce type de défaut de conception est loin d’être isolé.
Plus troublant encore : le processus de divulgation. Le chercheur avait soumis les détails à l’équipe de sécurité du noyau avec un embargo de 5 jours. Mais le jour même, un tiers a publié les détails complets et l’exploit ESP(xfrm). Pas de CVE, pas de patch, mais le PoC disponible pour tous. Ce n’est pas de la divulgation responsable, c’est un coup de couteau dans le dos de tous les utilisateurs Linux. La fenêtre de correction a explosé, tout le monde forcé de courir à nu.
Bon, fini de râler. Regardons sérieusement cette vulnérabilité.
Chronologie
| Date | Événement |
|---|---|
| 2017-01 | Vulnérabilité xfrm-ESP introduite avec le commit cac2661c53f3 (cachée 9 ans) |
| 2023-06 | Vulnérabilité RxRPC introduite avec le commit 2dc334f1a63a |
| 2026-04-29 | Hyunwoo Kim (@v4bel) signale la vulnérabilité RxRPC et l’exploit complet à security@kernel.org |
| 2026-05-07 | Détails soumis à linux-distros avec un embargo de 5 jours |
| 2026-05-07 | Le jour même, un tiers publie les détails ESP(xfrm), l’embargo est rompu |
| 2026-05-07 | Après consultation des mainteneurs, la documentation complète est publiée. Pas de CVE, pas de patch officiel |
| 2026-05-08 | À l’heure où j’écris, les distributions attendent encore le patch upstream |
Analyse d’Impact
| Métrique | Détails |
|---|---|
| CVE | Aucun pour l’instant (le NVD n’a pas eu le temps d’en attribuer) |
| Type | Défaut logique déterministe, pas une condition de course |
| Taux de réussite | 100%, succès garanti à la première exécution |
| Portée | Presque toutes les distributions majeures depuis 2017 (noyau 7.0.3 inclus) |
| Méthode | Payload de 192 octets, assemblé via 48 écritures de 4 octets |
| Trace disque | Aucune — empoisonne uniquement le cache de pages en mémoire, restauration au reboot |
| Contourne Copy Fail | La désactivation algif_aead est totalement inefficace |
Comparaison avec les vulnérabilités LPE historiques :
| Caractéristique | Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) | Dirty Frag (2026) |
|---|---|---|---|---|
| Condition de course | Requise | Non requise | Non requise | Non requise |
| Portée | Versions spécifiques | 5.8+ | Toutes distros 2017+ | Toutes distros 2017+ |
| Complexité de l’exploit | Complexe | Complexe | 10 lignes Python | Un seul fichier C |
| Trace disque | Oui | Oui | Non | Non |
| Statut du patch | Corrigé | Corrigé | Corrigé | Pas de patch officiel |
Distributions affectées confirmées :
| Distribution | Noyau testé |
|---|---|
| Ubuntu 24.04.4 | 6.17.0-23-generic |
| RHEL 10.1 | 6.12.0-124.49.1 |
| CentOS Stream 10 | 6.12.0-224 |
| AlmaLinux 10 | 6.12.0-124.52.3 |
| Fedora 44 | 6.19.14-300 |
| openSUSE Tumbleweed | 7.0.2-1 |
| Arch Linux | 7.0.3 |
De 6.12 à 7.0, d’Ubuntu à Arch — couverture totale. Oui, toute distribution majeure que vous utilisez est probablement affectée.
Principes Techniques : Pourquoi « Dirty Frag » ?
Le Cœur en Une Phrase
splice() implante une référence de cache de page de fichier en lecture seule dans le slot frag du skb → le noyau récepteur effectue des opérations cryptographiques in-situ sur le frag (src == dst, même mémoire) → la prétendue opération de déchiffrement devient une primitive STORE écrivant directement dans le cache de page en lecture seule → le code machine de su est remplacé par un ELF root-shell.
« Dirty » = empoisonnement du cache de pages. « Frag » = exploitation du mécanisme de fragments skb. Ensemble — Dirty Frag.
Pollution skb Frag sur le Chemin Zero-Copy
Normalement, quand vous écrivez dans un socket, le noyau copie les données utilisateur dans son skb. Mais splice() prend le chemin zero-copy — il insère directement le pointeur de la page de cache (structure page + offset) dans le tableau frag du skb, sans copier les données :
struct skb_shared_info {
struct sk_buff *frag_list; // liste chaînée de frags
skb_frag_t frags[MAX_SKB_FRAGS]; // chaque entrée = {page, offset, size}
// ...
};
Le point clé : la page passée à splice() est la page de cache en mémoire de votre fichier (ex. /usr/bin/su) — vous n’avez que les droits de lecture, mais une référence à cette page est déjà dans le skb de la pile réseau.
Ensuite, tout dépend si la pile réseau réceptrice « met les mains » sur cette page.
Vulnérabilité 1 : Écriture Cache de Page xfrm-ESP
Première vulnérabilité : le chemin de déchiffrement IPsec ESP.
esp_input() déchiffre les données. Normalement, si le skb partage des données avec un frag, le noyau doit d’abord appeler skb_cow_data() (Copy-on-Write). Mais il existe un chemin qui contourne COW :
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) {
// [1] skb linéaire : pas de frag, sûr
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) {
// [2] A des frags, mais pas de frag_list → saute COW directement !
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow; // LA BOMBE
}
}
err = skb_cow_data(skb, 0, &trailer);
}
Quand le skb est non-linéaire (a des frags contenant le cache de page) mais frag_list vide, le code saute à skip_cow. Ensuite, crypto_authenc_esn_decrypt() fait un déchiffrement AEAD in-situ — src et dst pointent vers la même scatterlist, la page de cache implantée par l’attaquant.
La ligne critique :
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
Ceci écrit 4 octets dans dst (la page de cache de su) à l’offset assoclen + cryptlen. La valeur vient des 32 bits hauts du numéro de séquence ESP — que l’attaquant contrôle via XFRMA_REPLAY_ESN_VAL.seq_hi.
L’attaquant contrôle donc :
- Où écrire (offset, ajusté via la longueur du payload)
- Quoi écrire (4 octets, via
seq_hi)
48 SA XFRM, chacune avec son seq_hi stockant un fragment de 4 octets de l’ELF, 48 itérations → un ELF root-shell complet dans le cache de su.
Limite : il faut CAP_NET_ADMIN. Obtenable via unshare(CLONE_NEWUSER | CLONE_NEWNET), mais l’AppArmor d’Ubuntu bloque les namespaces réseau pour les non-privilégiés. D’où le besoin d’une deuxième vulnérabilité.
Vulnérabilité 2 : Écriture Cache de Page RxRPC
Deuxième vulnérabilité : le déchiffrement Kerberos du protocole RxRPC.
rxkad_verify_packet_1() fait un déchiffrement pcbc(fcrypt) in-situ sur les 8 premiers octets :
skcipher_request_set_crypt(req, sg, sg, 8, iv.x);
// ^^ ^^
// src==dst → opération in-situ !
ret = crypto_skcipher_decrypt(req); // écriture de 8 octets
skb_to_sgvec() convertit le frag du skb (contenant la page de cache) en scatterlist, src == dst. Le « résultat de déchiffrement » est écrit directement dans la page de cache en lecture seule.
Comparaison :
| Caractéristique | xfrm-ESP | RxRPC |
|---|---|---|
| Taille d’écriture | 4 octets | 8 octets |
| Contrôle de la valeur | Direct (seq_hi) | Indirect (force brute fcrypt) |
| Privilège requis | Namespace utilisateur | Aucun privilège |
| Introduction | 2017-01 | 2023-06 |
Les valeurs RxRPC ne sont pas directement contrôlables — c’est le résultat de fcrypt_decrypt(C, K). L’attaquant enregistre une clé K via add_key("rxrpc", ...), puis force brute pour trouver K produisant les 8 octets cibles. fcrypt = 56-bit, 8-byte block, force brute facile.
Point critique : RxRPC ne nécessite aucun privilège. Pas de namespace utilisateur, pas de namespace réseau, pas de CAP_NET_ADMIN. Et Ubuntu charge rxrpc.ko par défaut.
Logique d’Enchaînement
Couverture complète par complémentarité :
| Scénario | Vulnérabilité | Raison |
|---|---|---|
| Ubuntu (AppArmor bloque namespaces) | RxRPC | rxrpc.ko chargé par défaut, sans privilège |
| RHEL / Fedora / openSUSE | xfrm-ESP | Namespaces dispos, écritures ESP précisément contrôlables |
| Autres distributions | xfrm-ESP ou RxRPC | Au moins un chemin disponible |
C’est ça qui rend Dirty Frag terrifiant — quelle que soit votre configuration, il y a toujours un chemin vers root.
Analyse du PoC
Exploit open-source sur github.com/V4bel/dirtyfrag :
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag && gcc -O0 -Wall -o exp exp.c -lutil && ./exp
Principe :
- Préparer un ELF minimal de 192 octets — root-shell à exécution immédiate
- Diviser en 48 fragments de 4 octets (chemin xfrm-ESP)
- Enregistrer un XFRM SA par fragment,
seq_hi= valeur du fragment splice()envoie le cache desudans le skb → écriture in-situ- 48 itérations → les 192 premiers octets du cache de
susont remplacés execve("/usr/bin/su")→ root shell
Zéro touche au disque. /usr/bin/su intact sur disque, md5 inchangé. Mais dans le cache de page du noyau, il a été remplacé. Quand un processus lance execve sur su, le noyau lit le cache — et exécute votre binaire. Root.
Réponse d’Urgence : Désactiver les Modules Vulnérables
Au 8 mai 2026, pas de patch officiel. Mitigation temporaire : décharger les trois modules.
Vérifiez si vous êtes affecté :
lsmod | grep -E 'esp4|esp6|rxrpc'
Si sortie non vide → affecté.
Exécutez immédiatement (pas de reboot requis) :
sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf"
sudo rmmod esp4 esp6 rxrpc 2>/dev/null
Important : si le PoC a déjà été exécuté, videz le cache de pages :
echo 3 | sudo tee /proc/sys/vm/drop_caches
Sans cela, même modules désactivés, le cache empoisonné reste en mémoire.
Effets secondaires :
esp4/esp6désactivés → coupure des tunnels VPN IPsec. Si vous utilisez strongSwan/Libreswan, évaluez d’abord l’impact.rxrpcdésactivé → impact minimal sauf si vous utilisez AFS.
Course aux Armements Accélérée par l’IA
Dirty Frag impose une question plus profonde : pourquoi les LPE du noyau sortent-elles en rafale ?
| Vulnérabilité | Découverte | Outil clé | Délai rapport → public |
|---|---|---|---|
| Dirty Pipe | 2022 | Audit manuel | Standard |
| Copy Fail | 2026-04 | Xint Code (IA) | ~1 mois |
| Dirty Frag | 2026-05 | Audit manuel | 8 jours (embargo rompu le jour même) |
D’une par an → une par mois → une par semaine ?
Derrière ça, l’explosion des outils d’audit IA. Avant, un humain lisait ligne par ligne, une vulnérabilité pouvait dormir dix ans. Aujourd’hui l’IA scanne un sous-système en heures. Le noyau n’a pas empiré — les vieilles dettes cachées sont juste révélées.
Et l’écosystème de divulgation change. L’embargo de Dirty Frag a été délibérément brisé le jour même. Du rapport à security@kernel.org jusqu’aux armes dans les mains des hackers du monde entier : huit jours. La fenêtre de correction — anéantie.
La même famille continue de s’agrandir :
| Vulnérabilité | Sous-système | Statut |
|---|---|---|
| Copy Fail | AF_ALG + authencesn | Corrigé |
| Dirty Frag | xfrm-ESP + RxRPC | Pas de patch |
| Copy Fail 2 | ESP-in-UDP | Divulgué |
| ZCRX Freelist | io_uring ZCRX | Divulgué |
Même principe partout : splice() injecte des références de cache de page en lecture seule, les sous-systèmes écrivent in-situ. La même classe de défaut de conception :
| Composant | Raison | Effet secondaire |
|---|---|---|
splice() | Zero-copy, perf | Réfs de cache en lecture seule envoyées aux sous-systèmes |
AF_ALG | Exposer le chiffrement noyau | Sessions de chiffrement initiables sans privilège |
| xfrm-ESP | Accélération IPsec | Déchiffrement in-situ, pages en lecture seule comme buffer |
| RxRPC | Support protocole AFS | Idem, sans même avoir besoin de privilège namespace |
Chaque design, pris isolément, est une optimisation ou fonctionnalité légitime. Assemblés, ils forment une chaîne où n’importe quel utilisateur local devient root sans mot de passe.
Tant que le noyau upstream n’aura pas réexaminé le paradigme des « opérations in-situ sur chemins zero-copy », je vous le garantis — ce ne sera pas le dernier.
Pour les utilisateurs :
- Exécutez les commandes de blacklist maintenant. Revenez en arrière au patch officiel.
- Surveillez les mises à jour du noyau. Dès que corrigé, mettez à jour et rebootez.
- Vérifiez régulièrement avec
lsmod | grep.
Références
- Dépôt GitHub PoC : https://github.com/V4bel/dirtyfrag
- LWN : https://lwn.net/Articles/1071719/
- Phoronix : https://www.phoronix.com/news/Dirty-Frag-Linux
- Analyse coréenne (GeekNews) : https://news.hada.io/topic?id=29275
- Copy Fail : https://copy.fail/
- Copy Fail 2 : https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo
Diagrammes d’Information

Figure 1 : Vue d’ensemble — comment les références de cache zero-copy sont exploitées via les skb frags