
PoC y cadena de exploits para CVE-2026-89633, una escritura fuera de límites en el heap en la ruta de coalescencia SMB1 TRANS2 de cifs.ko en Linux, que escala a uid 0 mediante la sobrescritura de credenciales y el aliasing de PTE.
coalesce_t2 de cifs SMB1English | 简体中文
PoC/EXP de investigación para una escritura fuera de límites en el heap en el cliente SMB1 cifs.ko de Linux. Un servidor SMB1 malicioso controla DataOffset en una respuesta secundaria TRANS2, y coalesce_t2() copia la carga útil secundaria de 8 bytes a &primary->Protocol + DataOffset + accumulated sin ninguna comprobación de los límites del destino. El destino de la copia es dondequiera que el servidor lo indique, en relación con el búfer de respuesta (server->bigbuf).
Este repositorio mantiene dos demostraciones con roles distintos; no son sustitutas entre sí:
DataOffset en tiempo de ejecución.uid/gid/suid/sgid/euid/egid/fsuid/fsgid. Es la prueba más directa de que la escritura fuera de límites es controlable y puede modificar un cred, con las mínimas dependencias.selinux_state, y el clon pasa a ser uid 0 con todas las capacidades sin observación del lado root. Es una extensión de EXP 1, no un reemplazo.La carga de módulos del kernel queda deliberadamente fuera del alcance de este repositorio: la cadena se detiene en uid 0 (y, opcionalmente, selinux_state en modo permisivo) y no instala ningún módulo. Incluso sin un módulo cargado, uid 0 + permisivo es la demostración completa de LPE que este repositorio afirma.
Los costes de EXP 2 siguen siendo explícitos: una lotería por ventana (~25–33 %, ~97–99 % en 12 intentos), un PTE desnudo que permanece activo hasta el reinicio, una dependencia de tracefs escribible desde el shell y suposiciones específicas de la compilación sobre el mapa directo, el sitio de llamada y SEL_PFN. EXP 1 sigue siendo la prueba de primitiva más simple y la alternativa cuando tracefs escribible desde el shell no está disponible.
| Elemento | Valor |
|---|---|
| CVE | CVE-2026-89633 |
| Clase | Escritura fuera de límites en el heap (CWE-787) |
| Componente | cifs.ko, coalescencia secundaria TRANS2 de SMB1 |
| Corrección upstream | 36d01415d881 ("smb: client: fix ... coalesce_t2", 6.12.113) |
| POC | Activación y escritura fuera de límites verificadas (tras el montaje y durante el montaje) |
| EXP 1 (primitiva mínima) | Escritura de cred fuera de límites de 4 tramas bajo observación del lado root; uid 0 verificado (tras el montaje y con respuesta retenida en el momento del montaje) |
| EXP 2 (primitiva avanzada) | cadena de PTE solo con shell; uid 2000 → uid 0 + todas las capacidades + Permissive verificados (SEL_PFN/SEL_OFF); se detiene en un PTE desnudo, sin carga de módulo |
| Requisitos | un montaje SMB1 contra un servidor controlado por el atacante (puerta privilegiada); la puerta de referencia usa su, y la colocación solo con shell necesita tracefs escribible desde el shell |
Entorno probado: una compilación de proveedor de Android 16 para arm64, kernel 6.12.38 con cifs.ko compilado a partir de la base KMI 6.12.23, SELinux en modo enforcing.
statfs, sin acceso al punto de montaje), la colocación solo con shell de EXP 2 (solo tracefs, sin observación del lado root), la aceptación completa de EXP 2 (uid 0 + todos los bits de capacidad + Permissive con SEL_PFN/SEL_OFF) y la puerta de montaje impulsada por su (solo el montaje se ejecuta como root; la colocación se ejecuta como uid 2000) se probaron allí bajo SELinux en modo enforcing.Para una respuesta SMB1 TRANS2 mayor que MAX_CIFS_SMALL_BUFFER_SIZE - 4
(444 bytes), el cliente mantiene la primaria en server->bigbuf y la almacena en
mid->resp_buf. Las siguientes respuestas SMB_COM_TRANSACTION2_SECONDARY pasan
por cifs_check_trans2() -> check2ndT2() -> coalesce_t2():
data_area_of_tgt = &primary->Protocol + primary->DataOffset + total_in_tgt;
memcpy(data_area_of_tgt, data_area_of_src, secondary->DataCount);
DataOffset se toma literalmente de la respuesta del servidor. Las comprobaciones del contador
(desbordamiento de total_in_tgt/byte_count, límites del búfer de origen) están presentes, pero
no hay ninguna comprobación de que data_area_of_tgt permanezca dentro del búfer de respuesta.
Cada trama secundaria aterriza 8 bytes más allá (el DataCount acumulado), por lo que una
respuesta multiframa produce una escritura de 8*N bytes en un desplazamiento elegido por el atacante
(16 bits) desde la base del búfer.
Condiciones de activación:
vers=1.0) desde un servidor que controla el atacante;statfs);TotalDataCount > DataCount y
es lo suficientemente grande para la ruta bigbuf, luego una o más tramas secundarias con
TotalDataCount > DataCount.Makefile
src/poc/poc_smb1_trans2.py malicious SMB1 server (QFS hook, runtime ctl)
src/poc/smb1_proxy.py TCP proxy: patches DataOffset, repeats secondaries
src/exp/pipesearch.c pipe placement/write driver (modes 3, 8, 9)
src/exp/shcred.c shell-side drain + clone helper (uid 2000)
src/exp/shp4a2.c trace-based drainer/observer/clone (modes 8/9)
src/exp/pte_fault.c 2 MiB fault + PTE alias writes
src/exp/bscan.c bigbuf discovery by call site (no kprobes)
src/exp/kslide.c KASLR slide from a raw call site
src/exp/smbd.c device-side SMB1 server (held TRANS2)
src/exp/smbd_templates.h SMB1 response templates for smbd
src/exp/pfn.c linear-map VA to pfn
src/exp/kflags.c /proc/kpageflags reader
scripts/setup_device.sh trace instance + kprobes (root on device)
scripts/start_servers.sh host: SMB1 server + proxy + mount helper
scripts/probe_round.sh one probe round: bigbuf base + free target page
scripts/place.sh placement: pipesearch mode 3 + shcred
scripts/write_cred.sh 4-frame coalesce write + /proc verification
scripts/mount_su.sh generic su mount gate (target/decoy/abort/unmount)
scripts/chain_shell.sh shell-only placement for an already-held TRANS2
scripts/gen_smb_templates.py regenerates smbd_templates.h from a capture
docs/analysis.md vulnerability and chain analysis
docs/exp-protocol.md shared-page and smbd ctl protocols
make deps # checks impacket
make helpers # NDK cross-build of all C helpers
make push # adb push to /data/local/tmp