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

Tip

Μάθε & εξασκήσου στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks

Αυτή η σελίδα τεκμηριώνει ένα deterministic out-of-bounds write στο ksmbd streams handling που επιτρέπει ένα reliable Linux kernel privilege escalation στο Ubuntu 22.04 LTS (5.15.0-153-generic), παρακάμπτοντας KASLR, SMEP, και SMAP χρησιμοποιώντας standard kernel heap primitives (msg_msg + pipe_buffer).

  • Επηρεαζόμενο component: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
  • Primitive: page-overflow OOB write πέρα από ένα 0x10000-byte kvmalloc() buffer
  • Preconditions: ksmbd running με ένα authenticated, writable share using vfs streams_xattr

Example smb.conf

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

Root cause (allocation clamped, memcpy at unclamped offset)

  • Η συνάρτηση υπολογίζει size = *pos + count, περιορίζει το size στο XATTR_SIZE_MAX (0x10000) όταν ξεπερνιέται, και επανυπολογίζει count = (*pos + count) - 0x10000, αλλά εξακολουθεί να κάνει memcpy(&stream_buf[*pos], buf, count) μέσα σε buffer 0x10000 bytes. Αν *pos ≥ 0x10000 ο δείκτης προορισμού είναι ήδη έξω από την allocation, παράγοντας OOB write count bytes.
  • Το streams_xattr αποθηκεύει SMB alternate data streams μέσα σε POSIX extended attributes, οπότε το όριο 0x10000 προκύπτει από το Linux single-xattr size limit και όχι από κάποιο SMB protocol field. Αυτό κάνει το bug πρακτικό μόνο όταν το share ενεργοποιεί ρητά vfs objects = streams_xattr και το filesystem υποστηρίζει xattrs.

Why the write offset matters

  • Το vulnerable path δεν είναι απλώς “write more than 64KiB”. Το missing check ήταν ότι το *pos δεν validated against the current stream length (v_len) πριν εκτελεστεί η append/copy logic.
  • Το upstream διόρθωσε αυτό απορρίπτοντας writes όπου *pos >= v_len με -EINVAL. Pre-fix, ένας attacker μπορούσε να επαναχρησιμοποιήσει ένα valid authenticated handle σε ένα named stream και να στείλει ένα raw SMB2 WRITE του οποίου το file_offset ήδη δείχνει στο τέλος ή πέρα από το τέλος του υπάρχοντος stream, κάτι που μετατρέπει το post-clamp memcpy() σε deterministic page overflow.
  • Το public PoC το δείχνει αυτό authenticating with libsmb2, opening a stream path such as 1337:, extracting SessionId/TreeId/FileId, and then sending a handcrafted SMB2 WRITE with file_offset = 0x10018 and a small Length.
Vulnerable function snippet (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 και μήκος OOB

  • Παράδειγμα: ορίστε file offset (pos) σε 0x10018 και το αρχικό length (count) σε 8. Μετά το clamping, count’ = (0x10018 + 8) - 0x10000 = 0x20, αλλά το memcpy γράφει 32 bytes ξεκινώντας από stream_buf[0x10018], δηλαδή 0x18 bytes πέρα από το 16-page allocation.

Triggering the bug via SMB streams write

  • Χρησιμοποιήστε την ίδια authenticated SMB connection για να ανοίξετε ένα file στο share και εκτελέστε ένα write σε ένα named stream (streams_xattr). Ορίστε file_offset ≥ 0x10000 με μικρό length για να δημιουργήσετε ένα deterministic OOB write ελεγχόμενου μεγέθους.
  • Το libsmb2 μπορεί να χρησιμοποιηθεί για authentication και για να κατασκευάσει τέτοια writes over SMB2/3.
  • Στην πράξη, η επαναχρησιμοποίηση της negotiated SMB session είναι βολική επειδή το exploit χρειάζεται μόνο να διορθώσει λίγα dynamic fields στο WRITE request (TreeId, SessionId, FileId) και στη συνέχεια μπορεί να μεταδώσει το malformed packet απευθείας στο ίδιο socket.

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 και γιατί απαιτείται page shaping

  • Το kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) ζητά μια allocation order-4 (16 συνεχόμενες pages) από τον buddy allocator όταν το size > KMALLOC_MAX_CACHE_SIZE. Αυτό δεν είναι SLUB cache object.
  • Το memcpy συμβαίνει αμέσως μετά το allocation· το post-allocation spraying είναι αναποτελεσματικό. Πρέπει να προ-groom το φυσικό memory ώστε ένας επιλεγμένος target να βρίσκεται ακριβώς μετά το allocated 16-page block.
  • Στο Ubuntu, το GFP_KERNEL συχνά αντλεί από τον Unmovable migrate type στο zone Normal. Εξάντλησε τα order-3 και order-4 freelists για να αναγκάσεις τον allocator να σπάσει ένα order-5 block σε ένα γειτονικό order-4 + order-3 pair, και μετά τοποθέτησε ένα order-3 slab (kmalloc-cg-4k) ακριβώς μετά το stream buffer.

