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) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기

이 페이지는 ksmbd streams 처리에서 결정적 out-of-bounds write를 문서화하며, standard kernel heap primitives (msg_msg + pipe_buffer)를 사용해 KASLR, SMEP, 및 SMAP을 우회하면서 Ubuntu 22.04 LTS (5.15.0-153-generic)에서 신뢰할 수 있는 Linux kernel privilege escalation을 가능하게 합니다.

  • Affected component: fs/ksmbd/vfs.c — ksmbd_vfs_stream_write()
  • Primitive: page-overflow OOB write past a 0x10000-byte kvmalloc() buffer
  • Preconditions: ksmbd running with an 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)로 clamp한 뒤 count = (*pos + count) - 0x10000을 다시 계산하지만, 여전히 0x10000바이트 버퍼에 memcpy(&stream_buf[*pos], buf, count)를 수행한다. 만약 *pos ≥ 0x10000이면 destination pointer는 이미 allocation 범위를 벗어나 있으며, 그 결과 count 바이트의 OOB write가 발생한다.
  • streams_xattr는 SMB alternate data streams를 POSIX extended attributes 안에 저장하므로, 0x10000 상한은 SMB protocol field가 아니라 Linux의 single-xattr size limit에서 온 것이다. 따라서 이 bug는 share가 명시적으로 vfs objects = streams_xattr를 활성화하고 filesystem이 xattrs를 지원할 때만 실질적으로 악용 가능하다.

Why the write offset matters

  • vulnerable path는 단순히 “64KiB보다 더 많이 write“하는 문제가 아니다. 누락된 check는 append/copy logic이 실행되기 전에 *pos가 현재 stream length(v_len)에 대해 validate되지 않았다는 점이다.
  • upstream은 이를 *pos >= v_len인 write를 -EINVAL로 reject하도록 fix했다. fix 이전에는 attacker가 named stream에 대한 유효한 authenticated handle을 재사용한 뒤, file_offset이 이미 existing stream의 끝 또는 그 이후를 가리키는 raw SMB2 WRITE를 보낼 수 있었고, 이로 인해 clamp 이후의 memcpy()가 deterministic page overflow로 이어졌다.
  • public PoC는 libsmb2로 authenticate하고, 1337: 같은 stream path를 open한 뒤, SessionId/TreeId/FileId를 추출하고, 그 다음 file_offset = 0x10018과 작은 Length를 가진 handcrafted SMB2 WRITE를 보내는 방식으로 이를 demonstrat한다.
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 steering 및 OOB length

  • 예: file offset (pos)을 0x10018로, 원래 length (count)를 8로 설정한다. clamping 후 count’ = (0x10018 + 8) - 0x10000 = 0x20이 되지만, memcpy는 stream_buf[0x10018]에서 시작해 32바이트를 write하므로 16-page allocation을 0x18바이트 넘어선다.

SMB streams write를 통한 bug trigger

  • 동일한 authenticated SMB connection을 사용해 share에서 file을 열고 named stream (streams_xattr)에 write를 issue한다. file_offset ≥ 0x10000에 작은 length를 설정해, 제어 가능한 크기의 결정적인 OOB write를 생성한다.
  • libsmb2는 SMB2/3 위에서 이런 write를 authenticate하고 craft하는 데 사용할 수 있다.
  • 실제로는 negotiated SMB session을 재사용하는 것이 편리한데, exploit은 WRITE request의 몇 개 dynamic field만 patch하면 되기 때문이다 (TreeId, SessionId, FileId). 그런 다음 malformed packet을 같은 socket으로 직접 transmit할 수 있다.

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

  • kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO)는 size > KMALLOC_MAX_CACHE_SIZE일 때 buddy allocator에서 order-4(연속된 16개 페이지) 할당을 요청한다. 이것은 SLUB cache object가 아니다.
  • memcpy는 할당 직후에 발생하므로, allocation 이후 spraying은 효과가 없다. 선택한 target이 할당된 16-page block 바로 뒤에 오도록, 미리 physical memory를 groom해야 한다.
  • Ubuntu에서는 GFP_KERNEL가 보통 zone Normal의 Unmovable migrate type에서 가져온다. order-3와 order-4 freelist를 소진해 allocator가 order-5 block을 인접한 order-4 + order-3 pair로 split하도록 강제한 다음, order-3 slab(kmalloc-cg-4k)을 stream buffer 바로 뒤에 배치한다.

