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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
이 페이지는 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)
// 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
- Spray many System V msg_msg primary/secondary messages (4KiB-sized to fit kmalloc-cg-4k).
- Trigger ksmbd OOB to corrupt a primary message’s next pointer so that two primaries share one secondary.
- Detect the corrupted pair by tagging queues and scanning with msgrcv(MSG_COPY) to find mismatched tags.
- Free the real secondary to create a UAF; reclaim it with controlled data via UNIX sockets (craft a fake msg_msg).
- Leak kernel heap pointers by abusing m_ts over-read in copy_msg to obtain mlist.next/mlist.prev (SMAP bypass).
- With an sk_buff spray, rebuild a consistent fake msg_msg with valid links and free it normally to stabilize state.
- Reclaim the UAF with struct pipe_buffer objects; leak anon_pipe_buf_ops to compute kernel base (defeat KASLR).
- 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
- ksmbd - Exploiting CVE-2025-37947 (3/3) — Doyensec
- libsmb2
- KSMBD-CVE-2025-37947: proof-of-concept.c
- KSMBD-CVE-2025-37947: CVE-2025-37947.c (full exploit)
- bpf-tracer.sh
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks