En abril de 2026, el investigador surcoreano Hyunwoo Kim (@v4bel) publicó una vulnerabilidad del kernel de Linux que tituló Dirty Frag. Esta vulnerabilidad, que permite a un usuario sin privilegios obtener root en la mayoría de distribuciones Linux actuales, encadena dos fallos distintos del kernel: xfrm-ESP Page-Cache Write y RxRPC Page-Cache Write. El trabajo es especialmente notable porque un único exploit cubre todas las distribuciones mayores sin excepción.
Dirty Frag pertenece a la misma clase que Dirty Pipe y Copy Fail: vulnerabilidades que modifican la Page Cache (copia en RAM que el kernel mantiene de los archivos en disco) sin marcar las páginas como dirty - páginas modificadas en memoria que aún no han sido sobreescritas en disco - , dejando el archivo en disco intacto mientras alteran el código del binario presente en memoria. En contraste con Dirty Pipe, que sobreescribe
struct pipe_buffer, Dirty Frag sobreescribe el campo frag de struct sk_buff (una estructura del sistema de red de Linux).
A diferencia de Copy Fail, Dirty Frag no requiere el módulo
algif_aead y funciona incluso en sistemas donde la mitigación conocida de Copy Fail ya está aplicada. Lo que hace especialmente relevante a Dirty Frag es además que es un fallo lógico determinista: no depende de una ventana de tiempo, no requiere race condition, no produce kernel panic si falla, y tiene una tasa de éxito muy alta.
El patrón común: splice(), frag y crypto in-place
Copy Fail y Dirty Frag comparten el mismo patrón fundamental. En una ruta de envío zero-copy,
splice() - la llamada al sistema que mueve datos entre descriptores de fichero sin copiarlos por espacio de usuario - planta una referencia a una página de la Page Cache, a la que el atacante solo tiene acceso de lectura, dentro del campo frag del skb del lado emisor. El dato clave es que esta referencia apunta directamente a la página real del archivo, no a una copia.
A continuación, el código del kernel del lado receptor ejecuta un cifrado criptográfico in-place directamente sobre ese
frag, usando la misma página simultáneamente como origen y destino de la operación. El resultado es que la Page Cache de archivos a los que el usuario sin privilegios solo tiene acceso de lectura, como /etc/passwd o /usr/bin/su, queda modificada en RAM y cualquier lectura posterior del archivo usa esa copia modificada o corrompida.
Primera variante: xfrm-ESP Page-Cache Write
El subsistema xfrm es el marco de transformaciones de seguridad del kernel Linux, utilizado principalmente por IPsec para cifrar y autenticar paquetes de red. Cuando un paquete ESP (Encapsulating Security Payload) llega al kernel, la función
esp_input() se encarga de descifrarlo y verificarlo antes de entregar el payload a la capa superior (la capa IP del kernel Linux).
En condiciones normales, cuando el
skb es no lineal (tiene datos en fragmentos), esp_input() debe llamar a skb_cow_data() para alojar un buffer privado del kernel, copiar los datos del fragmento en él y realizar el cifrado in-place sobre esa copia segura. Sin embargo, existe una rama de optimización llamada skip_cow que omite esa copia cuando el skb tiene un frag pero no tiene frag_list. Es ahí donde reside el fallo explotado por Dirty Frag:
Si el atacante ha plantado previamente una página de la Page Cache en ese
frag mediante splice(), esa página se convierte simultáneamente en origen y destino del cifrado. El daño concreto ocurre durante el preprocesado de crypto_authenc_esn_decrypt(), que realiza un STORE de 4 bytes en la posición assoclen + cryptlen del SGL (scatterlist) de destino para reordenar los bytes altos del número de secuencia ESN (números de secuencia utilizados por IPsec para evitar ataques de repetición).
El atacante controla tanto la posición exacta del STORE dentro del archivo (ajustando la longitud del payload) como el valor de los 4 bytes escritos (especificándolo en el campo
seq_hi del atributo XFRMA_REPLAY_ESN_VAL al registrar la SA - Asociación de Seguridad). La verificación AEAD se ejecuta después del STORE, de modo que aunque falle la autenticación, la modificación de la Page Cache ya ha ocurrido y persiste indefinidamente hasta el siguiente reinicio.
Esta variante requiere poder crear un namespace de usuario (
unshare (CLONE_NEWUSER)) con el fin de poder registrar la SA XFRM. En Ubuntu, la política AppArmor puede bloquear la creación de namespaces de usuario sin privilegios, lo que limita esta variante en ese entorno concreto, aunque no en otras distribuciones donde los namespaces de usuario están habilitados por defecto.
Segunda variante: RxRPC Page-Cache Write
RxRPC es el protocolo de red utilizado por el sistema de archivos distribuido Andrew (AFS). Para verificar paquetes de datos al nivel de seguridad
RXRPC_SECURITY_AUTH de la clase de seguridad RXKAD, la función rxkad_verify_packet_1() realiza un descifrado in-place de los primeros 8 bytes del payload del skb usando el algoritmo pcbc(fcrypt). La raíz del problema es idéntica a la de la variante ESP.
skb_to_sgvec() convierte el frag del skb directamente en el SGL, y la llamada skcipher_request_set_crypt(req, sg, sg, 8, iv) establece src y dst apuntando al mismo SGL. Si el atacante ha plantado una página de la Page Cache en ese frag mediante splice(), el descifrado escribe 8 bytes directamente sobre esa página sin pasar por ninguna copia intermedia, exactamente como en la variante ESP.
La diferencia clave entre ambos es que el atacante no controla directamente el valor de los 8 bytes escritos: el valor es el resultado de
fcrypt_decrypt(C, K), donde C son los bytes que ya existían en esa posición del archivo y K es la clave de sesión del token RxRPC que el atacante registra libremente con add_key(). Para escribir un valor concreto, el atacante debe buscar en espacio de usuario la clave K tal que el descifrado produzca el patrón deseado, lo que es computable en tiempo razonable dado el tamaño de bloque de fcrypt.
Esta variante no requiere privilegio para crear un namespace, lo que elimina el obstáculo que AppArmor impone en Ubuntu a la variante ESP. El módulo
rxrpc.ko no está incluido por defecto en la mayoría de distribuciones, pero sí se carga automáticamente en Ubuntu, lo que convierte a esta variante en el camino natural de ataque en ese sistema cuando la variante ESP está bloqueada.
El resultado final es el mismo en ambos casos. Ejecutar
/usr/bin/su con el shellcode inyectado en memoria o con el campo de contraseña de root vaciado en /etc/passwd es suficiente para obtener una shell de root. Esto funciona porque su tiene el bit setuid activo, lo que hace que el proceso se ejecute con los privilegios del propietario del fichero (root) independientemente de quién lo invoque, y PAM acepta una contraseña vacía cuando el campo está en blanco mediante la opción nullok.
El encadenamiento: cubriendo los puntos ciegos
Ninguna de las dos variantes funciona en todos los entornos por sí sola. La variante ESP ofrece una primitiva de escritura arbitraria de 4 bytes muy potente y está disponible en la mayoría de distribuciones, pero requiere crear un namespace de usuario, operación que Ubuntu puede bloquear mediante AppArmor. La variante RxRPC no necesita ese privilegio, pero
rxrpc.ko no está presente en la mayoría de distribuciones fuera de Ubuntu. Cada variante tapa exactamente el punto ciego de la otra.
El exploit intenta primero la variante ESP y comprueba si la modificación tuvo éxito leyendo el primer byte del shellcode en la Page Cache. Si está bloqueada por AppArmor o el namespace no está disponible, ejecuta automáticamente el fallback RxRPC, que vacía el campo de contraseña de root en el fichero
/etc/passwd y lanza su sin contraseña. El encadenamiento hace que los puntos ciegos de cada variante se cubran mutuamente:
Relación con Copy Fail y Dirty Pipe
Dirty Frag pertenece explícitamente a la misma clase de vulnerabilidades que Dirty Pipe (2022) y Copy Fail (2026). Las tres explotan el mismo principio: una operación del kernel escribe sobre una página de la Page Cache sin marcarla como dirty, de modo que el disco permanece intacto, los verificadores de integridad como SHA256 no detectan nada, y la modificación desaparece sola con el siguiente reinicio sin dejar rastro en el sistema de archivos.
La variante ESP de Dirty Frag comparte el mismo sink - el punto del código donde la escritura peligrosa finalmente ocurre - que Copy Fail: ambas terminan ejecutando
crypto_authenc_esn_decrypt() sobre una página modificada por el atacante. La diferencia está en el camino de llegada: Copy Fail usa AF_ALG como entrada, mientras que Dirty Frag llega directamente a través de esp_input() sin necesitar algif_aead, por lo que la mitigación conocida de Copy Fail no tiene ningún efecto sobre él.
Versiones afectadas
La vulnerabilidad xfrm-ESP Page-Cache Write está presente desde el commit
cac2661c53f3 del 17 de enero de 2017 hasta el upstream actual, lo que supone una vida efectiva de aproximadamente 9 años sin que nadie detectara la suposición de seguridad implícita. La variante RxRPC Page-Cache Write es más reciente: está presente desde el commit 2dc334f1a63a de junio de 2023.
Dirty Frag ha sido verificado en las siguientes distribuciones:
- Ubuntu 24.04.4: kernel 6.17.0-23-generic
- RHEL 10.1: kernel 6.12.0-124.49.1.el10_1.x86_64
- openSUSE Tumbleweed: kernel 7.0.2-1-default
- CentOS Stream 10: kernel 6.12.0-224.el10.x86_64
- AlmaLinux 10: kernel 6.12.0-124.52.3.el10_1.x86_64
- Fedora 44: kernel 6.19.14-300.fc44.x86_64
Mitigación
El parche oficial para la variante ESP fue introducido el 7 de mayo de 2026 en el árbol netdev del kernel (el repositorio de desarrollo del subsistema de red del kernel). Introduce el flag
SKBFL_SHARED_FRAG en los fragmentos que provienen de splice() y verifica ese flag en la rama skip_cow de esp_input() y esp6_input() antes de proceder al cifrado, forzando el paso por skb_cow_data() cuando el fragmento es compartido. La variante RxRPC dispone de parche propuesto pero pendiente de merge en este momento.
Mientras los parches llegan a todas las distribuciones puede aplicarse la siguiente mitigación temporal, que deshabilita los módulos vulnerables impidiendo que se carguen en el arranque y descargándolos si ya estuvieran activos:
sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' \
> /etc/modprobe.d/dirtyfrag.conf; rmmod esp4 esp6 rxrpc 2>/dev/null; true"
Es importante destacar que deshabilitar
algif_aead como mitigación de Copy Fail no ofrece ninguna protección contra Dirty Frag, ya que este no usa ese módulo en ninguna de sus dos variantes. Ambas mitigaciones son completamente independientes y deben aplicarse por separado si se quiere estar protegido frente a ambas vulnerabilidades.
Cronología de divulgación
La publicación de Dirty Frag estuvo marcada por la ruptura del embargo antes de que hubiera parches disponibles para todas las distribuciones. Hyunwoo Kim reportó ambas variantes a
security@kernel.org los días 29 y 30 de abril de 2026 respectivamente, publicando simultáneamente sus parches en la lista netdev para que los mantenedores pudieran revisarlos en conjunto.
El 7 de mayo se acordó un embargo de 5 días con los mantenedores de distribuciones, con la condición explícita de que si un tercero publicaba el exploit durante ese período, se haría pública la divulgación completa de inmediato. Ese mismo día 7 de mayo, un tercero no relacionado publicó el exploit, rompiendo el embargo y forzando la publicación inmediata de todo el documento de Dirty Frag antes de que todas las distribuciones afectadas tuvieran parches disponibles.
Conclusión
Dirty Frag demuestra que la clase de vulnerabilidades de Page Cache Write iniciada por Dirty Pipe en 2022 sigue siendo fértil cuatro años después. Apenas semanas tras la publicación de Copy Fail, un nuevo investigador encontró dos rutas distintas hacia el mismo sink (el punto del código donde la escritura peligrosa finalmente ocurre:
crypto_authenc_esn_decrypt()), sin necesitar el componente que la mitigación conocida bloqueaba.
La estrategia de encadenar dos variantes de escalada de privilegios con puntos ciegos complementarios para cubrir todas las distribuciones mayores con un único binario es especialmente notable desde el punto de vista ofensivo. Ni la política AppArmor de Ubuntu ni la ausencia de
rxrpc.ko en el resto de distribuciones son suficientes por sí solas para detenerlo, porque cada restricción solo bloquea una de las dos ramas del exploit.
El patrón subyacente lleva presente en el kernel desde 2017: una operación criptográfica in-place sobre un fragmento de red que nadie verificó que fuera memoria privada del kernel antes de escribir en él. Una suposición de seguridad implícita, nunca documentada en el código ni en los comentarios, sobrevivió a múltiples revisiones y a varios ciclos de auditoría hasta que alguien decidió, simplemente, comprobarla.
Fuentes:
CERT/CC - VU#980487 – Dirty Frag
GitHub - Repositorio oficial de Dirty Frag
GitHub - Write-up técnico completo de Dirty Frag
Ubuntu - Dirty Frag Linux kernel LPE vulnerability mitigations
No hay comentarios:
Publicar un comentario