ksmbd streams_xattr OOB write → local LPE (CVE-2025-37947)

Reading time: 7 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Esta página documenta um out-of-bounds write determinístico no tratamento de streams do ksmbd que permite uma escalada de privilégios confiável no kernel do Linux no Ubuntu 22.04 LTS (5.15.0-153-generic), contornando KASLR, SMEP e SMAP usando primitivas padrão do heap do kernel (msg_msg + pipe_buffer).

  • Componente afetado: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
  • Primitiva: page-overflow OOB write past a 0x10000-byte kvmalloc() buffer
  • Pré-requisitos: ksmbd em execução com um compartilhamento autenticado e gravável usando vfs streams_xattr

Exemplo smb.conf

ini
[share]
path = /share
vfs objects = streams_xattr
writeable = yes

Causa raiz (alocação limitada, memcpy em deslocamento não limitado)

  • A função calcula size = *pos + count, limita size para XATTR_SIZE_MAX (0x10000) quando excede, e recalcula count = (*pos + count) - 0x10000, mas ainda assim executa memcpy(&stream_buf[*pos], buf, count) em um buffer de 0x10000 bytes. Se *pos ≥ 0x10000 o ponteiro de destino já está fora da alocação, produzindo um OOB write de count bytes.
Trecho da função vulnerável (ksmbd_vfs_stream_write)
c
// 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;
}

Direcionamento de offset e comprimento OOB

  • Exemplo: definir o file offset (pos) para 0x10018 e o comprimento original (count) para 8. Após o clamping, count' = (0x10018 + 8) - 0x10000 = 0x20, mas memcpy escreve 32 bytes começando em stream_buf[0x10018], ou seja, 0x18 bytes além da alocação de 16 páginas.

Disparando o bug via SMB streams write

  • Use a mesma conexão SMB autenticada para abrir um arquivo no share e emitir uma escrita para um stream nomeado (streams_xattr). Defina file_offset ≥ 0x10000 com um comprimento pequeno para gerar uma escrita OOB determinística de tamanho controlável.
  • libsmb2 pode ser usado para autenticar e construir essas escritas sobre SMB2/3.

Alcance mínimo (conceito)

c
// 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

Comportamento do allocator e por que page shaping é necessário

  • kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) requests an order-4 (16 contiguous pages) allocation from the buddy allocator when size > KMALLOC_MAX_CACHE_SIZE. Isso não é um objeto de cache SLUB.
  • memcpy ocorre imediatamente após a alocação; post-allocation spraying é ineficaz. Você deve pre-groom a memória física para que um alvo escolhido fique imediatamente após o bloco de 16 páginas alocado.
  • On Ubuntu, GFP_KERNEL often pulls from the Unmovable migrate type in zone Normal. Esgote os freelists order-3 e order-4 para forçar o allocator a dividir um bloco order-5 em um par adjacente order-4 + order-3, então posicione um order-3 slab (kmalloc-cg-4k) diretamente após o stream buffer.

Practical page shaping strategy

  • Spray ~1000–2000 msg_msg objects de ~4096 bytes (fits kmalloc-cg-4k) para popular order-3 slabs.
  • Receba algumas mensagens para punch holes e encorajar adjacency.
  • Trigger o ksmbd OOB repetidamente até que o order-4 stream buffer fique imediatamente antes de um msg_msg slab. Use eBPF tracing para confirmar endereços e alinhamento, se disponível.

Useful observability

bash
# 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

Plano de exploração (msg_msg + pipe_buffer), adaptado de CVE-2021-22555

  1. Spray de muitas mensagens System V msg_msg primárias/secundárias (tamanho 4KiB para caber em kmalloc-cg-4k).
  2. Disparar OOB do ksmbd para corromper o ponteiro next de uma primary message de forma que duas primárias compartilhem uma secondary.
  3. Detectar o par corrompido tagueando filas e escaneando com msgrcv(MSG_COPY) para encontrar tags que não batem.
  4. Free da secondary real para criar um UAF; reclaim com dados controlados via UNIX sockets (construir um fake msg_msg).
  5. Leak ponteiros do kernel heap abusando de over-read em m_ts dentro de copy_msg para obter mlist.next/mlist.prev (SMAP bypass).
  6. Com um spray de sk_buff, reconstruir um fake msg_msg consistente com links válidos e freeá-lo normalmente para estabilizar o estado.
  7. Reclaim do UAF com objetos struct pipe_buffer; leak anon_pipe_buf_ops para calcular kernel base (derrotar KASLR).
  8. Spray de um fake pipe_buf_operations com release apontando para um stack pivot/gadget ROP; fechar pipes para executar e obter root.

Bypasses and notes

  • KASLR: leak anon_pipe_buf_ops, compute base (kbase_addr) and gadget addresses.
  • SMEP/SMAP: execute ROP in kernel context via pipe_buf_operations->release flow; avoid userspace derefs until after disable/prepare_kernel_cred/commit_creds chain.
  • Hardened usercopy: not applicable to this page overflow primitive; corruption targets are non-usercopy fields.

Reliability

  • Alta uma vez que a adjacência é obtida; falhas ocasionais ou panics (<10%). Ajustar counts de spray/free melhora a estabilidade. Sobrescrever dois LSBs de um ponteiro para induzir colisões específicas foi reportado como efetivo (por exemplo, escrever o padrão 0x0000_0000_0000_0500 no overlap).

Key parameters to tune

  • Número de sprays de msg_msg e padrão de holes
  • OOB offset (pos) e OOB length resultante (count')
  • Número de sprays de UNIX socket, sk_buff e pipe_buffer em cada estágio

Mitigations and reachability

  • Fix: clamp both allocation and destination/length or bound memcpy against the allocated size; upstream patches track as CVE-2025-37947.
  • Exploração remota exigiria adicionalmente um infoleak confiável e remote heap grooming; este write-up foca em 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

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks