ksmbd streams_xattr OOB write โ†’ local LPE (CVE-2025-37947)

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

์ด ํŽ˜์ด์ง€๋Š” ksmbd streams ์ฒ˜๋ฆฌ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฐ์ •๋ก ์  out-of-bounds write๋ฅผ ๋ฌธ์„œํ™”ํ•˜๋ฉฐ, ํ‘œ์ค€ ์ปค๋„ ํž™ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ (msg_msg + pipe_buffer)๋ฅผ ์‚ฌ์šฉํ•ด KASLR, SMEP, SMAP์„ ์šฐํšŒํ•˜์—ฌ Ubuntu 22.04 LTS (5.15.0-153-generic)์—์„œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” Linux kernel privilege escalation์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

  • ์˜ํ–ฅ๋ฐ›๋Š” ๊ตฌ์„ฑ์š”์†Œ: fs/ksmbd/vfs.c โ€” ksmbd_vfs_stream_write()
  • ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ: page-overflow OOB write past a 0x10000-byte kvmalloc() buffer
  • ์ „์ œ ์กฐ๊ฑด: vfs streams_xattr๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ธ์ฆ๋œ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๊ณต์œ ๊ฐ€ ์„ค์ •๋œ ์ƒํƒœ์—์„œ ksmbd๊ฐ€ ์‹คํ–‰ ์ค‘

Example smb.conf

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

๊ทผ๋ณธ ์›์ธ (ํ• ๋‹น์ด ์ œํ•œ๋˜๊ณ , ์ œํ•œ๋˜์ง€ ์•Š์€ ์˜คํ”„์…‹์—์„œ memcpy ๋ฐœ์ƒ)

  • ํ•จ์ˆ˜๋Š” size = *pos + count ๋ฅผ ๊ณ„์‚ฐํ•˜๊ณ , ์ดˆ๊ณผํ•˜๋ฉด size๋ฅผ XATTR_SIZE_MAX (0x10000)์œผ๋กœ ์ œํ•œ(clamp)ํ•˜๋ฉฐ, count๋ฅผ (*pos + count) - 0x10000 ์œผ๋กœ ์žฌ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์—ฌ์ „ํžˆ memcpy(&stream_buf[*pos], buf, count)๋ฅผ 0x10000-๋ฐ”์ดํŠธ ๋ฒ„ํผ์— ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ *pos โ‰ฅ 0x10000 ์ด๋ฉด ๋ชฉ์ ์ง€ ํฌ์ธํ„ฐ๋Š” ์ด๋ฏธ ํ• ๋‹น ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฏ€๋กœ count ๋ฐ”์ดํŠธ์˜ OOB write๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