Practical page shaping strategy

  • 약 1000~2000개의 msg_msg object를 각 ~4096 bytes(kmalloc-cg-4k에 적합)로 spray해서 order-3 slabs를 채운다.
  • 일부 메시지를 receive해서 hole을 만들고 adjacency를 유도한다.
  • ksmbd OOB를 반복적으로 trigger해서 order-4 stream buffer가 msg_msg slab 바로 앞에 오도록 만든다. 가능하면 eBPF tracing으로 address와 alignment를 확인한다.

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

튜닝할 때 추적할 것

  • kvmalloc_node(0x10000)는 취약한 stream write가 실제로 order-4 allocation을 소비하는 시점을 확인해준다.
  • load_msg/kretprobe:load_msg는 각 spray된 message에 몇 개의 msg_msgseg allocations이 붙는지 추정하게 해주며, 특정 kernel build에서 primary/secondary message size를 튜닝할 때 유용하다.
  • exploit을 다른 distro/kernel로 포팅한다면, Ubuntu 22.04 LTS 5.15.0-153-generic 상수가 그대로 맞을 것이라고 가정하지 말고 cache 이름, inline msg_msg payload sizes, anon_pipe_buf_ops offsets, gadget addresses를 다시 확인하라.

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

  1. 많은 System V msg_msg primary/secondary messages( kmalloc-cg-4k에 맞도록 4KiB 크기)를 spray한다.
  2. ksmbd OOB를 trigger하여 primary message의 next pointer를 corrupt해서 두 개의 primaries가 하나의 secondary를 공유하게 만든다.
  3. queues를 tagging하고 msgrcv(MSG_COPY)로 scanning하여 tag가 맞지 않는 pair를 찾아 corrupted pair를 detect한다.
  4. real secondary를 free하여 UAF를 만든다; UNIX sockets를 통해 controlled data로 이를 reclaim한다(fake msg_msg를 craft).
  5. copy_msg에서 m_ts over-read를 악용해 kernel heap pointers를 leak하고 mlist.next/mlist.prev를 얻는다(SMAP bypass).
  6. sk_buff spray와 함께 valid links를 가진 일관된 fake msg_msg를 다시 rebuild하고 normally free하여 state를 stabilize한다.
  7. struct pipe_buffer objects로 UAF를 reclaim한다; anon_pipe_buf_ops를 leak하여 kernel base를 계산한다(KASLR defeat).
  8. release가 stack pivot/ROP gadget을 가리키는 fake pipe_buf_operations를 spray하고, pipes를 close해서 실행시킨 뒤 root 권한을 얻는다.

Bypasses and notes

  • KASLR: anon_pipe_buf_ops를 leak하고 base(kbase_addr)와 gadget addresses를 계산한다.
  • SMEP/SMAP: pipe_buf_operations->release flow를 통해 kernel context에서 ROP를 실행한다; disable/prepare_kernel_cred/commit_creds chain 전에는 userspace derefs를 피한다.
  • Hardened usercopy: 이 page overflow primitive에는 적용되지 않는다; corruption targets는 non-usercopy fields이다.

Reliability

  • adjacency를 확보하면 안정성이 높다; 가끔 miss나 panic(<10%)이 발생한다. spray/free counts를 튜닝하면 안정성이 향상된다. 특정 collision을 유도하기 위해 pointer의 두 LSB를 overwrite하는 방식이 효과적이라고 보고되었다(예: overlap에 0x0000_0000_0000_0500 pattern write).

Key parameters to tune

  • msg_msg sprays와 hole pattern의 개수
  • OOB offset (pos)와 그에 따른 OOB length (count’)
  • 각 stage 동안의 UNIX socket, sk_buff, pipe_buffer sprays 개수

Mitigations and reachability

  • Fix: allocation과 destination/length를 모두 clamp하거나 allocated size에 대해 memcpy를 bound 하라; upstream patches는 CVE-2025-37947로 추적된다.
  • Remote exploitation은 추가로 reliable infoleak과 remote heap grooming이 필요하다; 이 write-up은 local LPE에 초점을 맞춘다.

See also

Ksmbd Attack Surface And Fuzzing Syzkaller

References PoC and tooling

  • SMB auth와 streams writes를 위한 libsmb2
  • kvmalloc addresses를 log하고 allocations histogram을 만드는 eBPF tracer script (예: grep 4048 out-4096.txt)
  • Minimal reachability PoC와 full local exploit은 공개되어 있다 (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) 평가 트랙 (ARTA/GRTA/AzRTA)과 Linux Hacking Expert (LHE)를 보려면 전체 HackTricks Training 카탈로그를 둘러보세요.

HackTricks 지원하기