
Exercice de détection en laboratoire pour DirtyFrag (CVE-2026-43284) - escalade de privilèges du noyau Linux via la corruption du cache de pages xfrm-ESP. Article complet couvrant l'exécution de l'exploit, les lacunes de détection et les règles EQL corrigées utilisant Elastic Stack
Classification : Laboratoire interne - CyberLAB
Date : 24 mai 2026
Plateforme : Elastic Stack v9.4.1
Statut : Détection confirmée
Un exercice d'exploitation contrôlé simulant un adversaire après un accès initial utilisant DirtyFrag (CVE-2026-43284), une vulnérabilité d'élévation de privilèges du noyau Linux. Scénario d'attaque simulé : un utilisateur à faibles privilèges (saskia, uid=1001) obtenu via des identifiants phishés tente d'élever ses privilèges à root sur une machine Linux interne. Le laboratoire a utilisé une machine Ubuntu volontairement vulnérable (ubuntu-vuln, noyau 6.0.0-22-generic) isolée des réseaux de production.
L'attaquant a obtenu un accès root complet (uid=0) dans l'environnement de laboratoire. La pile de détection Elastic Security a généré 3 alertes de sévérité ÉLEVÉE (score de risque 73) identifiant correctement la chaîne d'exploitation. Cependant, la détection a nécessité un réglage manuel - la configuration par défaut présentait des lacunes qui auraient entraîné des alertes manquées.
En résumé : Les machines Linux non patchées exécutant un noyau <= 6.x sont vulnérables. Si un attaquant possède déjà un point d'appui à faibles privilèges, cet exploit lui donne un accès root complet silencieusement et rapidement, sans aucune trace sur le disque visible par les outils d'intégrité des fichiers.
auditd par défaut ne produisent aucune preuve. Sans règles d'appel système explicites pour socket, splice et unshare, l'exploit s'exécute silencieusement - aucun journal, aucune alerte. Le déploiement par défaut d'Elastic Agent n'ajoute pas ces règles automatiquement.process.parent.pid comme clé de corrélation. En pratique, DirtyFrag crée des processus intermédiaires, brisant cette corrélation. La correction - corrélation par auditd.session - a été identifiée et implémentée au cours de cet exercice./etc/shadow, accédé aux clés autorisées SSH, écrit un marqueur de persistance (/root/pwned.txt) et initié une reconnaissance réseau via nc. Tout cela s'est produit dans les 30 minutes suivant l'accès initial.Objectif : Valider la couverture de détection pour l'exploitation de DirtyFrag dans le SOC du laboratoire personnel, identifier les lacunes de détection et produire une règle de détection corrigée fondée sur le comportement observé de l'attaquant.
Modèle de menace : Adversaire simulé après accès initial avec des identifiants phishés pour un compte utilisateur à faibles privilèges (saskia, uid=1001). Objectif : élévation de privilèges vers root via DirtyFrag en utilisant la voie de réception xfrm-ESP. Exécution en laboratoire effectuée via la console Proxmox - fonctionnellement équivalente à un accès SSH postérieur.
Deux niveaux de capacité de l'attaquant ont été considérés :
Cet exercice a couvert le niveau vanille.
DirtyFrag est le troisième d'une famille de bogues du noyau Linux apparentés qui exploitent tous la même cause racine :
Cause racine : Les chemins d'ajout de datagrammes IPv4/IPv6 ne définissaient pas SKBFL_SHARED_FRAG après que skb_splice_from_iter() ait placé une page du cache de pages dans un tampon de socket (sk_buff). Le chemin d'entrée ESP effectuait ensuite une cryptographie en place sur les pages du cache de pages contrôlées par l'attaquant, les traitant comme des skbs non linéaires ordinaires non clonés.
Primitive d'exploit : Écriture contrôlée de 4 ou 8 octets dans le cache de pages. Déterministe - aucune condition de concurrence requise.
Chaîne d'exploitation :
/usr/bin/su) dans le cache de pagessplice() de sorte que la page du fichier se trouve dans frag[0] de struct sk_buffxfrm-ESPexecve() du noyau - retournent des octets corrompusPropriété d'évasion critique : Le fichier sur disque n'est jamais modifié. Les outils de surveillance de l'intégrité des fichiers (AIDE, Tripwire, vérifications de hachage) indiquent tous que le fichier cible est sain.
Distributions vulnérables confirmées : Ubuntu 24.04.4, RHEL 10.1, AlmaLinux 10, openSUSE Tumbleweed, CentOS Stream 10, Fedora 44.
Télémétrie active au moment de l'exercice :
Réseau de laboratoire isolé avant l'exercice. Alias de pare-feu Cyber_escape_door confirmé désactivé - empêchant toute portée latérale vers d'autres VLAN ou Internet.

Noyau vulnérable et compte à faibles privilèges confirmés sur ubuntu-vuln :```
saskia@ubuntu-vuln:$ uname -r
6.0.0-22-generic
saskia@ubuntu-vuln:$ whoami
saskia
saskia@ubuntu-vuln:~$ id
uid=1001(saskia) gid=1001(saskia) groups=1001(saskia),27(sudo),100(users)

#### 4.3 Vérification de l'état de la pile
**Suricata : en cours d'exécution et capture (systemctl status)**

**Kibana/Elasticsearch : vert/bon état (vérification API)**

**Instantanés de VM pris avant toute modification**



#### 4.4 Capture réseau
tcpdump démarré sur l'hôte Suricata (`emp6s19`, longueur d'instantané 262144 octets) pour la capture complète des paquets du trafic `172.66.66.27`.


---
### 5. Phase 2 - Exécution de l'exploit
#### 5.1 Préparation
Code source de l'exploit (`exp.c`) du PoC V4bel/dirtyfrag placé sur l'hôte victime.```bash
ubuntu-vuln@ubuntu-vuln:~/dirtyfrag$ ls
exp exp.c README.md
ubuntu-vuln@ubuntu-vuln:~/dirtyfrag$ cp exp.c /tmp/exp/
Le terminal de droite montre la copie préparée visible pour l'utilisateur saskia.

