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

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Diese Seite dokumentiert einen deterministischen Out-of-bounds write in der ksmbd-Streams-Verarbeitung, der eine zuverlässige Linux-Kernel-Privilege-Escalation auf Ubuntu 22.04 LTS (5.15.0-153-generic) ermöglicht, indem KASLR, SMEP und SMAP mit standardmäßigen Kernel-Heap-Primitives (msg_msg + pipe_buffer) umgangen werden.

  • Betroffene Komponente: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
  • Primitive: page-overflow OOB write über einen 0x10000-Byte-kvmalloc()-Buffer hinaus
  • Voraussetzungen: ksmbd läuft mit einer authentifizierten, beschreibbaren Freigabe unter Verwendung von vfs streams_xattr

Beispiel smb.conf

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

Ursache des Problems (Allocation begrenzt, memcpy an ungebremstem Offset)

  • Die Funktion berechnet size = *pos + count, begrenzt size auf XATTR_SIZE_MAX (0x10000), wenn es überschritten wird, und berechnet count = (*pos + count) - 0x10000 neu, führt aber trotzdem memcpy(&stream_buf[*pos], buf, count) in einen 0x10000-Byte-Puffer aus. Wenn *pos ≥ 0x10000 ist, liegt der Zielpointer bereits außerhalb der Allocation, wodurch ein OOB write von count Bytes entsteht.
  • streams_xattr speichert SMB alternate data streams in POSIX extended attributes, daher stammt die 0x10000-Grenze vom Linux-Single-xattr-Size-Limit und nicht von einem SMB-Protokollfeld. Das macht den Bug nur dann praktisch ausnutzbar, wenn die Freigabe ausdrücklich vfs objects = streams_xattr aktiviert und das Dateisystem xattrs unterstützt.

Warum der Schreib-Offset wichtig ist

  • Der verwundbare Pfad ist nicht nur „mehr als 64KiB schreiben“. Die fehlende Prüfung war, dass *pos nicht gegen die aktuelle Stream-Länge (v_len) validiert wurde, bevor die Append/Copy-Logik lief.
  • Upstream hat das behoben, indem Writes mit *pos >= v_len mit -EINVAL abgelehnt werden. Vor dem Fix konnte ein Angreifer einen gültig authentifizierten Handle auf einen benannten Stream wiederverwenden und einen rohen SMB2 WRITE senden, dessen file_offset bereits auf oder hinter dem Ende des vorhandenen Streams zeigt, wodurch das post-clamp memcpy() zu einem deterministischen Page overflow wird.
  • Der öffentliche PoC demonstriert das, indem er sich mit libsmb2 authentifiziert, einen Stream-Pfad wie 1337: öffnet, SessionId/TreeId/FileId extrahiert und dann einen handgefertigten SMB2 WRITE mit file_offset = 0x10018 und einer kleinen Length sendet.
