
Bloccare la catena di LPE Linux DirtyFrag (CVE-2026-43284 / CVE-2026-43500) in fase di esecuzione con una Cilium Tetragon TracingPolicy
Bloccare la catena di escalation dei privilegi Linux DirtyFrag (CVE-2026-43284 / CVE-2026-43500) in esecuzione con una TracingPolicy di Cilium Tetragon. La policy SIGKILLa il proof-of-concept pubblico al suo passo di setup del socket, prima che raggiunga la scrittura nella cache di pagina che gli darebbe root.
Cos'è questo. Una descrizione di laboratorio. Ho configurato Tetragon per la prima volta e volevo vedere se poteva effettivamente fermare una reale e attuale LPE del kernel. Poteva. Questo documenta esattamente cosa ho eseguito, cosa è scattato e, altrettanto importante, i limiti di ciò che dimostra. Non è un sostituto per le patch.
File: questo README (autocontenuto, evidenza inline) · block-dirtyfrag.yaml (la policy)
| Exploit | DirtyFrag - PoC pubblico: V4bel/dirtyfrag |
| CVE | CVE-2026-43284 (scrittura cache di pagina xfrm-ESP), CVE-2026-43500 (scrittura cache di pagina RxRPC) |
| Host | Ubuntu 24.04.4 LTS, Tetragon v1.7.0 (standalone, systemd) |
Base - nessuna policy, kernel 6.8.0-88 (vulnerabile) | PoC → uid=0(root) |
Con policy - kernel 6.8.0-88 | PoC SIGKILLato a socket(AF_RXRPC); utente rimane uid=1000 |
Kernel patchato 6.8.0-134 | PoC fallisce da solo (rc=4); l'hook del socket scatta ancora, rilevamento, non mitigazione (nota) |
| Natura del controllo | Controllo compensativo / patch virtuale, non una correzione del kernel |
DirtyFrag è una catena di escalation dei privilegi locale costruita da due primitive indipendenti di scrittura nella cache di pagina nel kernel Linux: una nel percorso di decrittazione in-place xfrm/ESP (IPsec) (CVE-2026-43284), e una nel percorso RxRPC (CVE-2026-43500). Ognuna permette a un utente locale senza privilegi di scrivere byte controllati dall'attaccante in pagine della cache di pagina in sola lettura, ad esempio l'immagine in cache di un binario setuid-root come /bin/su - e da lì ottenere root. La scrittura avviene solo in memoria; il file su disco non viene mai modificato, quindi il monitoraggio dell'integrità dei file non vede nulla. È la stessa classe di bug di Dirty Pipe e Copy Fail. Le due CVE sono concatenate deliberatamente: se un percorso non è disponibile in un dato ambiente, l'altro funziona ancora.
Su questo host, gli user namespace non privilegiati sono limitati da AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1), che blocca la metà ESP della catena. Questo lascia il percorso RxRPC, che apre un socket AF_RXRPC (famiglia di indirizzi 33) - e questo è il passo che questa policy uccide.
La vera soluzione è un kernel patchato. Tutto qui è una soluzione temporanea per un host che non puoi ancora patchare. Ubuntu ha rilasciato correzioni per DirtyFrag qualche tempo prima di questo test, quindi questo è un N-day noto, non uno 0-day attivo - il punto è mostrare cosa può fare un controllo a runtime su un host che sta ancora, per qualsiasi motivo, eseguendo un kernel vulnerabile.
Policy completa: block-dirtyfrag.yaml. Installa due kprobe.
Hook 1 - socket di grooming (sys_socket). Il controllo portante. Il percorso RxRPC dell'exploit deve chiamare socket(AF_RXRPC, …) su ogni esecuzione, indipendentemente dal fatto che un modulo del kernel sia già caricato. La policy matcha sulla famiglia di indirizzi:
AF_RXRPC) → Sigkill. Su qualsiasi host che non sia un client AFS, effettivamente nulla di legittimo apre un socket AF_RXRPC, quindi un kill generalizzato qui è sicuro e ad alta fiducia.AF_ALG) → Post (solo audit, nessun kill). AF_ALG è l'API di crittografia del kernel in spazio utente e ha utenti legittimi (cryptsetup, strumenti libkcapi, alcuni workflow FIPS). Uccidere solo sulla famiglia causerebbe falsi positivi, quindi questa gamba solo registra. Il percorso per l'applicazione è: audita per un po', costruisci una lista consentita NotIn dai binari effettivamente osservati, poi considera di promuovere a Sigkill.Hook 2 - caricamento automatico del modulo vulnerabile (security_kernel_module_request). Difesa in profondità. Scatta quando al kernel viene chiesto di caricare automaticamente una famiglia di moduli vulnerabili (esp4, esp6, rxrpc, gli alias di socket net-pf-33/net-pf-38, i template crittografici pcbc/fcrypt). Limitazione: scatta solo quando il modulo non è già residente — dopo il primo run dell'exploit in un avvio, quei moduli sono caricati e questo hook tace. È uno strato a freddo, non il controllo principale.
Insieme: Hook 1 cattura l'exploit sia che i moduli siano caldi o freddi; Hook 2 aggiunge uno scatto più precoce e specifico su un host freddo.
6.8.0-88-generic, che è vulnerabile (la baseline raggiunge root). La macchina ha anche 6.8.0-134-generic installato — il kernel patchato — e questo è stato confermato direttamente: riavviando in -134 il PoC fallisce da solo (rc=4, nessun root), con o senza la policy (vedi la nota sotto). Per riprodurre la demo di mitigazione, avvia 6.8.0-88 da GRUB (è ancora installato) — su qualsiasi kernel patchato non c'è nulla da mitigare.AF_ALG.lockdown,capability,landlock,yama,apparmor — no bpf). Quindi l'applicazione usa Sigkill dalla kprobe, non un rifiuto LSM in-kernel. Il kill atterra alla syscall socket() — prima della primitiva di scrittura — quindi termina il processo a un passo obbligatorio precoce piuttosto che "bloccare la vulnerabilità" stessa.I passi 1–5 sono tutti dallo stesso avvio 6.8.0-88 (il kernel vulnerabile).
1. Ambiente - Ubuntu 24.04.4, kernel 6.8.0-88-generic, Tetragon v1.7.0 attivo sotto systemd, userns non privilegiati limitati.
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active
2. Precondizioni (policy OFF) - punto di partenza pulito: nessun modulo vulnerabile residente, nessuno stato xfrm, nessuna policy caricata.
$ 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. Baseline (policy OFF) - l'exploit funziona. Questo è ciò che prova che il kernel è effettivamente vulnerabile; l'intera descrizione si basa su questo.
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)
4. Carica la policy.
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added
5. Applicazione (policy ON) - il kill. L'exploit viene terminato da segnale alla chiamata socket() e non raggiunge mai su:
🚀 process /home/…/dirtyfrag/exp
❓ syscall /home/…/dirtyfrag/exp __x64_sys_socket
Kernel:
0x0: __x64_sys_socket+0x5
0x0: do_syscall_64+0x7f
0x0: entry_SYSCALL_64_after_hwframe+0x78
💥 exit /home/…/dirtyfrag/exp SIGKILL
L'evento strutturato conferma sia il match che il kill - un process_kprobe sulla syscall del socket, poi un process_exit per segnale dello stesso PID:
// process_kprobe — the AF_RXRPC match
{ "process_kprobe": {
"process": { "pid": 24083, "uid": 1000, "binary": ".../dirtyfrag/exp" },
"function_name": "__x64_sys_socket",
"args": [ { "int_arg": 33, "label": "family" } ],
"policy_name": "block-dirtyfrag",
"action": "KPROBE_ACTION_POST"
} }
// process_exit — same PID, killed by signal
{ "process_exit": {
"process": { "pid": 24083, "binary": ".../dirtyfrag/exp" },
"signal": "SIGKILL"
} }
L'action della kprobe legge KPROBE_ACTION_POST perché l'azione Post è ciò che emette l'evento visibile; l'azione Sigkill è ciò che produce la separata uscita SIGKILL. I due eventi insieme sono la prova, il match e il kill.
Nota: la cattura grezza
07porta anche una stringamessageda una revisione precedente della policy (prima che la gambaAF_ALGfosse separata in solo audit). Il matchfamily: 33e il risultanteSIGKILLsono identici tra le revisioni; solo quel testo differisce.
Dopo l'esecuzione sul kernel vulnerabile, l'host è stato riavviato in 6.8.0-134-generic (il kernel patchato di Ubuntu) e il PoC è stato eseguito di nuovo:
$ ./exp # policy OFF
dirtyfrag: failed (rc=4) # exploit fails on its own — no root
$ ./exp # policy ON
Killed # SIGKILL at socket(AF_RXRPC)
Essendo precisi su cosa mostra:
rc=4, nessun root) perché il kernel è patchato. Non c'è nulla che la policy possa prevenire. Il prima/dopo che porta l'affermazione di mitigazione esiste solo sul kernel vulnerabile -88.socket(AF_RXRPC), quindi Tetragon lo SIGKILLa ancora e segnala il tentativo nel log, su un kernel patchato come su uno non patchato. Questo ha valore come rilevamento di un tentativo e come difesa in profondità, ma non è la stessa cosa di fermare un exploit funzionante.AF_RXRPC è legittimo e un kill generalizzato lo romperebbe. Verifica per nodo.AF_ALG è solo audit per progettazione. Una variante che usa solo il percorso AF_ALG con moduli già caldi sarebbe registrata, non uccisa, finché non promuovi quella gamba a enforce (dopo l'allowlisting).AF_RXRPC viene aperto prima della primitiva di scrittura. Quell'ordine è ciò che rende efficace un kill precoce; non è una garanzia per ogni possibile exploit.Setup: Tetragon v1.7.0 standalone, avviato tramite systemctl; policy caricate con tetra tracingpolicy add.