Compilé en tant que root via sudo gcc (l'utilisateur saskia est dans sudoers):```
saskia@ubuntu-vuln:$ sudo gcc -o /tmp/exp/clean_frag /tmp/exp/exp.c
saskia@ubuntu-vuln:$ ls -la /tmp/exp/
-rwxr-xr-x 1 root root 62320 May 24 12:18 clean_frag
-rw-rw-r-- 1 ubuntu-vuln ubuntu-vuln 67803 May 24 12:13 exp.c
saskia@ubuntu-vuln:~$ id
uid=1001(saskia) gid=1001(saskia) groups=1001(saskia),27(sudo),100(users)
Binary: ELF 64-bit LSB exécutable, x86-64, lié dynamiquement.

#### 5.3 Exécution - Obtention du Root
Exploit exécuté en tant que `saskia` (uid=1001). Le processus (`clean_frag`, pid=4326) a ouvert des sockets `AF_RXRPC` et `AF_ALG`, puis a appelé `splice()` pour placer la page du cache de pages dans le tampon réseau. Le chiffrement en place sur le chemin ESP a écrit des octets contrôlés par l'attaquant dans le cache de pages. Un processus fils (pid=4327) a appelé `unshare` avec `a0=50000000` (CLONE_NEWUSER | CLONE_NEWNET) pour obtenir des capacités limitées à l'espace de noms.```
saskia@ubuntu-vuln:~$ /tmp/exp/clean_frag
...
root@ubuntu-vuln:~# id
uid=0(root) gid=0(root) groups=0(root)
root@ubuntu-vuln:~# ls
Full root shell obtained at 12:22 CEST (10:22 UTC), 24 May 2026.
Auditd syscall sequence captured (UTC):
All events share auditd.session=2.

Toutes les actions effectuées en tant que root sur ubuntu-vuln à partir d'environ 12:22 CEST.
root@ubuntu-vuln:# echo "Dirty Frag PoC executed at $(date)" > /root/pwned.txt
root@ubuntu-vuln:# cat /root/pwned.txt
Dirty Frag PoC executed at Sun May 24 12:28:05 PM CEST 2026

#### 6.2 Accès aux identifiants```bash
root@ubuntu-vuln:~# cat /etc/shadow | head -2
root:*:19823:0:99999:7:::
daemon:*:19823:0:99999:7:::
root@ubuntu-vuln:~# cat /home/ubuntu-vuln/.ssh/authorized_keys
ssh-ed25519 AAAA[...]
Fichier shadow et clés autorisées SSH consultés. Dans une compromission réelle, cela permet le cassage de mots de passe hors ligne et un accès persistant par backdoor SSH.

root@ubuntu-vuln:~# nc -vz 172.66.66.1 100-8000
Port scan de la passerelle du laboratoire (`172.66.66.1`, ports 100-8000) exécuté depuis le contexte root à 12:51 CEST. Cela démontre une capacité de mouvement latéral et de cartographie réseau à partir d'une session escaladée.

#### 6.4 Visibilité réseau Suricata
Suricata a capturé **105 événements** associés à `172.66.66.27` pendant la fenêtre d'exercice (88 mdns, 17 flow). Au moment où la capture d'écran a été prise (pendant le scan nc vers ~12:53 CEST), 29 événements étaient visibles dans le tableau de bord.
L'exploit DirtyFrag lui-même est interne au noyau et ne produit aucune signature réseau - la primitive d'escalade ne génère aucun trafic anormal sur le fil. Suricata a été utilisé pour collecter des pcaps du trafic post-exploitation et des tentatives d'énumération plutôt que pour détecter l'exploit lui-même.
| Type d'événement | Nombre |
| ---------- | ----- |
| mdns | 88 |
| flow | 17 |
| **Total** | **105** |
Les événements flow se regroupent autour de la fenêtre de scan de port nc (10:50-11:10 UTC / 12:50-13:10 CEST).

---
### 7. Phase 4 - Ingénierie de détection
#### 7.1 Règles de détection déployées
Deux règles de séquence EQL ont été créées dans le moteur de détection Kibana, basées sur l'article d'Elastic Security Labs.
**Règle 1 - Socket AF_RXRPC ou AF_ALG avec Splice suivi d'une exécution (n'a pas déclenché - voir l'écart ci-dessous)**```eql
sequence with maxspan=60s
[any where host.os.type == "linux" and
(
(event.category == "process" and auditd.data.syscall == "socket"
and auditd.data.a0 in ("26", "21")) or
(event.category == "process" and auditd.data.syscall == "splice") or
(event.category == "network" and event.action == "bound-socket"
and data_stream.dataset == "auditd_manager.auditd"
and auditd.data.socket.family == "38")
)
and user.id != "0"] by process.pid, host.id, user.id with runs=10
[process where host.os.type == "linux" and event.action == "executed" and
(
(user.effective.id == "0" and user.id != "0") or
(process.name in ("bash", "sh", "zsh", "dash", "fish", "ksh", "busybox")
and process.args in ("-c", "--command", "-ic", "-ci", "-cl", "-lc",
"-bash", "-sh", "-zsh", "-dash", "-fish", "-ksh"))
)] by process.parent.pid, host.id, user.id
Règle 2 - Manipulation de l'espace de noms suivie d'une escalade de privilèges (déclenché après correction)```eql sequence by host.id, auditd.session with maxspan=30s [process where host.os.type == "linux" and auditd.data.syscall == "unshare" and auditd.data.a0 in ("10000000", "50000000", "70000000", "10020000", "50020000", "70020000") and user.id != "0" and user.id != null] [process where host.os.type == "linux" and user.id == "0" and user.id != null and (process.name in ("bash", "sh", "zsh", "dash", "fish", "ksh", "su", "sudo", "pkexec", "busybox", "mksh") or process.name like ("python*", "perl*", "ruby*", "php*", "lua*"))]
#### 7.2 Écart de détection initial - Couverture manquante des appels système
La règle 1 n'a retourné aucun résultat. La configuration par défaut d'auditd sur `ubuntu-vuln` a capturé : `write`, `bpf`, `openat`, `unshare` uniquement.
Manquants : `socket`, `splice`, `bind` - les primitives d'exploitation principales.
**Remédiation :** Ajouté à `/etc/audit/rules.d/dirtyfrag.rules` :```
-a always,exit -F arch=b64 -S socket -k socket_syscall
-a always,exit -F arch=b32 -S socketcall -k socket_syscall
-a always,exit -F arch=b64 -S splice -k splice-syscall
-a always,exit -F arch=b32 -S splice -k splice-syscall
-a always,exit -F arch=b64 -S unshare -k unshare-syscall
-a always,exit -F arch=b32 -S unshare -k unshare-syscall
272 nouveaux événements auditd sont arrivés dans Elasticsearch en quelques secondes après l'exécution de l'exploit.
Bien que les événements aient été capturés, la règle 2 n'a renvoyé aucune correspondance.
Cause racine - arborescence réelle des processus (depuis Elasticsearch) :
La règle d'origine corrélait par process.parent.pid. L'événement unshare avait ppid=4326, mais le shell root avait un parent différent - DirtyFrag effectue un fork de processus intermédiaires, brisant la corrélation.
Correctif : Corréler par auditd.session. Tous les événements de la chaîne d'exploitation partageaient session=2, reliant correctement la séquence quelle que soit la profondeur de fork.
3 alertes de sévérité ÉLEVÉE, score de risque 73, dans Kibana Security - Alertes à 2026-05-24T10:25:57Z UTC (12:25:57 CEST).
| Alerte | Horodatage (UTC) | Signal |
|---|---|---|
| Correspondance de séquence - événement unshare | 10:25:57.869 | process=clean_frag, user=saskia (uid=1001) |
| Correspondance de séquence - shell root | 10:25:57.881 | process=bash, user=root (uid=0) |
DirtyFrag - Manipulation de Namespace suivie d'une escalade de privilègesubuntu-vuln

Tous les événements auditd liés à dirtyfrag :```kql data_stream.dataset: "auditd_manager.auditd" and auditd.summary.how: dirtyfrag
**Namespace manipulation by non-root:**```kql
data_stream.dataset: "auditd_manager.auditd" and auditd.data.syscall: "unshare" and auditd.data.a0: "50000000"
Activité root post-élévation dans la même session :```kql data_stream.dataset: "auditd_manager.auditd" and user.id: "0" and auditd.session: "2"
**Tous les événements auditd pendant la fenêtre d'exploitation :**```kql
data_stream.dataset: "auditd_manager.auditd" and @timestamp >= "2026-05-24T10:20:00Z" and @timestamp <= "2026-05-24T10:30:00Z"
Socket et splice syscalls depuis le processus d'exploit :```kql data_stream.dataset: "auditd_manager.auditd" and auditd.summary.how: "/tmp/exp/clean_frag"
### 9. Résumé des conclusions clés
| # | Constat | Gravité | Corrigé |
| - | ------- | -------- | ---------- |
| 1 | Les règles auditd par défaut manquent `socket`, `splice`, `unshare` - aucune preuve sans règles explicites | Critique | Oui - règles personnalisées déployées dans `/etc/audit/rules.d/dirtyfrag.rules` |
| 2 | La corrélation EQL par `process.parent.pid` échoue pour les chaînes d'exploitation bifurquées | Élevée | Oui - changé en `auditd.session` |
| 3 | La surveillance de l'intégrité des fichiers aveugle à DirtyFrag - les fichiers sur disque inchangés tout au long | Élevée | Aucune correction dans le périmètre - corriger le noyau est la solution |
| 4 | Post-exploitation : fichier shadow, clés SSH et reconnaissance réseau tous exécutés en tant que root avant toute réponse basée sur les alertes | Élevée | Détecté via les logs auditd ; aucune règle dédiée déclenchée pour l'accès aux identifiants ou la phase de reconnaissance |
---
### 10. Références
- Elastic Security Labs: [Copy Fail et DirtyFrag : Bugs de pages Linux dans la nature](https://www.elastic.co/security-labs/copy-fail-dirtyfrag-linux-page-bugs-in-the-wild)
- PoC public : [V4bel/dirtyfrag sur GitHub](https://github.com/V4bel/dirtyfrag)
- MITRE ATT&CK : [T1068 - Exploitation pour l'élévation de privilèges](https://attack.mitre.org/techniques/T1068/)
- MITRE ATT&CK : [TA0004 - Élévation de privilèges](https://attack.mitre.org/tactics/TA0004/)
- MITRE ATT&CK : [T1003.008 - /etc/passwd et /etc/shadow](https://attack.mitre.org/techniques/T1003/008/)
| Élément | Détail |
|---|
| Vulnérabilité | DirtyFrag - CVE-2026-43284 (voie xfrm-ESP), CVE-2026-43500 (voie RxRPC) |
| Impact CVSS | Élévation de privilèges locale complète vers root |
| Noyaux affectés | Noyau Linux >= janvier 2017 (durée de vie de 9 ans) |
| Distributions affectées | Ubuntu 24.04.4, RHEL 10.1, AlmaLinux 10, openSUSE, CentOS Stream 10, Fedora 44 |
| Contournement d'intégrité des fichiers | Fichiers sur disque inchangés - AIDE, Tripwire, surveillance de hachage indiquent tous un état sain |
| PoC public | Disponible (V4bel/dirtyfrag sur GitHub) |
| Temps jusqu'à root | < 10 minutes à partir d'un point d'appui à faibles privilèges |
| Priorité | Action |
|---|
| Immédiate | Patcher le noyau Linux vers une version corrigée sur tous les hôtes. Appliquer les avis de sécurité des éditeurs pour Ubuntu, RHEL, AlmaLinux, openSUSE, CentOS, Fedora. |
| Court terme | Déployer des règles explicites d'appel système auditd pour socket, splice et unshare sur tous les hôtes Linux surveillés par Elastic Agent. |
| Court terme | Mettre à jour les règles de détection EQL pour corréler par auditd.session plutôt que par process.parent.pid pour les séquences de manipulation d'espaces de noms. |
| En continu | Traiter les alertes d'élévation de privilèges avec un score de risque >= 73 comme une priorité élevée de triage. La règle actuelle se déclenche en quelques secondes après l'élévation. |
| CVE | Surnom | Introduit | Chemin |
|---|
| CVE-2022-0847 | Dirty Pipe | - | pipe |
| CVE-2026-31431 | Copy Fail | - | splice |
| CVE-2026-43284 | DirtyFrag | Jan 2017 (commit cac2661c53f3) | xfrm-ESP |
| CVE-2026-43500 | DirtyFrag | Juin 2023 (commit 2dc334f1a63a) | RxRPC |
| Composant | Détails |
|---|
| Hôte Fleet Server / ELK | ubuntu-tm / elk-docker - 172.66.66.30 (interne au laboratoire) |
| Elastic Stack | v9.4.1 (Elasticsearch, Kibana, Logstash - Docker) |
| Hôte cible (victime) | ubuntu-vuln - 172.66.66.27 |
| Noyau de la victime | 6.0.0-22-generic (vulnérable) |
| Utilisateur de la victime | saskia - uid=1001, groupes=1001(saskia),27(sudo),100(users) |
| Elastic Agent | v9.4.1 avec auditbeat |
| IDS réseau | Suricata 8.0.5 sur capteur dédié |
| Kibana | http://172.66.66.30:5601 (interne au laboratoire) |
| Ensemble de données | Part |
|---|
suricata.eve | 75,2 % |
elastic_agent.fleet_server | 10,8 % |
elastic_agent | 9,1 % |
elastic_agent.filebeat | 1,9 % |
elastic_agent.auditbeat | 1,4 % |
auditd_manager.auditd | 0,5 % |
system.auth | 0,2 % |
system.syslog | 0,1 % |
| Timestamp (UTC) | Syscall | Argument | Process (pid) | User |
|---|
| 10:21:11.054 | unshare | a0=50000000 | clean_frag (4327) | saskia (1001) |
| 10:21:11.055 | socket | a0=21 (AF_RXRPC) | clean_frag (4326) | saskia (1001) |
| 10:21:17.403 | socket | a0=26 (AF_ALG) | clean_frag (4326) | saskia (1001) |
| 10:21:17.419 | splice | a0=4 | clean_frag (4326) | saskia (1001) |
| 10:21:17.419 | splice | a0=7 | clean_frag (4326) | saskia (1001) |
| Processus | PID | PPID | UID | auditd.session |
|---|
| shell (saskia) | 4182 | - | 1001 | 2 |
clean_frag (main) | 4326 | 4182 | 1001 | 2 |
clean_frag (unshare child) | 4327 | 4326 | 1001 | 2 |
bash (root) | - | 4451 | 0 | 2 |
| Alerte de séquence composite | 10:25:57.893 | host=ubuntu-vuln |
| Tactique | Technique | ID |
|---|
| Élévation de privilèges | Exploitation pour l'élévation de privilèges | T1068 |
| Élévation de privilèges | Échappement vers l'hôte | T1611 |
| Accès aux identifiants | Extraction d'identifiants du système d'exploitation : /etc/passwd et /etc/shadow | T1003.008 |
| Découverte | Découverte des services réseau | T1046 |
| Appel système | Argument | Signification |
|---|
unshare | a0=50000000 | CLONE_NEWUSER | CLONE_NEWNET |
socket | a0=26 | AF_ALG (sous-système crypto du noyau) |
socket | a0=21 | AF_RXRPC (chemin RxRPC) |
bind | socket.family=38 | Liaison AF_ALG |
splice | - | Injection de pages dans les tampons réseau |