Verwundbarer Funktionsausschnitt (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; } ```

Offset-Steering und OOB-Länge

  • Beispiel: Setze den File-Offset (pos) auf 0x10018 und die ursprüngliche Länge (count) auf 8. Nach dem Clamping ist count’ = (0x10018 + 8) - 0x10000 = 0x20, aber memcpy schreibt 32 Bytes beginnend bei stream_buf[0x10018], also 0x18 Bytes über die 16-Page-Allocation hinaus.

Triggering the bug via SMB streams write

  • Verwende dieselbe authentifizierte SMB-Verbindung, um eine Datei auf dem Share zu öffnen und einen Write auf einen named stream (streams_xattr) auszuführen. Setze file_offset ≥ 0x10000 mit einer kleinen Länge, um einen deterministischen OOB write mit kontrollierbarer Größe zu erzeugen.
  • libsmb2 kann verwendet werden, um zu authentifizieren und solche Writes über SMB2/3 zu bauen.
  • In der Praxis ist es bequem, die ausgehandelte SMB-Session wiederzuverwenden, weil der Exploit nur ein paar dynamische Felder im WRITE-Request (TreeId, SessionId, FileId) patchen muss und dann das fehlerhafte Paket direkt über denselben Socket senden kann.

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

Allocator-Verhalten und warum page shaping erforderlich ist

  • kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) fordert eine order-4-Zuweisung (16 zusammenhängende pages) vom buddy allocator an, wenn size > KMALLOC_MAX_CACHE_SIZE. Das ist kein SLUB cache object.
  • memcpy erfolgt unmittelbar nach der Zuweisung; post-allocation spraying ist unwirksam. Du musst den physical memory vorher groomen, damit ein ausgewähltes target direkt hinter dem zugewiesenen 16-page block liegt.
  • Auf Ubuntu zieht GFP_KERNEL oft aus dem Unmovable migrate type in zone Normal. Erschöpfe die order-3- und order-4-freelists, um den allocator zu zwingen, einen order-5 block in ein angrenzendes order-4 + order-3 pair zu splitten, und parke dann ein order-3 slab (kmalloc-cg-4k) direkt hinter dem stream buffer.

Praktische page shaping strategy

  • Sprite ~1000–2000 msg_msg objects mit ~4096 bytes (passt zu kmalloc-cg-4k), um order-3 slabs zu belegen.
  • Empfange einige messages, um holes zu erzeugen und adjacency zu fördern.
  • Triggere den ksmbd OOB wiederholt, bis der order-4 stream buffer direkt vor einem msg_msg slab landet. Nutze eBPF tracing, um addresses und alignment zu bestätigen, falls verfügbar.

Nützliche Observability

# 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

Was beim Tuning zu tracen ist

  • kvmalloc_node(0x10000) bestätigt, wann der verwundbare stream write tatsächlich eine order-4-Allokation verbraucht.
  • load_msg/kretprobe:load_msg lässt dich abschätzen, wie viele msg_msgseg-Allokationen an jede gesprayte Message angehängt sind, was beim Tuning von primary/secondary Message-Größen für einen bestimmten Kernel-Build nützlich ist.
  • Wenn der exploit auf eine andere Distro/einen anderen Kernel portiert wird, prüfe Cache-Namen, Inline-msg_msg-Payload-Größen, anon_pipe_buf_ops-Offsets und Gadget-Adressen erneut, statt anzunehmen, dass die Ubuntu 22.04 LTS 5.15.0-153-generic-Konstanten noch passen.

Exploitation-Plan (msg_msg + pipe_buffer), angepasst von CVE-2021-22555

  1. Viele System V msg_msg primary/secondary Messages sprayen (4KiB groß, um in kmalloc-cg-4k zu passen).
  2. ksmbd OOB auslösen, um den next-Pointer einer primary Message zu korrumpieren, sodass zwei primaries eine secondary teilen.
  3. Das korrumpierte Paar durch Taggen von Queues und Scannen mit msgrcv(MSG_COPY) erkennen, um nicht übereinstimmende Tags zu finden.
  4. Die echte secondary freigeben, um ein UAF zu erzeugen; sie mit kontrollierten Daten via UNIX sockets reclaimen (eine gefälschte msg_msg bauen).
  5. Kernel-Heap-Pointer leaken, indem m_ts-Over-read in copy_msg ausgenutzt wird, um mlist.next/mlist.prev zu erhalten (SMAP-Bypass).
  6. Mit einem sk_buff-Spray eine konsistente gefälschte msg_msg mit gültigen Links wieder aufbauen und sie normal freigeben, um den Zustand zu stabilisieren.
  7. Das UAF mit struct pipe_buffer-Objekten reclaimen; anon_pipe_buf_ops leaken, um die Kernel-Base zu berechnen (KASLR besiegen).
  8. Eine gefälschte pipe_buf_operations sprayen, deren release auf einen Stack-Pivot/ROP-Gadget zeigt; Pipes schließen, um auszuführen und root zu erlangen.

Bypasses und Notizen

  • KASLR: anon_pipe_buf_ops leaken, Base (kbase_addr) und Gadget-Adressen berechnen.
  • SMEP/SMAP: ROP im Kernel-Kontext über den pipe_buf_operations->release-Flow ausführen; Userspace-Derefs vermeiden, bis die disable/prepare_kernel_cred/commit_creds-Kette ausgeführt wurde.
  • Hardened usercopy: für diese Page-Overflow-Primitive nicht relevant; die Korruptionsziele sind keine Usercopy-Felder.

Zuverlässigkeit

  • Hoch, sobald Adjazenz erreicht ist; gelegentliche Misses oder Panics (<10%). Das Tuning von Spray-/Free-Counts verbessert die Stabilität. Das Überschreiben der zwei LSBs eines Pointers, um bestimmte Kollisionen zu erzwingen, wurde als effektiv berichtet (z. B. ein 0x0000_0000_0000_0500-Muster in den Overlap schreiben).

Wichtige Parameter zum Tuning

  • Anzahl der msg_msg-Sprays und Hole-Pattern
  • OOB-Offset (pos) und resultierende OOB-Länge (count')
  • Anzahl der UNIX-socket-, sk_buff- und pipe_buffer-Sprays in jeder Phase

Mitigations und Erreichbarkeit

  • Fix: sowohl Allokation als auch Destination/Länge begrenzen oder memcpy gegen die zugewiesene Größe bounds; Upstream-Patches werden als CVE-2025-37947 verfolgt.
  • Remote exploitation würde zusätzlich einen zuverlässigen infoleak und Remote-Heap-Grooming erfordern; diese Beschreibung konzentriert sich auf lokales LPE.

Siehe auch

Ksmbd Attack Surface And Fuzzing Syzkaller

References PoC und Tooling

  • libsmb2 für SMB-Auth und streams writes
  • eBPF-Tracer-Script zum Loggen von kvmalloc-Adressen und zum Erstellen eines Histogramms der Allokationen (z. B. grep 4048 out-4096.txt)
  • Ein minimales Reachability-PoC und ein vollständiger lokaler exploit sind öffentlich verfügbar (siehe References)

References

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks