
Un rapport sur Dirty Frag, qui est une chaîne de vulnérabilités d'escalade de privilèges locaux (LPE) sous Linux permettant à un utilisateur non privilégié d'obtenir un accès root.
Laboratoire de reproduction et de détection d'exploit pour une chaîne d'élévation de privilèges locale du noyau Linux.
Statut : VÉRIFIÉ. J'ai effectué la reproduction, la vérification sans fichier et la détection au niveau des appels système dans le laboratoire (noyau 6.18.9+kali-amd64). Ce document est un journal de laboratoire. Chaque affirmation ci-dessous a été observée lors de l'exécution de la reproduction. Les captures d'écran et les artefacts sont de véritables captures de la machine virtuelle.
Dirty Frag combine deux bugs logiques déterministes dans le noyau Linux. Ces bugs permettent à un utilisateur local non privilégié d'écraser le cache de pages de fichiers en lecture seule (par exemple, /usr/bin/su) et d'obtenir un shell root :
Les deux variantes utilisent le même schéma racine que Dirty Pipe et Copy Fail. L'appel système splice(2) place une référence à une page du cache de pages d'un fichier dans l'emplacement frag d'un sk_buff côté émetteur. L'attaquant ne peut que lire ce fichier. Le code du noyau côté réception effectue ensuite un STOCKAGE crypto en place par-dessus ce frag. Cela modifie le cache de pages en mémoire RAM. Aucune écriture sur disque n'a lieu, donc la surveillance de l'intégrité des fichiers (AIDE, Tripwire) ne peut pas le voir. L'attaque est déterministe. Elle n'a pas de fenêtre de course et aucun panique du noyau en cas d'échec.
cac2661c53f3 (2017‑01) à f4c50a4034e6 (corrigé 2026‑05‑05)2dc334f1a63a (2023‑06) à aa54b1d27fe0 (corrigé 2026‑05‑10)CVE-2026-43284 = 8,8 (Élevée), CVE-2026-43500 = 7,8 (Élevée)Dirty Frag est une élévation de privilèges sans fichier. Elle corrompt le cache de pages en mémoire, pas le fichier sur disque. La surveillance traditionnelle de l'intégrité des fichiers ne peut pas la voir. La détection doit se faire au niveau des appels système. La chaîne utilise ces primitives d'appels système : socket(AF_ALG)/socket(AF_RXRPC), splice et unshare(CLONE_NEWUSER|CLONE_NEWNET). Le chemin ESP crée également des sockets UDP AF_INET et netlink. Cette couche est au cœur de l'ingénierie de détection de ce dépôt.
Les deux variantes utilisent le même point de chute : un crypto en place qui STOCKE des octets sur une page du cache de pages que l'attaquant place avec splice(2).
UDP_ENCAP_ESPINUDP.vmsplice, puis 16 octets de /usr/bin/su au décalage de fichier cible avec splice.splice pousse le tube dans le socket d'envoi. splice_to_socket() définit MSG_SPLICE_PAGES. Cela place la page du cache de pages de /usr/bin/su directement dans skb->frags[0].xfrm4_udp_encap_rcv, puis xfrm_input, puis esp_input(). La branche vulnérable skip_cow () contourne . Elle effectue un avec la page du cache de pages comme source et destination.L'attaquant contrôle à la fois l'emplacement (décalage splice) et la valeur (4 octets). La vérification d'authentification s'exécute après le stockage, donc la couche crypto ne signale jamais l'écriture. Cette variante nécessite CAP_NET_ADMIN et utilise unshare(CLONE_NEWUSER|CLONE_NEWNET).
rxkad_verify_packet_1() effectue un déchiffrement pcbc(fcrypt) sur un seul bloc directement sur le frag skb épinglé par splice. Elle ne copie pas les données d'abord. L'attaquant choisit une clé de session (add_key("rxrpc", …)) pour que decrypt(ciphertext) soit égal à desired_plaintext. Cela produit un STOCKAGE de 8 octets. Cette variante cible /etc/passwd. Elle ne nécessite pas d'espace de noms utilisateur. Elle requiert le module rxrpc.ko (chargé par défaut sur Ubuntu).
Le PoC public cible /usr/bin/su. Il écrit 48 stockages ESP de 4 octets chacun (192 octets au décalage de fichier 0). Il remplace les premiers octets du cache de pages par un ELF de shell root statique. Le point d'entrée de l'ELF exécute setgid(0); setuid(0); setgroups(0,NULL); execve("/bin/sh", …). Un seul execve("/usr/bin/su") donne alors un shell root.
Le correctif ESP (mainline f4c50a4034e6) marque les frags de pages qui arrivent via splice() avec le drapeau SKBFL_SHARED_FRAG. La branche skip_cow dans esp_input() vérifie désormais aussi ce drapeau. Les skbs à frags partagés passent par skb_cow_data() avant le déchiffrement AEAD en place.
Le correctif RxRPC (mainline aa54b1d27fe0) ajoute une vérification skb->data_len à côté de la vérification skb_cloned() existante. Le noyau copie un skb non linéaire avec des données paginées avant le déchiffrement pcbc(fcrypt) en place.
Capture d'écran de la configuration du laboratoire VirtualBox :

.
├── README.md # ce journal de laboratoire
├── detection/
│ ├── dirtyfrag.rules # règles de détection auditd au niveau des appels système
│ ├── ausearch_dirtyfrag_observed.txt # sortie réelle de détection d'exploit
│ ├── sigma/
│ │ └── dirty_frag_exploit.yml # règle Sigma pour la détection SIEM
│ └── yara/
│ └── dirty_frag_exploit.yar # règle YARA pour le code PoC sur disque/mémoire
├── mitigation/
│ └── dirtyfrag_mitigation.sh # liste noire de modules + vidage du cache de pages
├── poc/
│ └── check_vulnerable.py # vérificateur pré-vol non destructif
├── reports/
│ └── incident-dirtyfrag.md # playbook de réponse à incident
└── screenshots/ # captures réelles de la VM de laboratoire
poc/check_vulnerable.py et confirmer noyau/modules/usernstestuser non privilégié/usr/bin/sudrop_caches ou redémarrage)detection/dirtyfrag.rules et valider les alertes auditdcat /etc/os-release | head -3
uname -r
Capture d'écran de la vérification de la version du noyau :

Le noyau doit être plus ancien que les correctifs de mai 2026 (f4c50a4034e6 / aa54b1d27fe0). Si vous avez exécuté apt upgrade après cette date, l'exploit échouera. Voir Dépannage.
Un vérificateur sûr indique si la VM est une cible plausible. Il vérifie le noyau en cours d'exécution, la présence des modules esp4/esp6/rxrpc et si les espaces de noms utilisateur non privilégiés sont disponibles. La variante ESP nécessite ces espaces de noms.
python3 poc/check_vulnerable.py
Verdict attendu : [*] potentiellement vulnérable -- continuer uniquement dans une VM jetable.
Un testuser non privilégié simule un attaquant sans droits spéciaux.
sudo useradd -m testuser
sudo passwd testuser
su - testuser
id
Capture d'écran : id montre UID 1001. Cela confirme un accès non root.

Depuis le compte non privilégié :
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag
gcc -O0 -Wall -o exp exp.c -lutil
./exp
En cas de succès, l'exploit corrige le cache de pages de /usr/bin/su. Il écrit 48 stockages ESP de 4 octets chacun (un ELF de shell root de 192 octets au décalage de fichier 0). Il lance ensuite un shell root interactif avec forkpty.
Capture d'écran de la disponibilité des modules, de la revue du code source et de la compilation propre :

id
whoami
Capture d'écran : id montre uid=0(root) après ./exp.

sha256sum lit à travers le cache de pages. Pendant que l'écriture de l'exploit est active, le hachage est différent de l'original. Après un vidage, le hachage revient à l'original. Cette paire avant/après prouve que le binaire sur disque n'a jamais été touché.
sha256sum /usr/bin/su # 1) pendant que le cache de pages est contaminé -> hachage DIFFÉRENT
Capture d'écran : le hachage est différent du hachage du paquet (RAM empoisonnée, disque intact).

Hachage corrompu observé (cache de pages) : 3fc29078bd77150b5d6fbb368f632bf02c1a3704816f03fb694ec2e81e77bde4
Après l'exploitation, le cache de pages contient les données corrompues. Videz-le toujours :
echo 3 | sudo tee /proc/sys/vm/drop_caches
# ou redémarrez la VM
Puis vérifiez la restauration (en tant que testuser) :
sha256sum /usr/bin/su # correspond maintenant au hachage ORIGINAL du paquet
su - # demande à nouveau un mot de passe — pas de root automatique
Capture d'écran : hachage restauré (original sur disque).

