
# Reescritura educativa del PoC de Copy Fail (CVE-2026-31431) — LPE del kernel de Linux mediante crypto in-place algif_aead + escritura en page-cache con splice()
Reescritura educativa de la prueba de concepto de Theori/Xint para CVE-2026-31431,
una vulnerabilidad de escalada de privilegios local en la interfaz de sockets criptográficos
algif_aead del kernel de Linux. Afecta a todos los kernels mainline desde 4.14 (julio de 2017)
hasta 6.18.21, y a la rama estable 6.19 hasta 6.19.11.
Solo para fines educativos e investigación de seguridad autorizada. NO ejecute esto en sistemas que no le pertenezcan o para los que no tenga permiso explícito por escrito para realizar pruebas. Ejecutar esto en un host vulnerable otorga acceso root inmediato a cualquier usuario local sin privilegios. Los autores no se hacen responsables del mal uso.
El kernel de Linux ha expuesto una API de criptografía interna al espacio de usuario
desde el kernel 3.2 mediante la familia de sockets AF_ALG (<linux/if_alg.h>). El
espacio de usuario abre un socket SOCK_SEQPACKET, lo vincula a una plantilla de
algoritmo (p. ej. "aead" / "authencesn(hmac(sha256),cbc(aes))"), y envía datos para
cifrado o descifrado.
En 2017, se fusionó una optimización de rendimiento que permite a los algoritmos AEAD cifrar o descifrar datos in situ cuando el kernel detecta que el origen y el destino comparten las mismas páginas subyacentes. Esto evita una copia de memoria redundante en cada operación.
La plantilla authencesn (que añade soporte de Número de Secuencia Extendido al
cifrado autenticado) contiene una escritura temporal durante el descifrado: escribe
bytes ESN en un desplazamiento específico dentro del búfer de datos de la operación
antes de que se ejecute la comprobación de autenticación. Cuando las páginas de origen
de la operación se obtuvieron de la caché de páginas de un archivo regular mediante
splice(), y la optimización in situ está activa, esta escritura temporal va
directamente de vuelta a esas páginas en caché.
Debido a que la caché de páginas del kernel es un mapeo compartido — cualquier proceso que abra el mismo archivo ve las mismas páginas físicas — un usuario sin privilegios puede:
/usr/bin/su) con O_RDONLY.AF_ALG mediante splice().authencesn; la escritura temporal ESN corrompe la
página en un desplazamiento controlado con bytes controlados por el atacante.Los escáneres de integridad fuera del host (AIDE, Tripwire, IMA/EVM) que comparan sumas de verificación de archivos en disco son completamente ciegos a este ataque. La escritura desaparece tras una expulsión de la caché de páginas o un reinicio, sin dejar rastro forense en disco.
Al apuntar a /usr/bin/su (un binario setuid root) y sobrescribir una rama
que protege la comprobación de autenticación PAM, el atacante obtiene un shell
root sin contraseña.
| Rama | Primera Vulnerable | Última Vulnerable | Corregida En |
|---|---|---|---|
| mainline | 4.14 | 6.18.21 | 6.18.22 |
| 6.19 estable | 6.19.0 | 6.19.11 | 6.19.12 |
| 7.0-rc | 7.0-rc1 | 7.0-rc6 | 7.0-rc7 |
El commit de optimización que introdujo el error llegó en el ciclo de desarrollo
de 4.14 (julio de 2017). Cualquier kernel compilado con CONFIG_CRYPTO_USER_API_AEAD=y
y CONFIG_CRYPTO_AUTHENC=y (ambos valores predeterminados comunes) en los rangos
anteriores es vulnerable.
| Distribución | Kernel Incluido (aprox.) | ¿Parcheado? |
|---|---|---|
| Debian 12 (Bookworm) | 6.1.x | Sí (backport) |
| Debian 13 (Trixie) | 6.12.x / 6.14.x | Sí (backport) |
| Ubuntu 24.04 LTS | 6.8.x | Sí (USN-7xxx) |
| Ubuntu 24.10 | 6.11.x | Sí |
| RHEL 9.x | 5.14.x (rebasado) | Sí (RHSA) |
| Alpine 3.20 | 6.6.x | Sí (backport) |
| Arch Linux | rolling (≥ 6.18.22 ahora) | Sí |
| Kali Linux 2026.1 | 6.18.12 | Vulnerable |
| Kali Linux 2026.2+ | 6.18.22+ | Corregido |
| Linux Mint 22 | 6.8.x (base Ubuntu) | Sí (USN de Ubuntu) |
Consulte el rastreador de seguridad de su distribución para conocer el estado exacto del aviso.
os.splice se añadió en Python 3.10)/usr/bin/su, legible
por todos ya que es setuid)# Mostrar lo que se escribiría sin tocar el kernel (seguro):
python3 copy_fail_exploit.py --dry-run
# Parchear la caché de páginas de /usr/bin/su (requiere kernel vulnerable):
# ADVERTENCIA: se requiere kernel vulnerable — úselo solo en sistemas que le pertenezcan
python3 copy_fail_exploit.py
# Parchear y escalar inmediatamente a root:
python3 copy_fail_exploit.py --spawn-shell
# Usar un binario objetivo y payload personalizados:
python3 copy_fail_exploit.py --target /usr/bin/sudo --payload-file ./custom_patch.bin
# Salida detallada (imprimir cada escritura de 4 bytes):
python3 copy_fail_exploit.py --verbose --dry-run
Por defecto, --spawn-shell está desactivado. Después de parchear, el script imprime:
[+] Parche aplicado a la caché de páginas de '/usr/bin/su' (disco sin cambios).
Ejecute `su` para escalar, o vuelva a ejecutar con --spawn-shell.
Para restaurar: echo 3 | sudo tee /proc/sys/vm/drop_caches
Debe optar por el lanzamiento del shell con --spawn-shell. Esto es intencional:
le obliga a comprender qué hizo el exploit antes de ejecutar el resultado.
Configuración de la primitiva.
El exploit abre un socket AF_ALG y lo vincula a la plantilla
authencesn(hmac(sha256),cbc(aes)) con una clave ficticia de todo ceros. Luego
configura el tamaño de la etiqueta de autenticación a 4 bytes mediante ALG_SET_AEAD_AUTHSIZE.
accept() en el socket de control produce un socket de operación que puede enviar
solicitudes de descifrado individuales.
Activación de la escritura en la caché de páginas.
Para cada fragmento de 4 bytes del payload del parche, el exploit llama a sendmsg()
en el socket de operación con MSG_MORE establecido, pasando 8 bytes de datos
asociados (AAD): 4 bytes de relleno seguidos de los 4 bytes del payload. MSG_MORE
indica al kernel que retenga la operación hasta que lleguen más datos. Luego crea
una tubería y emite dos llamadas splice() — file_fd → pipe → op_sock — donando
las páginas de la caché de páginas del propio archivo como entrada para la operación
de descifrado. Cuando finalmente se llama a recv(), el kernel ejecuta el descifrado
authencesn. La optimización in situ se activa (origen y destino son las mismas
páginas), y la escritura temporal ESN copia nuestros bytes del payload en la página
de caché en el desplazamiento objetivo. recv() devuelve EBADMSG porque la etiqueta
de autenticación no puede verificarse — eso es esperado e inofensivo. La escritura ya
ha ocurrido.