
Bloqueando la cadena DirtyFrag de LPE en Linux (CVE-2026-43284 / CVE-2026-43500) en tiempo de ejecución con un TracingPolicy de Cilium Tetragon
Bloqueando la cadena de escalada de privilegios DirtyFrag de Linux (CVE-2026-43284 / CVE-2026-43500) en tiempo de ejecución con una TracingPolicy de Cilium Tetragon. La política mata con SIGKILL la prueba de concepto pública en su paso de configuración del socket, antes de que alcance la escritura en la caché de páginas que le otorgaría acceso root.
Qué es esto. Es un informe de laboratorio. Configuré Tetragon por primera vez y quería comprobar si realmente podía detener una LPE real del kernel actual. Lo logró. Este documento registra exactamente lo que ejecuté, qué se disparó y, igual de importante, los límites de lo que demuestra. No es un sustituto de la aplicación de parches.
Archivos: este README (autocontenido, evidencia en línea) · block-dirtyfrag.yaml (la política)
| Exploit | DirtyFrag - PoC público: V4bel/dirtyfrag |
| CVE | CVE-2026-43284 (escritura en caché de páginas xfrm-ESP), CVE-2026-43500 (escritura en caché de páginas RxRPC) |
| Anfitrión | Ubuntu 24.04.4 LTS, Tetragon v1.7.0 (independiente, systemd) |
Línea base - sin política, kernel 6.8.0-88 (vulnerable) | PoC → uid=0(root) |
Con política - kernel 6.8.0-88 | PoC SIGKILL en socket(AF_RXRPC); el usuario sigue siendo uid=1000 |
Kernel parcheado 6.8.0-134 | PoC falla por sí mismo (rc=4); el hook del socket aún se dispara, detección, no mitigación (nota) |
| Naturaleza del control | Control compensatorio / parche virtual, no una corrección del kernel |
DirtyFrag es una cadena de escalada de privilegios local construida a partir de dos primitivas independientes de escritura en la caché de páginas del kernel de Linux: una en la ruta de descifrado in situ de xfrm/ESP (IPsec) (CVE-2026-43284), y otra en la ruta de RxRPC (CVE-2026-43500). Cada una permite que un usuario local sin privilegios escriba bytes controlados por el atacante en páginas de la caché de páginas de solo lectura, por ejemplo, la imagen en caché de un binario setuid-root como /bin/su – y desde allí obtener acceso root. La escritura se produce solo en memoria; el archivo en disco nunca se modifica, por lo que el monitoreo de integridad de archivos no ve nada. Es la misma clase de error que Dirty Pipe y Copy Fail. Las dos CVE se encadenan deliberadamente: si una ruta no está disponible en un entorno dado, la otra sigue funcionando.
En este anfitrión, los espacios de nombres de usuario sin privilegios están restringidos por AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1), lo que bloquea la mitad ESP de la cadena. Esto deja la ruta RxRPC, que abre un socket AF_RXRPC (familia de direcciones 33) – y ese es el paso que esta política mata.
La verdadera solución es un kernel parcheado. Todo esto es una solución provisional para un anfitrión que aún no puede parchear. Ubuntu envió correcciones para DirtyFrag algún tiempo antes de esta prueba, por lo que esto es un N-day conocido, no un 0-day activo – el objetivo es mostrar lo que un control en tiempo de ejecución puede hacer en un anfitrión que aún, por cualquier motivo, ejecuta un kernel vulnerable.
Política completa: block-dirtyfrag.yaml. Instala dos kprobes.
Hook 1 – sockets de preparación (sys_socket). El control principal. La ruta RxRPC del exploit debe llamar a socket(AF_RXRPC, …) en cada ejecución, independientemente de si el módulo del kernel ya está cargado. La política coincide con la familia de direcciones:
AF_RXRPC) → Sigkill. En cualquier anfitrión que no sea un cliente AFS, prácticamente nada legítimo abre un socket AF_RXRPC, por lo que una eliminación general aquí es segura y de alta confianza.AF_ALG) → Post (solo auditoría, sin eliminación). AF_ALG es la API de criptografía del kernel para espacio de usuario y tiene usuarios legítimos (cryptsetup, herramientas libkcapi, algunos flujos de trabajo FIPS). Matar solo por la familia causaría falsos positivos, por lo que esta rama solo registra. El camino hacia la aplicación es: auditar durante un tiempo, construir una lista de permitidos NotIn a partir de los binarios realmente observados, y luego considerar promover a Sigkill.Hook 2 – carga automática de módulos vulnerables (security_kernel_module_request). Defensa en profundidad. Se dispara cuando se le pide al kernel que cargue automáticamente una familia de módulos vulnerables (esp4, esp6, rxrpc, los alias de socket net-pf-33/net-pf-38, las plantillas criptográficas pcbc/fcrypt). Limitación: solo se dispara cuando el módulo aún no está residente — después de la primera ejecución del exploit en un arranque, esos módulos están cargados y este hook queda en silencio. Es una capa de arranque en frío, no el control principal.
Juntos: Hook 1 atrapa al exploit tanto si los módulos están calientes como fríos; Hook 2 añade un disparo más temprano y más específico en un anfitrión frío.
6.8.0-88-generic, que es vulnerable (la línea base alcanza root). La máquina también tiene instalado 6.8.0-134-generic — el kernel parcheado — y esto se confirmó directamente: reiniciar en -134 hace que el PoC falle por sí solo (rc=4, sin root), con o sin la política (ver la nota a continuación). Para reproducir la demostración de mitigación, arranque 6.8.0-88 desde GRUB (aún está instalado) — en cualquier kernel parcheado no hay nada que mitigar.AF_ALG.lockdown,capability,landlock,yama,apparmor — sin bpf). Por lo tanto, la aplicación usa Sigkill desde el kprobe, no una denegación LSM en el kernel. La eliminación ocurre en la llamada al sistema socket() — de la primitiva de escritura — por lo que termina el proceso en un paso temprano obligatorio en lugar de "bloquear la vulnerabilidad" en sí.Los pasos 1–5 son todos del mismo arranque 6.8.0-88 (el kernel vulnerable).
1. Entorno – Ubuntu 24.04.4, kernel 6.8.0-88-generic, Tetragon v1.7.0 activo bajo systemd, userns sin privilegios restringido.
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active
2. Precondiciones (política DESACTIVADA) – punto de partida limpio: ningún módulo vulnerable residente, sin estado xfrm, sin política cargada.
$ lsmod | grep -E 'esp4|esp6|rxrpc' || echo "(ninguno cargado)"
(ninguno cargado)
$ sudo ip xfrm state # (vacío)
$ sudo tetra tp list
ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY MODE NPOST NENFORCE NMONITOR
3. Línea base (política DESACTIVADA) – el exploit funciona. Esto es lo que demuestra que el kernel es realmente vulnerable; todo el informe se basa en ello.
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)
4. Cargar la política.
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added
5. Aplicado (política ACTIVADA) – la eliminación. El exploit es terminado por señal en la llamada socket() y nunca llega a 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
El evento estructurado confirma tanto la coincidencia como la eliminación – un process_kprobe en la llamada al sistema socket, luego un process_exit por señal para el mismo PID:
// process_kprobe — la coincidencia AF_RXRPC
{ "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 — mismo PID, eliminado por señal
{ "process_exit": {
"process": { "pid": 24083, "binary": ".../dirtyfrag/exp" },
"signal": "SIGKILL"
} }
La action del kprobe dice KPROBE_ACTION_POST porque la acción Post es la que emite el evento visible; la acción Sigkill es la que produce la salida SIGKILL separada. Los dos eventos juntos son la prueba, la coincidencia y la eliminación.
Nota: la captura sin formato
07también lleva una cadenamessagede una revisión anterior de la política (antes de que la ramaAF_ALGse dividiera a solo auditoría). La coincidenciafamily: 33y elSIGKILLresultante son idénticos en todas las revisiones; solo ese texto difiere.
Después de la ejecución con el kernel vulnerable, el anfitrión se reinició en 6.8.0-134-generic (el kernel parcheado de Ubuntu) y el PoC se ejecutó nuevamente:
$ ./exp # política DESACTIVADA
dirtyfrag: failed (rc=4) # exploit falla por sí solo — sin root
$ ./exp # política ACTIVADA
Killed # SIGKILL en socket(AF_RXRPC)
Para ser precisos sobre lo que esto muestra:
rc=4, sin root) porque el kernel está parcheado. No hay nada que la política deba prevenir. El antes/después que respalda la afirmación de mitigación existe solo en el kernel vulnerable -88.socket(AF_RXRPC), por lo que Tetragon aún lo mata con SIGKILL y registra el intento en el log, tanto en un kernel parcheado como en uno sin parchear. Eso tiene valor como detección de un intento y como defensa en profundidad, pero no es lo mismo que detener un exploit funcional.AF_RXRPC es legítimo y una eliminación general lo rompería. Verificar por nodo.AF_ALG es solo auditoría por diseño. Una variante que use solo la ruta AF_ALG con módulos ya calientes sería registrada, no eliminada, hasta que promueva esa rama a aplicación (después de crear una lista de permitidos).AF_RXRPC se abre antes de la primitiva de escritura. Ese orden es lo que hace efectiva una eliminación temprana; no es una garantía sobre todos los exploits posibles.Configuración: Tetragon v1.7.0 independiente, iniciado mediante systemctl; políticas cargadas con tetra tracingpolicy add.