En abril de 2026 se hizo pública la vulnerabilidad CVE-2026-31431, más conocida como "Copy Fail". Se trata de una vulnerabilidad crítica del kernel de Linux que permite a un usuario sin privilegios modificar la Page Cache de archivos legibles y utilizar esa modificación para obtener privilegios de root.
La vulnerabilidad afecta a todas las distribuciones existentes de GNU/Linux en el momento de escribir estas líneas y ha sido probada con éxito en múltiples distribuciones importantes: Ubuntu, Amazon Linux, RHEL, SUSE...
Lo interesante de Copy Fail es que no explota un buffer overflow, un race condition o un Use After Free (UAF), que son los procedimientos típicos en los "root exploits", sino que se vale de la interacción inesperada entre varios componentes internos del kernel:
- AF_ALG
- AEAD
- Scatterlists
- Page Cache
- splice()
- authencesn
Antes de profundizar en el exploit en sí, vamos a entender a qué se dedican cada uno de estos elementos.
¿Qué es la Page Cache?
La Page Cache es el mecanismo de caché del kernel Linux utilizado para almacenar páginas de archivos recientemente accedidos en memoria RAM.
Cuando una aplicación ejecuta:
read(fd, buffer, size);
Linux normalmente no accede directamente al disco cada vez.
El flujo habitual es:
Disco
↓
Page Cache
↓
Aplicación
Si un archivo ya está cacheado:
read()
mmap()
execve()
sendfile()
splice()
Todos reutilizan las mismas páginas físicas en memoria.
Por ejemplo:
cat /usr/bin/su
Puede cargar páginas del binario dentro de la Page Cache.
Posteriormente:
su
Utilizará esas mismas páginas.
Por ello, modificar la Page Cache equivale a modificar temporalmente el binario ejecutado, aunque el archivo real permanezca intacto.
¿Qué es AF_ALG?
AF_ALG (Algorithm Family) es una interfaz del kernel Linux que expone algoritmos criptográficos mediante sockets. Permite acceder desde user space a la Crypto API del kernel.
Ejemplo:
socket(AF_ALG, SOCK_SEQPACKET, 0)
A través de AF_ALG pueden utilizarse algoritmos como:
- AES
- SHA256
- HMAC
- GCM
- CCM
- AEAD
Ejemplo práctico:
alg = socket.socket(
AF_ALG,
SOCK_SEQPACKET,
0
)
alg.bind((
"hash",
"sha256"
))
Internamente el flujo sería:
Aplicación
↓
AF_ALG socket
↓
Crypto API Linux
↓
Algoritmo criptográfico
Copy Fail explota precisamente esta ruta.
¿Qué son AEAD?
AEAD significa Authenticated Encryption With Associated Data. Es un mecanismo criptográfico que combina:
1. Confidencialidad
2. Integridad
3. Autenticación
La estructura típica es:
AAD || Ciphertext || Authentication Tag
Donde:
AAD (Associated Authenticated Data)
Información autenticada pero no cifrada.
Ejemplos:
- Cabeceras IPsec
- Números de secuencia
- Metadatos de protocolos
Ciphertext
Corresponde a los datos cifrados.
Authentication Tag
Es la firma criptográfica que verifica integridad.
Ejemplos:
- HMAC
- GCM Tag
- CCM Tag
Si cualquier byte cambia:
Tag inválido → descifrado fallido
¿Qué es authencesn?
authencesn es un template AEAD utilizado principalmente por IPsec para manejar Extended Sequence Numbers (ESN).
El nombre proviene de:
AUTHentication
ENCryption
Extended Sequence Number
IPsec utiliza números de secuencia de 64 bits:
+------------+
| seqno_hi |
+------------+
+------------+
| seqno_lo |
+------------+
Estos valores evitan:
- Replay attacks
- Reutilización de paquetes
- Manipulación de tráfico
El problema es que authencesn reorganiza temporalmente estos bytes durante el cálculo del HMAC utilizando memoria que no debería modificar.
Ahí nace Copy Fail.
¿Qué son las Scatterlists?
Las Scatterlists son estructuras del kernel Linux que permiten representar memoria fragmentada como si fuese continua.
Ejemplo físico:
Página A
Página B
Página C
Scatterlist equivalente:
entry0 → página A
entry1 → página B
entry2 → página C
Para el algoritmo criptográfico todo aparece como:
ABC
Esto evita copias adicionales.
En Copy Fail existen dos scatterlists principales.
TX Scatterlist
Contiene entrada:
AAD
Ciphertext
Authentication Tag
Representación:
TX SGL
[AAD]
[CT]
[TAG]
RX Scatterlist
Contiene salida:
AAD
Plaintext
Representación:
RX SGL
[AAD]
[PLAINTEXT]
El fallo aparece cuando ambas terminan mezclándose.
¿Qué hace splice()?
splice() es una llamada del sistema diseñada para mover datos entre descriptores sin copiar información a espacio usuario.
Ejemplo:
splice(
filefd,
NULL,
pipefd,
NULL,
size,
0
);
Ruta tradicional:
Disco
↓
Kernel
↓
Userspace
↓
Kernel
Con splice():
Disco
↓
Page Cache
↓
Pipe
No hay copia. Solo referencias.
Esto recibe el nombre de:
Zero-Copy
En Copy Fail el flujo es:
Archivo
↓
Page Cache
↓
splice()
↓
Pipe
↓
AF_ALG
↓
Scatterlist
Las páginas reales del archivo terminan entrando directamente al subsistema criptográfico. Ese es el punto crítico de la vulnerabilidad.
El origen real del fallo
Durante una operación AEAD el kernel crea:
Entrada:
Input Scatterlist
AAD
Ciphertext
Authentication Tag
Salida:
Output Scatterlist
AAD
Plaintext
En 2017 Linux introdujo una optimización denominada:
In-Place AEAD
Antes:
req->src ≠ req->dst
Después:
req->src = req->dst
Resultado:
RX BUFFER
+----------------+
| AAD |
| PLAINTEXT |
+----------------+
↓
sg_chain()
↓
+----------------+
| TAG |
| PAGE CACHE |
+----------------+
Las páginas del archivo original pasan a formar parte de una región considerada escribible. Y es ahí donde aparece Copy Fail.
Cadena completa de explotación
Hasta ahora hemos visto cada componente por separado, pero la explotación completa de Copy Fail sigue el siguiente flujo:
Usuario sin privilegios
│
▼
splice()
│
▼
Page Cache de /usr/bin/su
│
▼
AF_ALG (authencesn)
│
▼
AEAD In-Place
│
▼
Escritura controlada de 4 bytes
│
▼
Page Cache corrupta
│
▼
execve("/usr/bin/su")
│
▼
ROOT
La clave está en que el atacante nunca modifica el archivo físico. Toda la alteración ocurre únicamente sobre la copia almacenada en memoria.
¿Por qué no detectan nada las herramientas?
Una de las características más peligrosas de Copy Fail es que el archivo real permanece intacto. La corrupción ocurre únicamente sobre la versión almacenada en la Page Cache. Esto significa que herramientas habituales de comprobación seguirán mostrando el fichero como legítimo:
- sha256sum
- rpm -V
- debsums
- AIDE
- Tripwire
Ejemplo:
sha256sum /usr/bin/su
3f91d2.... /usr/bin/su
Sin embargo el proceso lanzado mediante:
execve("/usr/bin/su")
Puede ejecutarse desde una versión alterada presente únicamente en memoria.
Por ello Copy Fail resulta especialmente sigiloso frente a mecanismos tradicionales de detección basados en disco.
Cómo apareció realmente el bug
Copy Fail no nació por un único error de programación. Apareció por la combinación de varios cambios introducidos a lo largo de casi una década.
Cronología simplificada:
2011
│
├── Se introduce authencesn
│
2015
│
├── AF_ALG recibe soporte AEAD
│
2017
│
└── Optimización In-Place
│
▼
Nace Copy Fail
Ninguno de estos cambios era vulnerable de forma aislada.
El fallo aparece cuando coinciden simultáneamente:
- authencesn escribiendo fuera de la región esperada
- AF_ALG aceptando datos procedentes de splice()
- Uso compartido entre TX y RX Scatterlists
- Páginas de Page Cache dentro de una región escribible
Es precisamente esta combinación la que hace tan interesante a Copy Fail.
Copy Fail frente a Dirty COW y Dirty Pipe
Las comparaciones más habituales son con Dirty COW y Dirty Pipe, aunque internamente funcionan de forma muy distinta.
| Vulnerabilidad | Técnica | Race Condition | Persistencia | Modifica disco |
|---|---|---|---|---|
| Dirty COW | Copy-On-Write | Sí | Temporal | No |
| Dirty Pipe | Pipe Buffers | No | Temporal | Sí |
| Copy Fail | Page Cache + AEAD | No | Temporal | No |
A diferencia de Dirty COW, Copy Fail no depende de sincronización ni reintentos.
Y a diferencia de Dirty Pipe, el archivo real nunca resulta alterado.
¿Por qué el disco nunca cambia?
La corrupción producida por Copy Fail no utiliza rutas tradicionales de escritura como:
write()
pwrite()
vfs_write()
La modificación ocurre directamente sobre la Page Cache y el kernel nunca marca la página como modificada (dirty page). Por ello no se ejecuta el mecanismo de writeback hacia disco.
El resultado es especialmente llamativo:
Disco:
/usr/bin/su
SHA256 correcto
Memoria:
/usr/bin/su
contenido alterado
El binario ejecutado puede ser distinto del almacenado físicamente.
Impacto en contenedores y Kubernetes
La Page Cache pertenece al kernel y se comparte entre procesos, espacios de nombres y contenedores. Esto implica que una modificación realizada mediante Copy Fail no queda necesariamente limitada al proceso atacante.
Ejemplo conceptual:
Container A
│
▼
Modifica Page Cache
│
▼
/usr/bin/su
│
▼
Container B
Host
Otros procesos
Por este motivo Copy Fail no se considera únicamente una escalada local de privilegios. También puede actuar como primitiva de escape de contenedores y compromiso de nodos Kubernetes dependiendo de la configuración del entorno.
Mitigación
La corrección oficial elimina el funcionamiento in-place dentro de algif_aead y vuelve al modelo out-of-place, separando las regiones de lectura y escritura.
Mientras los parches llegan a todas las distribuciones puede aplicarse una mitigación temporal deshabilitando el módulo vulnerable:
echo "install algif_aead /bin/false" \
> /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
También es recomendable bloquear AF_ALG mediante políticas seccomp en:
- Entornos Docker
- Kubernetes
- Servidores multiusuario
- Infraestructuras compartidas
- CI/CD
Conclusión
Copy Fail demuestra que algunas de las vulnerabilidades más peligrosas no aparecen por errores clásicos de memoria. Aquí no encontramos buffer overflows, Use After Free ni condiciones de carrera.
El problema aparece por una suposición incorrecta mantenida durante años: asumir que ningún algoritmo AEAD escribiría fuera de su región legítima. Esa hipótesis sobrevivió a múltiples cambios del kernel y terminó permitiendo una escalada local a root basada únicamente en cuatro bytes escritos sobre la Page Cache. Una vulnerabilidad pequeña en tamaño, pero enorme en impacto.
Referencias:
- CVE-2026-31431
- Linux Crypto API
- AF_ALG documentation
- algif_aead.c
- authencesn implementation
- Dirty COW (CVE-2016-5195)
- Dirty Pipe (CVE-2022-0847)
No hay comentarios:
Publicar un comentario