
# 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.
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.
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.
Parcheo del binario.
El payload integrado (tomado textualmente del PoC de Theori) sobrescribe una instrucción
de rama en /usr/bin/su de una compilación estándar de util-linux, convirtiendo un
salto condicional que aplica la autenticación PAM en una caída incondicional.
Después del parcheo, ejecutar su sin contraseña tiene éxito y abre un shell root.
El binario en disco no se toca; eliminar las entradas de la caché de páginas
(echo 3 > /proc/sys/vm/drop_caches) revierte el cambio al instante.
Para un análisis técnico más profundo, consulte las referencias a continuación, en particular la publicación del blog de Xint y el repositorio del PoC de Theori.
- rule: AF_ALG socket opened by unprivileged process
desc: >
CVE-2026-31431 — an unprivileged process opened an AF_ALG (family 38)
socket, which is the first step of the Copy Fail exploit.
condition: >
evt.type = socket and
evt.arg.domain = 38 and
not user.uid = 0
output: >
AF_ALG socket opened (user=%user.name uid=%user.uid
pid=%proc.pid comm=%proc.name)
priority: WARNING
tags: [host, network, privilege_escalation, CVE-2026-31431]
rule CopyFail_CVE_2026_31431 {
meta:
description = "Detects Copy Fail exploit script in memory or on disk"
cve = "CVE-2026-31431"
strings:
$template = "authencesn(hmac(sha256),cbc(aes))" ascii
$splice = "os.splice" ascii
$aflag = "AF_ALG" ascii
condition:
all of them
}
Añada a /etc/audit/rules.d/cve-2026-31431.rules:
-a always,exit -F arch=b64 -S socket -F a0=38 -k cve_2026_31431_afalg
Luego supervise los registros con:
ausearch -k cve_2026_31431_afalg --interpret
1. Aplique el parche del kernel (recomendado) Actualice al kernel 6.18.22, 6.19.12, 7.0-rc7 o posterior, o aplique la actualización de seguridad de su distribución.
2. Desactive CONFIG_CRYPTO_USER_API_AEAD en tiempo de compilación Si compila su propio kernel, establezca:
# CONFIG_CRYPTO_USER_API_AEAD is not set
Esto elimina toda la interfaz AEAD de AF_ALG y elimina la superficie de ataque.
La mayoría de los despliegues embebidos/endurecidos no la necesitan.
3. seccomp — denegar la familia de sockets AF_ALG
Añada un filtro seccomp a los procesos sensibles (o a nivel de sistema mediante una
política LSM) que rechace socket(AF_ALG, ...):
// Deny socket() when domain == AF_ALG (38)
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EACCES),
4. Reduzca los privilegios de la caché de páginas mediante el endurecimiento del kernel
vm.unprivileged_userfaultfd=0 y parámetros similares reducen la superficie de ataque
de las interfaces de memoria del kernel accesibles al usuario, aunque no bloquean
directamente este CVE.
El exploit modifica solo la caché de páginas en memoria; el binario en disco nunca se toca. Para restaurar el contenido original del archivo en memoria, elimine la caché de páginas del kernel:
echo 3 | sudo tee /proc/sys/vm/drop_caches
Esto expulsa todas las páginas limpias de la caché. La siguiente lectura de /usr/bin/su
(o del binario que se haya parcheado) recargará los bytes originales desde el disco.
Reiniciar también limpia la caché de páginas por completo.
MIT — consulte LICENSE.
Se eligió MIT en lugar de una licencia personalizada de "solo uso educativo" porque
no existe tal licencia como identificador OSI/SPDX reconocido. MIT es permisiva,
legalmente bien comprendida y compatible con repositorios de divulgación responsable.
La intención educativa se expresa en el docstring del módulo, en la sección de Aviso
Legal de este README y en la salida de --help — no en una cláusula de licencia
personalizada legalmente dudosa.
| 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 |
| 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) |