
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:
| 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. |
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
| 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í |
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, ...)).
| 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. |