Πρακτική στρατηγική page shaping

  • Κάνε spray ~1000–2000 msg_msg objects των ~4096 bytes (ταιριάζει στο kmalloc-cg-4k) για να γεμίσεις order-3 slabs.
  • Λάβε κάποια messages για να ανοίξεις holes και να ενθαρρύνεις adjacency.
  • Ενεργοποίησε το ksmbd OOB επανειλημμένα μέχρι το order-4 stream buffer να προσγειωθεί ακριβώς πριν από ένα msg_msg slab. Χρησιμοποίησε eBPF tracing για να επιβεβαιώσεις addresses και alignment αν είναι διαθέσιμο.

Χρήσιμη παρατηρησιμότητα

# 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

Τι να παρακολουθείς κατά το tuning

  • kvmalloc_node(0x10000) επιβεβαιώνει πότε το ευάλωτο stream write καταναλώνει πραγματικά μια allocation order-4.
  • load_msg/kretprobe:load_msg σου επιτρέπει να εκτιμήσεις πόσες msg_msgseg allocations είναι attached σε κάθε sprayed message, κάτι που είναι χρήσιμο όταν κάνεις tuning τα primary/secondary message sizes για συγκεκριμένο kernel build.
  • Αν το exploit portαριστεί σε διαφορετικό distro/kernel, έλεγξε ξανά cache names, inline msg_msg payload sizes, offsets του anon_pipe_buf_ops, και gadget addresses αντί να υποθέτεις ότι οι constants του Ubuntu 22.04 LTS 5.15.0-153-generic εξακολουθούν να ταιριάζουν.

Σχέδιο exploitation (msg_msg + pipe_buffer), προσαρμοσμένο από το CVE-2021-22555

  1. Spraye πολλά System V msg_msg primary/secondary messages (4KiB-sized για να χωρέσουν στο kmalloc-cg-4k).
  2. Trigger το ksmbd OOB για να corrupt το next pointer ενός primary message έτσι ώστε δύο primaries να μοιράζονται ένα secondary.
  3. Ανίχνευσε το corrupted pair tagάροντας queues και κάνοντας scan με msgrcv(MSG_COPY) για να βρεις mismatched tags.
  4. Κάνε free το πραγματικό secondary για να δημιουργήσεις ένα UAF; reclaim it με controlled data μέσω UNIX sockets (craft ένα fake msg_msg).
  5. Leak kernel heap pointers abuse-άροντας το m_ts over-read στο copy_msg για να πάρεις mlist.next/mlist.prev (SMAP bypass).
  6. Με ένα sk_buff spray, ξαναχτίσε ένα συνεπές fake msg_msg με valid links και κάν’ το free κανονικά για να σταθεροποιήσεις την κατάσταση.
  7. Reclaim το UAF με struct pipe_buffer objects; leak το anon_pipe_buf_ops για να υπολογίσεις kernel base (defeat KASLR).
  8. Spray ένα fake pipe_buf_operations με release που δείχνει σε ένα stack pivot/ROP gadget; close pipes για να εκτελεστεί και να αποκτήσεις root.

Bypasses και notes

  • KASLR: leak το anon_pipe_buf_ops, υπολόγισε base (kbase_addr) και gadget addresses.
  • SMEP/SMAP: εκτέλεσε ROP σε kernel context μέσω της ροής pipe_buf_operations->release; απόφυγε userspace derefs μέχρι μετά το disable/prepare_kernel_cred/commit_creds chain.
  • Hardened usercopy: δεν ισχύει για αυτό το page overflow primitive; οι corruption targets είναι non-usercopy fields.

Reliability

  • Υψηλό μόλις επιτευχθεί adjacency; περιστασιακά misses ή panics (<10%). Το tuning στα spray/free counts βελτιώνει τη σταθερότητα. Το overwrite δύο LSBs ενός pointer για να προκληθούν συγκεκριμένες collisions αναφέρθηκε ως αποτελεσματικό (π.χ. γράψε pattern 0x0000_0000_0000_0500 στο overlap).

Key parameters to tune

  • Number of msg_msg sprays and hole pattern
  • OOB offset (pos) and resulting OOB length (count’)
  • Number of UNIX socket, sk_buff, and pipe_buffer sprays during each stage

Mitigations and reachability

  • Fix: clamp both allocation and destination/length or bound memcpy against the allocated size; upstream patches track as CVE-2025-37947.
  • Remote exploitation would additionally require a reliable infoleak and remote heap grooming; this write-up focuses on local LPE.

See also

Ksmbd Attack Surface And Fuzzing Syzkaller

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

Μάθε & εξασκήσου στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθε & εξασκήσου στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθε & εξασκήσου στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Περιηγήσου στον πλήρη κατάλογο HackTricks Training για τα assessment tracks (ARTA/GRTA/AzRTA) και στο Linux Hacking Expert (LHE).

Υποστήριξε το HackTricks