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

Reading time: 7 minutes

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

ini
[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을 통해 이러한 쓰기 작업을 인증하고 구성하는 데 사용할 수 있다.

최소 도달 가능성 (개념)

c
// 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

유용한 관측성

bash
# 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 지원하기