Analizamos Copy Fail, un fallo lógico en el 'kernel' Linux que permite escalar a 'root'

7 de mayo de 2026

La vulnerabilidad CVE-2026-31431, más conocida como Copy Fail, es un exploit para Linux que rara vez se ve. No por el impacto (que es un Local Privilege Elevation y de estos salen varios en un año) sino por varios motivos que iremos desgranando a lo largo del artículo.

El primero de ellos es el que más ha dado que hablar: funciona en casi cualquier distribución Linux posterior a 2017 con el mismo exploit. Ni ajustes, condiciones especiales o comprar billetes para ganar la carrera TOCTOU. Descargas el exploit, lo ejecutas y desde una cuenta sin privilegios ya eres root; con bastantes garantías y de forma simple.

Esto es posible debido a que la vulnerabilidad, que es un fallo de lógica, se encuentra en un componente que está presente en todos los kernels Linux que hayan sido compilados desde 2017. En concreto, se trata del subsistema de criptografía y dentro de este, de la interfaz AF_ALG, que permite su uso desde el espacio de usuario a través de sockets.

Desde de la interfaz que te proporciona el socket del tipo AF_ALG, accedemos a la cripto API del kernel, entre ellas las funciones de authencesn. Recordemos: seguimos siendo un usuario sin privilegios llamando a funciones desde espacio de usuario, y esa API nos permite cifrar y descifrar buffers previamente creados por ese mismo usuario.

Vale, pues tenemos un socket abierto que nos abre la puerta a ese grupo de funciones que necesitamos. ¿Por qué ese grupo en concreto? Porque el fallo está en ellas.

Lo primero es localizar una utilidad de sistema que posea el setuid (un permiso que hace que el binario se ejecute para algo en concreto con permisos de administrador o root). Entre los archivos que podemos "leer" y que posee el setuid están ciertos comandos del sistema. El gran clásico es el comando 'su' que nos permite ejecutar, de forma limitada y controlada, procesos con permisos de root.

Por supuesto, el exploit abre el archivo 'su' provocando una llamada a la syscall 'read' o similares, y en ese momento se creará una page cache de memoria con su contenido.

Luego, usando la API de cifrado que expone el socket (aunque no vamos a cifrar absolutamente nada) lo que haremos será aprovechar esa puerta de entrada como vector de explotación del fallo lógico que ahora comentaremos para manipular esa página.

De momento, quedémonos con que tenemos un socket abierto a la API de criptografía del kernel Linux y el contenido del comando 'su' se encuentre cargado en la caché de páginas de memoria del sistema.

El fallo lógico

Hay una regla de oro no escrita en el kernel: no debe ser posible modificar una página de la caché de un archivo sin que el kernel lo detecte y marque esa página como 'dirty'.

Cuando una página está marcada como'"dirty' significa que el contenido de la memoria difiere respecto del contenido del archivo en disco. A partir de este punto el kernel la programa para sincronizar su contenido con una operación de escritura.

Por diseño, para optimizar tiempos de carga y reutilización, si alguien llama a 'su', el kernel no va a volver a cargar desde disco la imagen del comando 'su' y crear una nueva. En cambio aprovechará la que ya está en memoria porque no se ha modificado y es exactamente igual a la imagen de disco, es decir, no está marcada como sucia...

Bien ¿verdad?

Pues ahora suponed que podemos modificar la página cacheada en memoria y el kernel no se da cuenta de esos cambios y no marca la página como 'dirty'. Pues eso es exactamente lo que está ocurriendo y por qué funciona el exploit.

De hecho, que se llame Copy Fail no es casualidad. Además está relacionada con otras dos vulnerabilidades del mismo estilo y bastante conocidas: Dirty Pipe y más lejanamente con Dirty Cow.

Es más, un usuario sin privilegios ni tan siquiera debería poder modificar una página caché con el contenido del 'su', puesto que no tiene o debería tener los permisos adecuados.

