ksmbd streams_xattr OOB write → local LPE (CVE-2025-37947)
Reading time: 7 minutes
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Esta página documenta una escritura determinista fuera de límites en el manejo de streams de ksmbd que permite una escalada de privilegios del kernel de Linux fiable en Ubuntu 22.04 LTS (5.15.0-153-generic), saltándose KASLR, SMEP y SMAP mediante primitivas estándar del heap del kernel (msg_msg + pipe_buffer).
- Componente afectado: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
- Primitiva: page-overflow OOB write que sobrescribe más allá de un búfer kvmalloc() de 0x10000 bytes
- Precondiciones: ksmbd en ejecución con un recurso compartido autenticado y escribible usando vfs streams_xattr
Ejemplo de smb.conf
[share]
path = /share
vfs objects = streams_xattr
writeable = yes
Causa raíz (asignación limitada, memcpy en desplazamiento no limitado)
- La función calcula size = *pos + count, limita size a XATTR_SIZE_MAX (0x10000) cuando se excede, y vuelve a calcular count = (*pos + count) - 0x10000, pero aún así ejecuta memcpy(&stream_buf[*pos], buf, count) en un buffer de 0x10000 bytes. Si *pos ≥ 0x10000 el puntero destino ya está fuera de la asignación, produciendo un OOB write de count bytes.
Fragmento de función vulnerable (ksmbd_vfs_stream_write)
// https://elixir.bootlin.com/linux/v5.15/source/fs/ksmbd/vfs.c#L411
static int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, size_t count)
{
char *stream_buf = NULL, *wbuf;
size_t size;
...
size = *pos + count;
if (size > XATTR_SIZE_MAX) { // [1] clamp allocation, but...
size = XATTR_SIZE_MAX;
count = (*pos + count) - XATTR_SIZE_MAX; // [1.1] ...recompute count
}
wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO); // [2] alloc 0x10000
stream_buf = wbuf;
memcpy(&stream_buf[*pos], buf, count); // [3] OOB when *pos >= 0x10000
...
kvfree(stream_buf);
return err;
}
Offset steering y longitud OOB
- Ejemplo: set file offset (pos) a 0x10018 y la longitud original (count) a 8. After clamping, count' = (0x10018 + 8) - 0x10000 = 0x20, but memcpy writes 32 bytes starting at stream_buf[0x10018], i.e., 0x18 bytes beyond the 16-page allocation.
Triggering the bug via SMB streams write
- Usa la misma conexión SMB autenticada para abrir un archivo en el share y realiza un write a un named stream (streams_xattr). Set file_offset ≥ 0x10000 con una longitud pequeña para generar un OOB write determinista de tamaño controlable.
- libsmb2 puede usarse para autenticar y craftar dichos writes sobre SMB2/3.
Alcance mínimo (concepto)
// Pseudocode: send SMB streams write with pos=0x0000010018ULL, len=8
smb2_session_login(...);
smb2_open("\\\\host\\share\\file:stream", ...);
smb2_pwrite(fd, payload, 8, 0x0000010018ULL); // yields 32-byte OOB
Comportamiento del asignador y por qué se requiere el modelado de páginas
- kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) requests an order-4 (16 páginas contiguas) allocation from the buddy allocator when size > KMALLOC_MAX_CACHE_SIZE. This is not a SLUB cache object.
- memcpy occurs immediately after allocation; post-allocation spraying is ineffective. You must pre-groom physical memory so that a chosen target lies immediately after the allocated 16-page block.
- On Ubuntu, GFP_KERNEL often pulls from the Unmovable migrate type in zone Normal. Exhaust order-3 and order-4 freelists to force the allocator to split an order-5 block into an adjacent order-4 + order-3 pair, then park an order-3 slab (kmalloc-cg-4k) directly after the stream buffer.
Estrategia práctica de modelado de páginas
- Spray ~1000–2000 msg_msg objects of ~4096 bytes (fits kmalloc-cg-4k) to populate order-3 slabs.
- Receive some messages to punch holes and encourage adjacency.
- Trigger the ksmbd OOB repeatedly until the order-4 stream buffer lands immediately before a msg_msg slab. Use eBPF tracing to confirm addresses and alignment if available.
Observabilidad útil
# Check per-order freelists and migrate types
sudo cat /proc/pagetypeinfo | sed -n '/Node 0, zone Normal/,/Node/p'
# Example tracer (see reference repo) to log kvmalloc addresses/sizes
sudo ./bpf-tracer.sh
Exploitation plan (msg_msg + pipe_buffer), adapted from CVE-2021-22555
- Spray muchos mensajes System V msg_msg primarios/secundarios (4KiB-sized to fit kmalloc-cg-4k).
- Trigger ksmbd OOB para corromper el puntero next de un primary message de forma que dos primaries compartan un secondary.
- Detectar el par corrompido etiquetando las colas y escaneando con msgrcv(MSG_COPY) para encontrar tags que no coinciden.
- Free del secondary real para crear un UAF; reclaim con datos controlados vía UNIX sockets (construir un fake msg_msg).
- Leak kernel heap pointers abusando del m_ts over-read en copy_msg para obtener mlist.next/mlist.prev (SMAP bypass).
- Con un sk_buff spray, reconstruir un fake msg_msg consistente con enlaces válidos y liberarlo normalmente para estabilizar el estado.
- Reclaim del UAF con objetos struct pipe_buffer; leak anon_pipe_buf_ops para calcular la base del kernel (derrotar KASLR).
- Spray un fake pipe_buf_operations con release apuntando a un stack pivot/ROP gadget; cerrar pipes para ejecutar y obtener root.
Bypasses y notas
- KASLR: leak anon_pipe_buf_ops, calcular base (kbase_addr) y direcciones de gadgets.
- SMEP/SMAP: ejecutar ROP en contexto kernel vía el flujo pipe_buf_operations->release; evitar derefs a userspace hasta después de la cadena disable/prepare_kernel_cred/commit_creds.
- Hardened usercopy: no aplica a este primitive de page overflow; los campos corruptos no son campos de usercopy.
Confiabilidad
- Alta una vez conseguida la adyacencia; fallos ocasionales o panics (<10%). Ajustar cuentas de spray/free mejora la estabilidad. Se reportó que sobrescribir dos LSBs de un puntero para inducir colisiones específicas es efectivo (p. ej., escribir el patrón 0x0000_0000_0000_0500 en el overlap).
Parámetros clave a ajustar
- Número de sprays de msg_msg y patrón de holes
- OOB offset (pos) y la OOB length resultante (count')
- Número de sprays de UNIX socket, sk_buff y pipe_buffer en cada etapa
Mitigaciones y alcance
- Fix: clamp tanto la allocation como destination/length o acotar memcpy contra el tamaño asignado; parches upstream rastreados como CVE-2025-37947.
- Remote exploitation requeriría además un infoleak fiable y remote heap grooming; este write-up se centra en LPE local.
References PoC and tooling
- libsmb2 for SMB auth and streams writes
- eBPF tracer script to log kvmalloc addresses and histogram allocations (e.g., grep 4048 out-4096.txt)
- Minimal reachability PoC and full local exploit are publicly available (see References)
References
- ksmbd - Exploiting CVE-2025-37947 (3/3) — Doyensec
- libsmb2
- KSMBD-CVE-2025-37947: proof-of-concept.c
- KSMBD-CVE-2025-37947: CVE-2025-37947.c (full exploit)
- bpf-tracer.sh
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks