
PoC e catena di exploit per CVE-2026-89633, una scrittura fuori dai limiti dell'heap nel percorso di coalescenza SMB1 TRANS2 di cifs.ko su Linux, che consente l'escalation a uid 0 tramite sovrascrittura delle credenziali e aliasing delle PTE.
coalesce_t2 scrittura OOB nell'heapItaliano | 简体中文
PoC/EXP di ricerca per una scrittura fuori dai limiti (out-of-bounds) nell'heap nel client SMB1 cifs.ko di Linux. Un server SMB1 malevolo controlla DataOffset in una risposta secondaria TRANS2, e coalesce_t2() copia il payload secondario di 8 byte in &primary->Protocol + DataOffset + accumulated senza alcun controllo dei limiti della destinazione. La destinazione della copia è ovunque il server la indichi, relativamente al buffer di risposta (server->bigbuf).
Questo repository mantiene due dimostrazioni con ruoli distinti; non sono sostitutive l'una dell'altra:
DataOffset a runtime.uid/gid/suid/sgid/euid/egid/fsuid/fsgid. È la prova più diretta che la scrittura OOB è controllabile e può modificare un cred, con il minor numero di dipendenze.selinux_state, e il clone diventa uid 0 con tutti i cap senza osservazione lato root. È un'estensione di EXP 1, non un suo sostituto.Il caricamento di moduli del kernel è deliberatamente fuori dall'ambito di questo repository: la catena si ferma a uid 0 (e opzionalmente selinux_state permissivo) e non installa un modulo. Anche senza un modulo caricato, uid 0 + permissivo è la dimostrazione completa di LPE che questo repository rivendica.
I costi di EXP 2 restano espliciti: una lotteria per finestra (~25–33%, ~97–99% su 12 tentativi), un PTE nudo che resta attivo fino al riavvio, una dipendenza da tracefs scrivibile dalla shell, e assunzioni specifiche della build su direct-map/call-site/SEL_PFN. EXP 1 resta la prova più semplice della primitiva e il fallback quando tracefs scrivibile dalla shell non è disponibile.
| Voce | Valore |
|---|---|
| CVE | CVE-2026-89633 |
| Classe | Scrittura fuori dai limiti nell'heap (CWE-787) |
| Componente | cifs.ko, coalescing secondario SMB1 TRANS2 |
| Fix upstream | 36d01415d881 ("smb: client: fix ... coalesce_t2", 6.12.113) |
| POC | Trigger e scrittura OOB verificati (post-mount e durante il mount) |
| EXP 1 (primitiva minimale) | Scrittura OOB dei cred a 4 frame sotto osservazione lato root; uid 0 verificato (post-mount e risposta trattenuta al momento del mount) |
| EXP 2 (primitiva avanzata) | catena PTE solo-shell; uid 2000 → uid 0 + tutti i cap + Permissive verificato (SEL_PFN/SEL_OFF); si ferma a un PTE nudo, nessun caricamento di modulo |
| Requisiti | un mount SMB1 verso un server controllato dall'attaccante (gate privilegiato); il gate di riferimento usa su, e il posizionamento solo-shell richiede tracefs scrivibile dalla shell |
Ambiente testato: una build vendor arm64 Android 16, kernel 6.12.38 con cifs.ko compilato dalla baseline KMI 6.12.23, SELinux enforcing.
statfs, nessun accesso al punto di mount), il posizionamento solo-shell di EXP 2 (solo tracefs, nessuna osservazione lato root), l'accettazione completa di EXP 2 (uid 0 + tutti i bit dei cap + Permissive con SEL_PFN/SEL_OFF), e il gate di mount pilotato da su (solo il mount viene eseguito come root; il posizionamento viene eseguito come uid 2000) sono stati testati lì sotto SELinux enforcing.Per una risposta SMB1 TRANS2 più grande di MAX_CIFS_SMALL_BUFFER_SIZE - 4
(444 byte) il client mantiene la primaria in server->bigbuf e la memorizza in
mid->resp_buf. Le successive risposte SMB_COM_TRANSACTION2_SECONDARY passano
attraverso 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 viene preso letteralmente dalla risposta del server. I controlli sul contatore
(overflow di total_in_tgt/byte_count, limiti del buffer sorgente) sono presenti, ma
non c'è alcun controllo che data_area_of_tgt rimanga all'interno del buffer di risposta.
Ogni frame secondario si posiziona 8 byte più avanti (il DataCount accumulato), quindi una
risposta multi-frame produce una scrittura di 8*N byte a un offset scelto dall'attaccante
(16 bit) dalla base del buffer.
Condizioni di attivazione:
vers=1.0) da un server controllato
dall'attaccante;statfs);TotalDataCount > DataCount ed
è abbastanza grande per il percorso bigbuf, seguito da uno o più frame secondari 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
NDK, API, ARCH, ADB, DEVICE_DIR sono sovrascrivibili.