
PoC e cadeia de exploração para CVE-2026-89633, uma escrita fora dos limites no heap no caminho de coalescência SMB1 TRANS2 do cifs.ko do Linux, escalando para uid 0 via sobrescrita de cred e aliasing de PTE.
coalesce_t2 do SMB1 do cifsPortuguês | 简体中文
PoC/EXP de pesquisa para uma gravação fora dos limites no heap no cliente SMB1 do cifs.ko do Linux. Um servidor SMB1 malicioso controla o DataOffset numa resposta secundária TRANS2, e coalesce_t2() copia o payload secundário de 8 bytes para &primary->Protocol + DataOffset + accumulated sem qualquer verificação dos limites do destino. O destino da cópia é onde quer que o servidor o aponte, relativamente ao buffer de resposta (server->bigbuf).
Este repositório mantém duas demonstrações com papéis distintos; não são substitutas uma da outra:
DataOffset em tempo de execução.uid/gid/suid/sgid/euid/egid/fsuid/fsgid. É a prova mais direta de que a gravação OOB é controlável e pode modificar uma cred, com o menor número de dependências.selinux_state, e o clone torna-se uid 0 com todas as caps sem observação do lado root. É uma extensão do EXP 1, não um substituto.O carregamento de módulos do kernel está deliberadamente fora do âmbito deste repositório: a cadeia termina em uid 0 (e, opcionalmente, selinux_state permissivo) e não instala um módulo. Mesmo sem um módulo carregado, uid 0 + permissivo é a demonstração completa de LPE que este repositório reivindica.
Os custos do EXP 2 permanecem explícitos: uma lotaria por janela (~25–33%, ~97–99% em 12 tentativas), uma PTE nua que permanece ativa até ao reboot, uma dependência de tracefs gravável pelo shell, e pressupostos de direct-map/call-site/SEL_PFN específicos da build. O EXP 1 continua a ser a prova de primitiva mais simples e a alternativa quando o tracefs gravável pelo shell não está disponível.
| Item | Valor |
|---|---|
| CVE | CVE-2026-89633 |
| Classe | Gravação fora dos limites no heap (CWE-787) |
| Componente | cifs.ko, coalescência secundária TRANS2 do SMB1 |
| Correção upstream | 36d01415d881 ("smb: client: fix ... coalesce_t2", 6.12.113) |
| POC | Acionamento e gravação OOB verificados (pós-montagem e durante a montagem) |
| EXP 1 (primitiva mínima) | Gravação OOB de cred com 4 frames sob observação do lado root; uid 0 verificado (pós-montagem e resposta retida em tempo de montagem) |
| EXP 2 (primitiva avançada) | cadeia PTE apenas com shell; uid 2000 → uid 0 + todas as caps + Permissive verificados (SEL_PFN/SEL_OFF); termina numa PTE nua, sem carregamento de módulo |
| Requisitos | uma montagem SMB1 para um servidor controlado pelo atacante (portão privilegiado); o portão de referência usa su, e a colocação apenas com shell requer tracefs gravável pelo shell |
Ambiente testado: uma build de fornecedor Android 16 arm64, kernel 6.12.38 com cifs.ko compilado a partir da baseline KMI 6.12.23, SELinux em modo enforcing.
statfs, sem acesso ao ponto de montagem), a colocação apenas com shell do EXP 2 (apenas tracefs, sem observação do lado root), a aceitação completa do EXP 2 (uid 0 + todos os bits de cap + Permissive com SEL_PFN/SEL_OFF), e o portão de montagem acionado por su (apenas a montagem corre como root; a colocação corre como uid 2000) foram testados aí sob SELinux em modo enforcing.Para uma resposta SMB1 TRANS2 maior que MAX_CIFS_SMALL_BUFFER_SIZE - 4
(444 bytes), o cliente mantém a primária em server->bigbuf e a armazena em
mid->resp_buf. As respostas SMB_COM_TRANSACTION2_SECONDARY seguintes passam
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 é obtido literalmente da resposta do servidor. As verificações de contador (total_in_tgt/estouro de byte_count, limites do buffer de origem) estão presentes, mas não há verificação de que data_area_of_tgt permaneça dentro do buffer de resposta. Cada frame secundário avança 8 bytes (o DataCount acumulado), portanto uma resposta multiframe resulta em uma escrita de 8*N bytes em um deslocamento escolhido pelo atacante (16 bits) a partir da base do buffer.
Condições de disparo:
vers=1.0) a partir de um servidor controlado pelo atacante;statfs);TotalDataCount > DataCount e é grande o suficiente para o caminho bigbuf, seguido de um ou mais frames secundários com 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
NDK, API, ARCH, ADB, DEVICE_DIR são substituíveis.