์ทจ์•ฝํ•œ ํ•จ์ˆ˜ ์Šค๋‹ˆํŽซ (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; } ```

์˜คํ”„์…‹ ์กฐ์ • ๋ฐ OOB ๊ธธ์ด

  • ์˜ˆ: file offset (pos)๋ฅผ 0x10018๋กœ ์„ค์ •ํ•˜๊ณ  ์›๋ž˜ ๊ธธ์ด (count)๋ฅผ 8๋กœ ์„ค์ •. ํด๋žจํ•‘ ํ›„, countโ€™ = (0x10018 + 8) - 0x10000 = 0x20 ์ด์ง€๋งŒ memcpy๋Š” stream_buf[0x10018]์—์„œ ์‹œ์ž‘ํ•ด 32๋ฐ”์ดํŠธ๋ฅผ ์“ฐ๋ฏ€๋กœ 16-ํŽ˜์ด์ง€ ํ• ๋‹น๋ณด๋‹ค 0x18 ๋ฐ”์ดํŠธ ์ดˆ๊ณผํ•œ๋‹ค.

SMB streams write๋ฅผ ํ†ตํ•ด ๋ฒ„๊ทธ ์œ ๋ฐœ

  • ๋™์ผํ•œ ์ธ์ฆ๋œ SMB ์—ฐ๊ฒฐ์„ ์‚ฌ์šฉํ•ด share์˜ ํŒŒ์ผ์„ ์—ด๊ณ  named stream (streams_xattr)์— write๋ฅผ ์‹คํ–‰ํ•œ๋‹ค. file_offset โ‰ฅ 0x10000๋กœ ์„ค์ •ํ•˜๊ณ  ์ž‘์€ ๊ธธ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ œ์–ด ๊ฐ€๋Šฅํ•œ ํฌ๊ธฐ์˜ ๊ฒฐ์ •๋ก ์  OOB write๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • libsmb2๋Š” SMB2/3์„ ํ†ตํ•ด ์ด๋Ÿฌํ•œ ์“ฐ๊ธฐ ์ž‘์—…์„ ์ธ์ฆํ•˜๊ณ  ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ตœ์†Œ ๋„๋‹ฌ ๊ฐ€๋Šฅ์„ฑ (๊ฐœ๋…)

// 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 behavior and why page shaping is required

  • 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.

ํ• ๋‹น์ž ๋™์ž‘ ๋ฐ ์™œ page shaping์ด ํ•„์š”ํ•œ๊ฐ€

  • kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO)๋Š” size > KMALLOC_MAX_CACHE_SIZE์ผ ๋•Œ buddy allocator๋กœ๋ถ€ํ„ฐ order-4(์—ฐ์†๋œ 16ํŽ˜์ด์ง€) ํ• ๋‹น์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” SLUB cache ๊ฐ์ฒด๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.
  • memcpy๋Š” ํ• ๋‹น ์งํ›„์— ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ํ• ๋‹น ํ›„์— ์Šคํ”„๋ ˆ์ดํ•˜๋Š” ๊ฒƒ์€ ํšจ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์„ ํƒํ•œ ํƒ€๊นƒ์ด ํ• ๋‹น๋œ 16ํŽ˜์ด์ง€ ๋ธ”๋ก ๋ฐ”๋กœ ๋‹ค์Œ์— ์˜ค๋„๋ก ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์ „์— ์ •๋ฆฌ(pre-groom)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Ubuntu์—์„œ๋Š” GFP_KERNEL์ด zone Normal์˜ Unmovable migrate ํƒ€์ž…์—์„œ ํ• ๋‹น๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. order-3๊ณผ order-4 freelist๋ฅผ ๊ณ ๊ฐˆ์‹œ์ผœ allocator๊ฐ€ order-5 ๋ธ”๋ก์„ ์ธ์ ‘ํ•œ order-4 + order-3 ์Œ์œผ๋กœ ๋ถ„ํ• ํ•˜๋„๋ก ๊ฐ•์ œํ•œ ๋‹ค์Œ, stream buffer ๋ฐ”๋กœ ๋’ค์— order-3 slab(kmalloc-cg-4k)๋ฅผ ๋ฐฐ์น˜ํ•˜์„ธ์š”.

Practical page shaping strategy

  • 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.

์‹ค์ „ page shaping ์ „๋žต

  • order-3 slab์„ ์ฑ„์šฐ๊ธฐ ์œ„ํ•ด ์•ฝ 4096๋ฐ”์ดํŠธ ํฌ๊ธฐ์˜ msg_msg ๊ฐ์ฒด๋ฅผ ๋Œ€๋žต 1000โ€“2000๊ฐœ ์ •๋„ Sprayํ•˜์„ธ์š”(์ด๋Š” kmalloc-cg-4k์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค).
  • ์ผ๋ถ€ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•˜์—ฌ punch holes๋ฅผ ๋งŒ๋“ค๊ณ  ์ธ์ ‘์„ฑ์„ ์œ ๋„ํ•˜์„ธ์š”.
  • order-4 stream buffer๊ฐ€ msg_msg slab ๋ฐ”๋กœ ์•ž์— ์˜ฌ ๋•Œ๊นŒ์ง€ ksmbd OOB๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐํ•˜์„ธ์š”. ๊ฐ€๋Šฅํ•˜๋ฉด eBPF tracing์œผ๋กœ ์ฃผ์†Œ์™€ ์ •๋ ฌ์„ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค.

Useful 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

Exploitation plan (msg_msg + pipe_buffer), adapted from CVE-2021-22555

  1. Spray many System V msg_msg primary/secondary messages (4KiB-sized to fit kmalloc-cg-4k).
  2. Trigger ksmbd OOB to corrupt a primary messageโ€™s next pointer so that two primaries share one secondary.
  3. Detect the corrupted pair by tagging queues and scanning with msgrcv(MSG_COPY) to find mismatched tags.
  4. Free the real secondary to create a UAF; reclaim it with controlled data via UNIX sockets (craft a fake msg_msg).
  5. Leak kernel heap pointers by abusing m_ts over-read in copy_msg to obtain mlist.next/mlist.prev (SMAP bypass).
  6. With an sk_buff spray, rebuild a consistent fake msg_msg with valid links and free it normally to stabilize state.
  7. Reclaim the UAF with struct pipe_buffer objects; leak anon_pipe_buf_ops to compute kernel base (defeat KASLR).
  8. Spray a fake pipe_buf_operations with release pointing to a stack pivot/ROP gadget; close pipes to execute and gain root.

์šฐํšŒ ๋ฐ ๋…ธํŠธ

  • KASLR: anon_pipe_buf_ops๋ฅผ leakํ•˜์—ฌ base(kbase_addr)์™€ gadget ์ฃผ์†Œ ๊ณ„์‚ฐ.
  • SMEP/SMAP: pipe_buf_operations->release ํ๋ฆ„์„ ํ†ตํ•ด kernel ์ปจํ…์ŠคํŠธ์—์„œ ROP๋ฅผ ์‹คํ–‰; disable/prepare_kernel_cred/commit_creds ์ฒด์ธ ์ด์ „๊นŒ์ง€ userspace derefs๋ฅผ ํ”ผํ•จ.
  • Hardened usercopy: ์ด page overflow primitive์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Œ; ์†์ƒ ๋Œ€์ƒ์€ non-usercopy ํ•„๋“œ.

์‹ ๋ขฐ์„ฑ

  • ์ธ์ ‘์„ฑ(adjacency)์„ ํ™•๋ณดํ•˜๋ฉด ์‹ ๋ขฐ๋„ ๋†’์Œ; ๊ฐ€๋” ์‹คํŒจ๋‚˜ panic ๋ฐœ์ƒ(<10%). spray/free ํšŸ์ˆ˜ ์กฐ์ •์œผ๋กœ ์•ˆ์ •์„ฑ ํ–ฅ์ƒ. ํŠน์ • ์ถฉ๋Œ์„ ์œ ๋„ํ•˜๊ธฐ ์œ„ํ•ด ํฌ์ธํ„ฐ์˜ ๋‘ LSBs๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ๋ฐฉ๋ฒ•์ด ํšจ๊ณผ์ ์ด๋ผ๊ณ  ๋ณด๊ณ ๋จ(์˜ˆ: ๊ฒน์นจ ์˜์—ญ์— 0x0000_0000_0000_0500 ํŒจํ„ด์„ ์”€).

์กฐ์ •ํ•ด์•ผ ํ•  ์ฃผ์š” ํŒŒ๋ผ๋ฏธํ„ฐ

  • 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

์™„ํ™” ๋ฐ ๋„๋‹ฌ์„ฑ

  • 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.

์ฐธ๊ณ : PoC ๋ฐ ๋„๊ตฌ

  • 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 ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