Hachage original observé (sur disque) : 2b4f8770bd35bba5cdc5cfe292bc1d988e92ec1786bf91cf83e0e86fac056eb6
Après un redémarrage, dpkg -V util-linux n'a renvoyé aucune sortie. Le /usr/bin/su sur disque correspond exactement au paquet, donc le hachage corrompu 3fc29078… n'existait que dans le cache de pages.
drop_cachespeut ne pas expulser la page empoisonnée. Cela se produit si un processus en cours d'exécution épingle encore la page. Dans ce cas,sha256sumetdpkg -Vcontinuent de montrer le contenu corrompu. La solution fiable est un redémarrage. Notez quedpkg -Vlit aussi à travers le cache de pages. Pendant que la page est empoisonnée, il signale??5??????(le MD5 est différent ; la taille, le mode, le propriétaire et le mtime correspondent tous). Il redevient silencieux après le redémarrage. Cela prouve que le fichier sur disque n'a jamais été modifié.
Le vidage ne désactive pas l'exploit. Il ne fait que nettoyer le cache de pages empoisonné. Vous devez désactiver les modules séparément (voir Atténuation). Retirer les modules déjà chargés sur un hôte exploité nécessite un redémarrage.
Dirty Frag est invisible pour la surveillance de l'intégrité des fichiers. La détection se concentre sur les primitives d'appels système que la chaîne doit utiliser.
detection/dirtyfrag.rules)# /etc/audit/rules.d/dirtyfrag.rules
-a always,exit -F arch=b64 -S socket -F a0=38 -F uid!=0 -k dirtyfrag_af_alg
-a always,exit -F arch=b64 -S socket -F a0=33 -F uid!=0 -k dirtyfrag_rxrpc
-a always,exit -F arch=b64 -S splice -F uid!=0 -k dirtyfrag_splice
-a always,exit -F arch=b64 -S unshare -F uid!=0 -k dirtyfrag_namespace
-w /usr/bin/su -p r -k dirtyfrag_suid_read
Note :
AF_ALG = 38etAF_RXRPC = 33sous Linux (voir/usr/include/bits/socket.h). Les premières ébauches utilisaienta0=21. Cette valeur est incorrecte. AF_RXRPC est 33, pas 21.
Déployer et vérifier :
sudo cp detection/dirtyfrag.rules /etc/audit/rules.d/
sudo systemctl restart auditd # ou : sudo auditctl -D && sudo auditctl -R /etc/audit/rules.d/dirtyfrag.rules
sudo auditctl -l
Alertes attendues après avoir ré-exécuté l'exploit (corréler par PID) :
ausearch -k dirtyfrag_af_alg
ausearch -k dirtyfrag_rxrpc
ausearch -k dirtyfrag_splice
ausearch -k dirtyfrag_namespace
Les cinq règles ont été chargées (auditctl -R, res=1 pour chaque CONFIG_CHANGE). Les règles ont détecté la configuration des espaces de noms de l'exploit :
time->Wed Aug 5 08:26:37 2026
type=PROCTITLE msg=audit(1785932797.328:592): proctitle="./exp"
type=SYSCALL msg=audit(1785932797.328:592): arch=c000003e syscall=272 success=yes exit=0 a0=50000000 a1=0 a2=0 a3=0 items=0 ppid=5198 pid=5199 auid=1000 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=pts2 ses=2 comm="exp" exe="/home/testuser/dirtyfrag/exp" subj=unconfined key="dirtyfrag_namespace"
Décodé : syscall=272 (unshare), a0=50000000 équivaut à CLONE_NEWUSER | CLONE_NEWNET, uid=1001 (testuser non privilégié), comm="exp". Le journal complet se trouve dans detection/ausearch_dirtyfrag_observed.txt.
Capture d'écran : la sortie ausearch montre le chargement des règles et l'événement de l'exploit.

