Foto de Blai Peidro
Hola

Soy Blai Peidro

Senior Infrastructure Engineer

  • Stack Linux · Bash · Python · Ansible
  • Intereses Automatización · IA · Network
  • Idiomas Español · Català · English
  • Ubicación Barcelona, España
  • Web https://www.blai.blog

Copy Fail

Copy Fail (CVE-2026-31431): cómo una corrupción de Page Cache permite obtener root en Linux mediante AF_ALG y splice()

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 Temporal No
Dirty Pipe Pipe Buffers No Temporal
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