El fallo permite, por un lado, escribir en la página caché y por otro, que esa escritura pase inadvertida por el kernel y no la marque como 'dirty'.

La modificación que se hace, que además solo permite hacer escrituras de cuatro bytes, es parchear ciertas comprobaciones de 'su' y manipular su comportamiento.

Poco más: basta con llamar a 'su' y el kernel ejecutará la versión que hay en la página caché modificada con el resultado de abrir una sesión shell con permisos del usuario root.

El 'exploit', paso a paso

Hay una prueba de concepto publicada por los mismos investigadores para comprobar si nuestros sistemas están o no afectados. Está creada en Python; no obstante, nos hemos tomado la libertad de renombrar las variables y añadir comentarios para hacerlos más educativos.

Al comienzo del exploit hacemos unas importaciones necesarias y damos valores a ciertas constantes que en el exploit original son literales:

Como hemos comentado, el material cripto es necesario para "hablar" correctamente con el socket, pero ni vamos a utilizarlo ni nos hace falta para nada.

La base de llamada del exploit es esta:

Leemos /usr/bin/su eso provoca que se cree esa página en la caché del kernel. Índice a cero, descomprimimos el shellcode e iteramos sobre el mismo en tramos de 4 bytes que es lo que nos permite escribir en la página cacheada debido al formato de mensajes que emplea AEAD.

Al final del todo, ese system ejecutará el comando 'su', pero ya no será el 'su' de disco sino la versión parcheada maliciosamente la que lo hará.

La función que hemos nombrado de manera discretamente descriptiva como abrir_socket_y_escribir hace fundamentalmente dos cosas:

Todo ese galimatías es para preparar el mensaje adecuado para que no se queje en términos de formato y material cripto (que como hemos dicho, ha de ser formal pero ni lo empleamos ni tenemos en cuenta el resultado).

Una vez preparado el mensaje provocamos la escritura de esos 4 bytes por tandas a través de las funciones de pipe (crea una tubería Linux de entrada/salida) y splice, que conduce los bytes a parchear hacia donde está la página caché:

Y listo, poco más. Esto terminará parcheando la página caché donde está 'su' y al culminar las operaciones el kernel. Debido a ese error de lógica no marcará la página caché como sucia por lo que la empleará tal cual.

Análisis del 'shellcode/parche' incrustado

Como habrás visto, hay una especie de shellcode codificado en hexadecimal. De hecho, por la costumbre, lo renombro a 'shellcode':

No es exactamente un shellcode, sino un parche para 'su'. Es decir, parchea la imagen de 'su' cacheada y lo más importante: SIN TOCAR DISCO.

Lo ponemos en mayúsculas porque este hecho es importante. Esto hace que, como todas las operaciones que no tocan disco, sean complicadas de detectar para los EDR y familia. Traducido al lenguaje SOCiano: casi indetectable.

El shell... parche es fácil de reversear, le damos la vuelta desde el hexadecimal hacia binario:

¿Veis el ELF asomando la patita?

y una vez desensamblado:

Poca cosa y poca broma. Simplemente nos abre una shell, solo que, naturalmente, como el proceso corre con privilegios de root, tendremos una shell con root.

Esto lo hace de forma clásica: la syscall 0x69 es setuid con parámetros a cero (root) y posteriormente, la syscall 0x3b que es equivalente a, con sus parámetros: execve("/bin/sh", NULL, NULL)

Por supuesto... la IA

Otro de los motivos por los que esta vulnerabilidad es particularmente reseñable es que el equipo que la ha encontrado se ha apoyado en un modelo propio de Inteligencia Artificial (llamado Xint Code) afinado precisamente para encontrar vulnerabilidades en el código.

Esta no es la primera y por supuesto va a ser la última, sino que estamos ya viendo la norma respecto a la investigación de vulnerabilidades.

Más información sobre Copy Fail.

Kali GPT, el asistente de IA para automatización y análisis en Ciberseguridad