ksmbd streams_xattr OOB write → local LPE (CVE-2025-37947)
Reading time: 6 minutes
tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Ta strona dokumentuje deterministyczny out-of-bounds write w obsłudze ksmbd streams, który umożliwia niezawodne eskalowanie uprawnień jądra Linux na Ubuntu 22.04 LTS (5.15.0-153-generic), omijając KASLR, SMEP i SMAP przy użyciu standardowych prymitywów sterty jądra (msg_msg + pipe_buffer).
- Dotknięty komponent: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
- Prymityw: page-overflow OOB write past a 0x10000-byte kvmalloc() buffer
- Warunki wstępne: ksmbd uruchomiony z uwierzytelnionym, zapisywalnym udostępnieniem używającym vfs streams_xattr
Przykład smb.conf
[share]
path = /share
vfs objects = streams_xattr
writeable = yes
Root cause (allocation clamped, memcpy at unclamped offset)
- Funkcja oblicza size = *pos + count, ogranicza size do XATTR_SIZE_MAX (0x10000) jeśli zostanie przekroczony, i ponownie oblicza count = (*pos + count) - 0x10000, ale nadal wykonuje memcpy(&stream_buf[*pos], buf, count) do bufora o rozmiarze 0x10000 bajtów. Jeśli *pos ≥ 0x10000 wskaźnik docelowy znajduje się już poza alokacją, powodując OOB write o długości count bajtów.
Wrażliwy fragment funkcji (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 and OOB length
- Przykład: ustaw file offset (pos) na 0x10018 i oryginalną długość (count) na 8. Po clamping, count' = (0x10018 + 8) - 0x10000 = 0x20, ale memcpy zapisuje 32 bajty zaczynając od stream_buf[0x10018], tj. 0x18 bajtów poza 16-stronicową alokacją.
Triggering the bug via SMB streams write
- Użyj tego samego uwierzytelnionego połączenia SMB, aby otworzyć plik na udziale i wykonać zapis do nazwanego strumienia (streams_xattr). Ustaw file_offset ≥ 0x10000 z małą długością, aby wygenerować deterministyczny OOB write o kontrolowanym rozmiarze.
- libsmb2 może być użyte do uwierzytelnienia i skonstruowania takich zapisów przez SMB2/3.
Minimal reachability (concept)
// 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
Zachowanie alokatora i dlaczego wymagane jest page shaping
- kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) requests an order-4 (16 contiguous pages) 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.
Praktyczna strategia page shaping
- 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.
Przydatna obserwowalność
# 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
Plan eksploatacji (msg_msg + pipe_buffer), zaadaptowany z CVE-2021-22555
- Rozpylić wiele System V msg_msg primary/secondary messages (rozmiar 4KiB, aby pasowały do kmalloc-cg-4k).
- Wywołać ksmbd OOB, aby skorumpować wskaźnik next primary message, tak żeby dwie primary współdzieliły jedną secondary.
- Wykryć skorumpowaną parę przez tagowanie kolejek i skanowanie msgrcv(MSG_COPY), aby znaleźć niepasujące tagi.
- Zwolnić prawdziwą secondary, aby stworzyć UAF; odzyskać ją z kontrolowanymi danymi przez UNIX sockets (utworzyć fałszywy msg_msg).
- Uzyskać leak wskaźników sterty jądra przez nadużycie over-read m_ts w copy_msg, aby zdobyć mlist.next/mlist.prev (obejście SMAP).
- Przy sk_buff spray odbudować spójną fałszywą msg_msg z poprawnymi linkami i zwolnić ją normalnie, aby ustabilizować stan.
- Odzyskać UAF używając struct pipe_buffer obiektów; uzyskać leak anon_pipe_buf_ops, aby obliczyć bazę jądra (pokonać KASLR).
- Rozpylić fałszywe pipe_buf_operations z release wskazującym na stack pivot/ROP gadget; zamknąć pipe'y, aby wykonać kod i uzyskać root.
Bypasses i uwagi
- KASLR: leak anon_pipe_buf_ops, obliczyć bazę (kbase_addr) i adresy gadgetów.
- SMEP/SMAP: wykonać ROP w kontekście jądra przez pipe_buf_operations->release; unikać userspace derefs aż do wykonania łańcucha disable/prepare_kernel_cred/commit_creds.
- Hardened usercopy: nie dotyczy tego overflow primitive; cele korupcji to pola niebędące usercopy.
Niezawodność
- Wysoka po osiągnięciu adjacency; sporadyczne nieudane próby lub paniki (<10%). Dostosowanie liczby spray/free poprawia stabilność. Nadpisywanie dwóch LSB wskaźnika, aby wymusić specyficzne kolizje, było raportowane jako skuteczne (np. zapis wzoru 0x0000_0000_0000_0500 w obszarze overlap).
Kluczowe parametry do dostrojenia
- Liczba msg_msg sprayów i hole pattern
- OOB offset (pos) i wynikająca długość OOB (count')
- Liczba UNIX socket, sk_buff i pipe_buffer sprayów na każdym etapie
Mitigacje i zasięg
- Fix: obciąć zarówno allocation jak i destination/length albo ograniczyć memcpy względem przydzielonego rozmiaru; poprawki upstream śledzone jako CVE-2025-37947.
- Zdalna eksploatacja wymagałaby dodatkowo niezawodnego infoleak i zdalnego heap grooming; ten opis koncentruje się na lokalnym LPE.
Referencje PoC i narzędzia
- libsmb2 do SMB auth i streams writes
- eBPF tracer script do logowania adresów kvmalloc i histogramu alokacji (np. grep 4048 out-4096.txt)
- Minimalny reachability PoC i pełny lokalny exploit są dostępne publicznie (zobacz Referencje)
Referencje
- 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
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
HackTricks