Le playbook complet se trouve dans reports/incident-dirtyfrag.md : résumé exécutif, chronologie, IoC, cartographie MITRE ATT&CK, confinement, éradication, récupération et leçons apprises.
Atténuation immédiate en cours d'exécution. Elle ne survit pas à un redémarrage pour les modules déjà chargés (voir la note ci-dessous) :
sudo mitigation/dirtyfrag_mitigation.sh
Ce qu'elle fait :
/etc/modprobe.d/dirtyfrag.conf pour bloquer esp4, esp6 et rxrpc. Elle inclut les lignes blacklist et alias … off. Une simple ligne unique manque ces lignes (le chargement automatique par alias fonctionnait sinon).Impact : désactiver ces modules fait cesser le fonctionnement des VPN IPsec (ESP) et du système de fichiers AFS (RxRPC).
Correctif permanent : mettre à niveau vers un noyau contenant les correctifs en amont. Ou mettre les modules en liste noire au démarrage (initcall_blacklist=esp4,esp6,rxrpc).
Ce dépôt est destiné à la recherche et à la formation en sécurité défensive autorisées uniquement.
Voir LICENSE. Ce laboratoire est destiné à un usage éducatif dans votre propre environnement isolé uniquement. Ne pas exécuter le PoC sur des systèmes que vous n'êtes pas autorisé à tester.
| Variante | CVE | Point de chute | Chemin de déclenchement | Nécessite un userns non privilégié |
|---|
| Écriture xfrm‑ESP dans le cache de pages | CVE‑2026‑43284 | crypto_authenc_esn_decrypt() dans esp_input() | socket(AF_INET) avec encapsulation UDP, puis xfrm_input() | Oui (CAP_NET_ADMIN) |
| Écriture RxRPC dans le cache de pages | CVE‑2026‑43500 | rxkad_verify_packet_1() (pcbc(fcrypt)) | socket(AF_RXRPC) | Non |
!skb_cloned() && !skb_has_frag_list()skb_cow_data()crypto_authenc_esn_decrypt() émet un STOCKAGE des 32 bits de poids fort de l'ESN. Cette valeur est replay_esn->seq_hi. L'attaquant choisit cette valeur lors de l'enregistrement de l'AS avec l'attribut netlink XFRMA_REPLAY_ESN_VAL.| Composant | Détails |
|---|
| Hyperviseur | VirtualBox |
| VM cible | Kali Linux 2026.1 (instantané restauré à un état vulnérable) |
| Noyau | 6.18.9+kali‑amd64 (plus ancien que les correctifs de mai 2026) |
| PoC d'exploit | V4bel/dirtyfrag (fichier C unique) |
| Détection | auditd (règles dans detection/dirtyfrag.rules) |
| Couche | Ce qu'elle voit | Statut |
|---|
| auditd | Sockets AF_ALG et AF_RXRPC, splice, unshare, lectures SUID | Oui. Déployé et validé (fichier de règles detection/dirtyfrag.rules) |
| Sigma | Modèles d'appels système pour SIEM | Oui. Règle prête (detection/sigma/dirty_frag_exploit.yml) |
| YARA | Code PoC sur disque ou en mémoire | Oui. Règle prête (detection/yara/dirty_frag_exploit.yar) |
| FIM (AIDE/Tripwire) | Modifications de fichiers | Non. Aveugle, car aucune écriture sur disque n'a lieu |
| Symptôme | Cause probable | Correctif |
|---|
./exp affiche failed / post-write verify failed | Le noyau est corrigé (mai 2026 ou ultérieur) | Démarrer un noyau plus ancien ou restaurer un instantané pré-mise à jour. Revérifier avec poc/check_vulnerable.py |
unshare(CLONE_NEWUSER) renvoie EPERM | Espaces de noms utilisateur non privilégiés désactivés (AppArmor ou sysctl) | Vérifier sysctl kernel.unprivileged_userns_clone. Sur Ubuntu, vérifier AppArmor. Kali l'autorise par défaut |
esp4/esp6/rxrpc non chargés | Modules non disponibles | sudo modprobe esp4 esp6 rxrpc (RxRPC se charge automatiquement avec socket(AF_RXRPC)) |
Binaire su « corrigé » mais pas de shell root | drop_caches a déjà été exécuté, ou le cache de pages est épinglé par des processus | Ré-exécuter l'exploit. Si bloqué, redémarrer la VM |
| L'exploit a été exécuté, puis toujours root-able après atténuation | Cache de pages non vidé, ou modules déjà chargés avant la liste noire | echo 3 > /proc/sys/vm/drop_caches. L'arrêt complet nécessite un redémarrage |