
Blocage de la chaîne DirtyFrag LPE Linux (CVE-2026-43284 / CVE-2026-43500) au moment de l'exécution avec un TracingPolicy Cilium Tetragon
Bloquer la chaîne d'élévation de privilèges Linux DirtyFrag (CVE-2026-43284 / CVE-2026-43500) à l'exécution avec une TracingPolicy Cilium Tetragon. La politique SIGKILL la preuve de concept publique lors de l'étape de configuration de sa socket, avant qu'elle n'atteigne l'écriture dans le cache de pages qui lui donnerait root.
Ce que c'est. Un compte-rendu de laboratoire. J'ai configuré Tetragon pour la première fois et je voulais voir s'il pouvait réellement arrêter une véritable LPE (élévation de privilèges locale) actuelle du noyau. Il le pouvait. Ce document décrit exactement ce que j'ai exécuté, ce qui s'est déclenché et, tout aussi important, les limites de ce qu'il prouve. Ce n'est pas un substitut à l'application de correctifs.
Fichiers : ce README (autonome, preuves intégrées) · block-dirtyfrag.yaml (la politique)
| Exploit | DirtyFrag - PoC publique : V4bel/dirtyfrag |
| CVE | CVE-2026-43284 (écriture dans le cache de pages xfrm-ESP), CVE-2026-43500 (écriture dans le cache de pages RxRPC) |
| Hôte | Ubuntu 24.04.4 LTS, Tetragon v1.7.0 (autonome, systemd) |
Référence - sans politique, noyau 6.8.0-88 (vulnérable) | PoC → uid=0(root) |
Avec politique - noyau 6.8.0-88 | PoC SIGKILLé à socket(AF_RXRPC) ; l'utilisateur reste uid=1000 |
Noyau corrigé 6.8.0-134 | PoC échoue seul (rc=4) ; le crochet socket se déclenche toujours, détection, pas atténuation (remarque) |
| Nature du contrôle | Contrôle compensatoire / correctif virtuel, pas un correctif du noyau |
DirtyFrag est une chaîne d'élévation de privilèges locale construite à partir de deux primitives indépendantes d'écriture dans le cache de pages du noyau Linux : l'une dans le chemin de déchiffrement en place xfrm/ESP (IPsec) (CVE-2026-43284), et l'autre dans le chemin RxRPC (CVE-2026-43500). Chacune permet à un utilisateur local non privilégié d'écrire des octets contrôlés par l'attaquant dans des pages du cache de pages en lecture seule, par exemple l'image mise en cache d'un binaire setuid-root comme /bin/su – et d'obtenir ainsi root. L'écriture se fait uniquement en mémoire ; le fichier sur le disque n'est jamais modifié, donc la surveillance de l'intégrité des fichiers ne voit rien. Il s'agit de la même classe de bugs que Dirty Pipe et Copy Fail. Les deux CVE sont enchaînées délibérément : si un chemin n'est pas disponible dans un environnement donné, l'autre fonctionne toujours.
Sur cet hôte, les espaces de noms utilisateur non privilégiés sont restreints par AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1), ce qui bloque la moitié ESP de la chaîne. Il reste le chemin RxRPC, qui ouvre une socket AF_RXRPC (famille d'adresse 33) – et c'est cette étape que cette politique tue.
Le vrai correctif est un noyau patché. Tout ceci est une solution de contournement pour un hôte que vous ne pouvez pas encore corriger. Ubuntu a livré les correctifs DirtyFrag quelque temps avant ce test, il s'agit donc d'un N-day connu, pas d'un 0-day en vie – le but est de montrer ce qu'un contrôle à l'exécution peut faire sur un hôte qui, pour une raison ou une autre, exécute encore un noyau vulnérable.
Politique complète : block-dirtyfrag.yaml. Elle installe deux kprobes.
Crochet 1 – sockets de préparation (sys_socket). Le contrôle principal. Le chemin RxRPC de l'exploit doit appeler socket(AF_RXRPC, …) à chaque exécution, qu'un module noyau soit déjà chargé ou non. La politique vérifie la famille d'adresse :
AF_RXRPC) → Sigkill. Sur tout hôte qui n'est pas un client AFS, pratiquement rien de légitime n'ouvre une socket AF_RXRPC, donc un kill aveugle ici est sûr et à haute confiance.AF_ALG) → Post (audit uniquement, pas de kill). AF_ALG est l'API de chiffrement noyau côté espace utilisateur et a des utilisateurs légitimes (cryptsetup, outils libkcapi, certains workflows FIPS). Tuer sur la seule famille entraînerait des faux positifs, donc cette branche se contente de journaliser. La voie vers le contrôle est : auditer pendant un certain temps, construire une liste d'autorisation NotIn à partir des binaires effectivement observés, puis envisager de passer à Sigkill.Crochet 2 – chargement automatique de modules vulnérables (security_kernel_module_request). Défense en profondeur. Se déclenche lorsque le noyau est invité à charger automatiquement une famille de modules vulnérables (esp4, esp6, rxrpc, les alias de sockets net-pf-33/net-pf-38, les modèles de chiffrement pcbc/fcrypt). Limite : il ne se déclenche que lorsque le module n'est pas déjà résident — après la première exécution de l'exploit dans un démarrage, ces modules sont chargés et ce crochet devient silencieux. C'est une couche de démarrage à froid, pas le contrôle principal.
Ensemble : le crochet 1 attrape l'exploit que les modules soient chauds ou froids ; le crochet 2 ajoute un déclenchement plus précoce et plus spécifique sur un hôte froid.
6.8.0-88-generic, qui est vulnérable (la référence atteint root). La machine dispose également de 6.8.0-134-generic installé — le noyau corrigé — et cela a été confirmé directement : redémarrer sur -134 fait échouer la PoC seule (rc=4, pas de root), avec ou sans politique (voir la remarque ci-dessous). Pour reproduire la démonstration d'atténuation, démarrez 6.8.0-88 depuis GRUB (il est toujours installé) — sur tout noyau corrigé, il n'y a rien à atténuer.AF_ALG.lockdown,capability,landlock,yama,apparmor — pas de bpf). Donc l'application utilise Sigkill depuis la kprobe, pas un refus LSM dans le noyau. Le kill a lieu à l'appel système socket() — avant la primitive d'écriture — donc il termine le processus à une étape précoce obligatoire plutôt que de "bloquer la vulnérabilité" elle-même.Les étapes 1 à 5 proviennent toutes du même démarrage 6.8.0-88 (le noyau vulnérable).
1. Environnement - Ubuntu 24.04.4, noyau 6.8.0-88-generic, Tetragon v1.7.0 actif sous systemd, espaces utilisateur non privilégiés restreints.
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active
2. Préconditions (politique OFF) - point de départ propre : aucun module vulnérable résident, pas d'état xfrm, pas de politique chargée.
$ lsmod | grep -E 'esp4|esp6|rxrpc' || echo "(none loaded)"
(none loaded)
$ sudo ip xfrm state # (empty)
$ sudo tetra tp list
ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY MODE NPOST NENFORCE NMONITOR
3. Référence (politique OFF) - l'exploit fonctionne. C'est ce qui prouve que le noyau est réellement vulnérable ; tout le compte-rendu repose là-dessus.
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)
4. Charger la politique.
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added