
Análisis de reproducción del CVE-2026-31431, exploit adaptado en C.
.
├── container
│ ├── container.md # Enfoque para el escape de contenedores
│ ├── expenv # Entorno del contenedor
│ │ ├── bin
│ │ │ ├── gscontainer
│ │ │ └── share
│ │ ├── Dockerfile
│ │ ├── run.sh
│ │ └── src
│ │ ├── gscontainer.c
│ │ └── share.c
│ ├── images
│ │ ├── container.png
│ │ ├── getshell.png
│ │ ├── host.png
│ │ ├── payload.png
│ │ └── share.png
│ ├── recode.bin # Shellcode de una sola etapa
│ └── stager
│ ├── shell.elf # Shellcode por etapas incrustado en el exploit
│ ├── stager1.bin
│ ├── stager2.bin
│ ├── stager3.bin
│ └── stager4.bin
├── doc
│ ├── cve-2026-31431.md # Resumen del análisis de xint.io
│ └── shellcode.md # Explica qué es un shellcode
├── exp.py # curl http://copy.fail/exp -o exp.py
├── README.md
├── success
│ ├── code.py # Shellcode extraído de exp.py
│ └── exploit.c # Exploit en C simplificado, con el shellcode nativo de escalada de privilegios de msf; requiere evasión de antivirus propia
└── test
├── exptest.c # Sin comentarios
└── test.c # Correcta y comentada, para facilitar la comprensión
Tres commits individualmente inofensivos que, al combinarse, generaron una vulnerabilidad:
splice(archivo → pipe → AF_ALG)
↓
af_alg_sendmsg: páginas del page cache del archivo → TX SGL (zero-copy)
↓
_aead_recvmsg: optimización in-place
├─ memcpy_sglist: copia de AAD+texto cifrado al RX SGL
├─ sg_chain: la página del tag (aún apuntando al page cache del archivo) se encadena al RX SGL
└─ req->src = req->dst (= scatterlist con las páginas del page cache del archivo)
↓
crypto_authenc_esn_decrypt: reordenación de ESN
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ recorre el scatterlist de dst hasta la posición del tag
↓ kmap_local_page() mapea la página del page cache del archivo
↓ memcpy: escribe seqno_lo en la página del page cache del archivo
↓
Fallo de verificación HMAC → devuelve -EBADMSG
↓
La página del page cache del archivo ya está modificada; el kernel no la marca como sucia y el archivo en disco permanece sin cambios
El artículo afirma que, desde 2017, «casi todas las distribuciones de Linux» están afectadas. El page cache se comparte entre contenedores, por lo que esta vulnerabilidad es a la vez una escalada local de privilegios (LPE) y un vector de escape de contenedores.
El commit de corrección a664bf3d603d en el kernel principal revierte las operaciones AEAD de AF_ALG al modo out-of-place (separando src y dst):
Antes: aead_request_set_crypt(..., rsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src == dst ^
Después: aead_request_set_crypt(..., tsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src ≠ dst ^ (TX SGL para src, RX SGL para dst)
Mensaje del commit: "There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings."
Medidas de mitigación de emergencia:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
O bien, prohibir la creación de sockets AF_ALG mediante una política seccomp (socket(AF_ALG, ...)).
| Año | Commit | Impacto |
|---|
| 2011 | a5079d084f8b | Se agregó authencesn, que utiliza el scatterlist de dst como espacio temporal para la reordenación de ESN. En ese momento era inofensivo: la antigua interfaz AEAD mantenía el AAD separado y el único llamador era el xfrm del kernel. |
| 2015 | 104880a6b470 | authencesn se migró a la nueva interfaz AEAD, lo que introdujo el comportamiento de escribir seqno_lo en assoclen+cryptlen, «más allá del límite de salida». Seguía sin ser explotable: como AF_ALG usaba el modo out-of-place, req->src y req->dst estaban separados, y las páginas del page cache solo estaban en el src de solo lectura. |
| 2017 | 72548b093ee3 | Optimización in-place de AF_ALG. Copia el AAD y el texto cifrado del TX SGL al buffer de RX, pero encadena la página del tag mediante sg_chain. Establece req->src = req->dst. Ahora las páginas del page cache introducidas por splice también quedan en el scatterlist de dst, que es escribible. La vulnerabilidad queda formada. |
| Distribución | Versión del kernel | Afectado |
|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | Sí |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | Sí |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | Sí |
| SUSE 16 | 6.12.0-160000.9-default | Sí |
| Característica | Verificación en el código fuente | Descripción |
|---|
| authencesn escribe seqno_lo al final | authencesn.c:134 | En assoclen+cryptlen se escribe seqno_lo (los 32 bits bajos). La descripción del usuario es correcta. |
| Convención de reordenación de ESN | Confirmado por el blog (xint.io) | Los bytes 0-3 del AAD = seqno_hi; los bytes 4-7 = seqno_lo. tmp[1] (=bytes 4-7 = seqno_lo) se escribe al final. |
| La ruta de descifrado no restaura los bytes sobrescritos | authencesn.c:270-273 vs 215-217 | decrypt_tail restaura el ESN en el offset 0-7, pero nunca restaura los bytes originales en assoclen+cryptlen. La ruta de cifrado (genicv_tail) sí los restaura antes de escribir el ICV. |
| Cifrado/descifrado in-place en AF_ALG | algif_aead.c:189-252 | rsgl_src y areq->first_rsgl.sgl.sgt.sgl apuntan al mismo RX SGL. |
| Encadenamiento del tag en AF_ALG | algif_aead.c:238-244 | Al descifrar, el tag se referencia mediante sg_chain en lugar de copiarse; en el escenario de splice, la página del tag sigue apuntando al page cache del archivo. Causa raíz de la vulnerabilidad. |
| Transferencia zero-copy mediante splice | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | Referencia directa de la página del pipe; zero-copy. |
| CVE-2026-31431 | Cadena de tres commits | authencesn(2011/2015) + in-place(2017) + zero-copy de splice = primitiva de escritura controlable en el page cache. |