CVE-2025-37899: let me try try

彩贝是这样的,没复现出来还恬不知耻地发Blog…
(记录一些踩的坑)

KSMBD - SMB3 内核服务器

KSMBD是一个Linux内核服务器,它在内核空间中实现了SMB3协议,用于在网络上共享文件。

KSMBD 架构

与性能相关的操作属于内核空间,而另一部分属于与性能无关的用户空间操作。因此,历史上导致缓冲区溢出问题和危险安全漏洞的DCE/RPC管理以及用户账户管理在用户空间实现为ksmbd.mounted。与性能相关的文件操作(如open/read/write/close等)在内核空间(ksmbd)中实现。这也使得所有文件操作更容易与VFS(虚拟文件系统)接口集成。

ksmbd (kernel daemon)

ksmbd(Kernel SMB Daemon)是Linux系统中的一个内核级守护进程,负责实现和管理SMB(Server Message Block)协议的文件共享功能,通常用于网络文件共享服务。它在内核层面运行,能够提供比传统用户空间SMB实现(如Samba)更高性能,因为它避免了用户空间和内核空间之间的频繁数据传输。具体的工作流程为:

  1. 启动和初始化。

    ksmbd初始化时会启动一个forker线程。该线程会监听来自客户端的SMB请求。ksmbd使用端口445,负责接收客户端的连接请求。

  2. 处理客户端连接

    每当有新客户端连接时,forker线程会接受连接,并为每个客户端请求创建一个新的线程。

  3. 处理命令的并行性

    ksmbd支持并行处理多个客户端请求。每当接收到一个新的SMB命令时,ksmbd会根据请求的类型决定是否将命令传递给用户空间的ksmbd.mountd进程。例如,DCE/RPC命令会被识别并通过用户空间处理。

    这种处理方式允许ksmbd动态决定哪些请求可以直接由内核处理,哪些需要通过用户空间处理。

  4. 利用Linux内核进行任务调度

    ksmbd将命令处理任务作为工作项(workitems)进行排队,并由ksmbd-io内核工作线程处理。这种方式使得能够动态增加或减少工作线程。当负载增加时,内核启动更多的工作线程;当负载降低时,则会销毁多余的工作线程。

  5. 命令的并行处理

    在与客户端建立连接后,ksmbd会为每个SMB命令串接一个独立的内核工作项,并将其排入ksmbd-io的工作队列。以实现并行处理多个客户端的请求。

CVE-2025-37778

环境配置

所需环境为:linux内核(v6.15-rc4版本为没有应用补丁的最新版),

编译内核

内核源码:

1
2
3
git clone https://github.com/torvalds/linux.git
cd linux
git checkout v6.15-rc4

内核增加SMB3 server support模块:

首先执行:

1
make menuconfig

依次选择File SystemNetwork File System,然后选择smb3 server以模块(按M)形式编译,而不是内核的一部分编译。最后Save配置,保存为.config。开始编译:

1
make bzImage -j$(nproc) 

直接用make -j$(nproc)也能够生成bzImage,但会编译出额外的东西。

编译成功后出现如下:

1
Kernel: arch/x86/boot/bzImage is ready

关注一下这俩文件:vmlinuxarch/x86/boot/bzImage。其中,vmlinux是未经压缩的完整内核映像 ,其包含所有内核代码,数据段,调试信息,所有内核符号;是一个ELF可执行文件 ,主要用于调试(如gdb vmlinux),不能直接被QEMU直接使用来引导系统。

bzImage(bootable zImage)是经过压缩处理、用于引导的内核镜像 。 可被QEMU直接用来引导系统。

接下来还需要编译模块:

1
make modules -j$(nproc)

内核模块会被放在./modules下,模块的扩展名通常为.ko

笔者认为到这儿已经可以了,因为.ko文件其实就可以通过insmod /path/to/module.ko进行加载,只不过缺少相关依赖会报错。

紧接着安装内核模块:

1
sudo make modules_install

这个命令会把编译好的内核模块安装到/lib/modules/$(kernel_version)中。这里的$(kernel_version)就是内核版本号,在此情况下就是/lib/modules/6.15.0-rc4

至此,我们有了内核镜像(bzImage)和安装好的内核模块目录(/lib/modules/6.15.0-rc4)。一个是为了qemu启动对应版本的linux内核,一个是为了在qemu模拟的系统中加载ksmbd模块。

构建文件系统

在linux流传着一句话:万物皆文件

对文件系统的概念笔者通过pwn.college接触到的,认为确实讲解清晰,实践做题过程中确实理解加深。

这里需要为我们的内核构建一个文件系统,一开始笔者使用的是 busybox,但有点太mini了。因为还得安装用户空间ksmbd守护进程。

所以,用debootstrap构建一个精简的文件系统吧:

1
sudo debootstrap --arch amd64 jammy .

这条命令是:选择架构和版本后,以当前目录为文件系统根目录进行构建。或许需要挂全局代理,笔者不挂全局代理发现不走代理。

构建完成后,先进入该文件系统进行一些简单的初始化操作。首先执行以下命令进入根文件系统:

1
sudo chroot . /bin/bash

然后设置root密码:

1
passwd root

我们需要将内核模块cp进该文件系统。 也就是将安装到宿主机的/lib/modules/6.15.0-rc4目录直接复制进我们创建好的文件系统。

1
2
mkdir ./lib/modules
sudo cp /lib/modules/6.15.0-rc4 ./lib/modules

最后用cpio打包成一个文件系统,供qemu使用:

1
sudo find . | sudo cpio -o -H newc > ../qemu-linux/rootfs.cpio

至此,我们有了一个文件系统,且这个文件系统可以自定义很多东西(通过chroot,借助宿主机内核自定义)。且这个文件系统已经安装好了内核模块。

QEMU模拟系统

bzImage为镜像,以打包的rootfs.cpio为根文件系统,用qemu进行模拟。

qemu启动该linux内核命令脚本:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/sh
qemu-system-x86_64 \
-m 2048M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet kaslr" \
-cpu kvm64,+smep \
-smp cores=2,threads=1 \
-nographic \
-s

进入后的网络配置(具体网卡号用ip address查看):

1
2
ip link set enp0s3 up
dhclient enp0s3

可以通过apt update来确定是否有网络

如果是bridge模式,那么就是这样的(宿主机为192.168.100.1/24):

1
2
ip addr add 192.168.100.2/24 dev enp0s3
ip link set enp0s3 up

宿主机的配置为:

1
2
3
sudo ip tuntap add tap0 mode tap
sudo ip addr add 192.168.100.1/24 dev tap0
sudo ip link set tap0 up

推荐使用tag,因为后面为了支持Kerberos认证。qemu的user模式端口转发不支持高级协议的数据包转发。

加载ksmbd模块

随后,可以加载ksmbd模块:

1
sudo modprobe ksmbd

最终成功加载:

1
2
3
4
5
6
root@dog:~# modprobe ksmbd
root@dog:~# lsmod
Module Size Used by
ksmbd 311296 0
cifs_arc4 12288 1 ksmbd
nls_ucs2_utils 8192 1 ksmbd

此刻,内核空间现在支持使用smb server了。现在需要一个用户空间的smb守护进程,即安装ksmbd-tools

启动ksmbd服务

建议在根文件系统里安装好ksmbd-tools,而不是qemu模拟的系统中。

具体需要安装的依赖有:apt-get install git make autoconf libtool pkg-config libkrb5-dev libglib2.0-dev libnl-3-dev libnl-genl-3-dev 。安装过程很简单:

1
2
3
4
5
6
git clone https://github.com/cifsd-team/ksmbd-tools.git
cd ksmbd-tools
./autogen.sh
./configure --with-rundir=/run
make
sudo make install

安装好了之后,配置ksmbd共享。其中配置文件ksmbd.conf是用于定义SMB共享的核心文件。可以参考/usr/local/etc/ksmbd/ksmbd.conf.example文件,需要配置一个ksmbd.conf文件。

1
cp /usr/local/etc/ksmbd/ksmbd.conf.example /usr/local/etc/ksmbd/ksmbd.conf

然后自定义里面的内容。后续再创建用户/密码。

1
ksmbd.adduser -a <USERNAME>

创建好之后,就是启动ksmdb用户空间守护进程:

1
2
root@dog:/home# sudo ksmbd.mountd
[ksmbd.mountd/384]: INFO: Started manager

安装一个smbclient进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
root@dog:~# apt-get install smbclient
root@dog:~# smbclient //localhost/example -U dog
Password for [WORKGROUP\dog]:
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Thu Jun 12 05:45:48 2025
.. D 0 Thu Jun 12 05:45:23 2025
hello.md A 16 Thu Jun 12 05:45:48 2025

0 blocks of size 4096. 0 blocks available
smb: \> get hello.md
getting file \hello.md of size 16 as hello.md (1.7 KiloBytes/sec) (average 1.7 KiloBytes/sec)

证明qemu模拟的系统成功启动了ksmbd服务,值得一提的是,验证服务是否启动成功直接查看进程,而不是用systemctl。因为这里并不是使用systemd启动的ksmbd

1
2
3
4
5
6
root@dog:~# ps aux | grep ksmbd
root 383 0.0 0.1 5736 2312 ? Ss 06:45 0:00 ksmbd.mountd -v
root 384 0.0 0.1 79776 3396 ? S 06:45 0:00 ksmbd.mountd -v
root 385 0.1 0.0 0 0 ? S 06:45 0:01 [ksmbd-lo]
root 436 0.1 0.0 0 0 ? S 06:51 0:00 [ksmbd-enp0s3]
root 1142 0.0 0.1 7012 2120 ttyS0 S+ 07:00 0:00 grep --color=auto ksmbd

上述状态显示:ksmbd.mountd正在运行并监听共享配置;内核层的ksmbd模块已经在处理来自lo(本地回环)和enp0s3(实际网卡)的SMB请求;

由于笔者只想用qemu模拟一个ksmbd Server,因此大部分的依赖和软件包都是在文件系统借助宿主机完成的。最终实际到qemu启动的系统中,只需要简单加载模块modprobe ksmdb和运行用户空间ksmbd。修改conf时也在模拟的根文件系统中完成。

倘若有需要,可申请一个虚拟磁盘给该系统,使得每次qemu启动该系统后做的操作由该虚拟磁盘保留,这样重启系统后状态就不会被重置。

关于原博客的简单分析

原博客链接:How I used o3 to find CVE-2025-37899, a remote zeroday vulnerability in the Linux kernel’s SMB implementation – Sean Heelan’s Blog

CVE-2025-37778是一个UAF漏洞。该问题发生在处理远程客户端发送的”session setup”请求时的Kerberos认证路径中。

如果krb5_authenticate检测到到会话状态是SMB2_SESSION_VALID,那么它就会释放sess->user。根据以下代码逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static int krb5_authenticate(struct ksmbd_work *work,
struct smb2_sess_setup_req *req,
struct smb2_sess_setup_rsp *rsp)
{
...
if (sess->state == SMB2_SESSION_VALID)
ksmbd_free_user(sess->user);

retval = ksmbd_krb5_authenticate(sess, in_blob, in_len,
out_blob, &out_len);
if (retval) {
ksmbd_debug(SMB, "krb5 authentication failed\n");
return -EINVAL;
}
...

这段代码认为当释放sess->user后,要么ksmbd_krb5_authenticate会将其重新初始化为一个新的有效值,要么不重新初始化时,返回-EINVAL后,sess->user不会再其他地方被使用。然而,事实证明这个假设是错误的。

可以强制ksmbd_krb5_authenticate不重新初始化sess->user,并且即使krb5_authenticate返回-EINVAL,依然可以继续访问sess->user

作者想办法用这个漏洞来验证LLM的能力,LLM需要理解并完成以下需求才能发现漏洞:

  • 想办法触发free操作以获得sess->state == SMB2_SESSION_VALID
  • 意识到在ksmbd_krb5_authenticate中存在不会重新初始化sess->user的路径,并思考如何触发这些路径
  • 意识到在sess->user被释放后,代码库的其他部分可能仍然可以访问它

此外,作者还公开了[o3](o3_finds_cve-2025-37899/o3_finds_CVE-2025-37778.txt at master · SeanHeelan/o3_finds_cve-2025-37899)和[claude3.7](o3_finds_cve-2025-37899/claude_3_7_finds_CVE-2025-37778.txt at master · SeanHeelan/o3_finds_cve-2025-37899)发现这个漏洞的分析报告。

漏洞分析与验证

这一切是发生在smb2_sess_setup函数中,前文提及它是在远程客户端发送”session setup”时触发的,然后需要触发krb5_authenticate()函数中的free操作,同时进入ksmbd_krb5_authenticate()后,触发goto out代码路径,从而避免sess->user的重新初始化操作。然后回到smb2_sess_setup()中的out_err路径,第1912行出现sess->user对已经释放的指针进行再次地使得,从而达成use after free

后续进一步分析分析源码….仔细看看调用链。尝试写个PoC试试。

Kerberos认证

Kerberos不过多介绍,因为漏洞涉及的其实并不多,主要需要使得用qemu搭建的ksmbd服务器支持Kerberos认证。

配置Kerberos环境

安装必要的软件包:

1
sudo apt install krb5-kdc krb5-admin-server krb5-config krb5-user

然后宿主机和ksmbd服务器(qemu模拟的)需要配置对应的域名解析。在各自的/etc/hosts中加上:

1
192.168.100.2 kdc.example.com

验证是否配置域名解析成功,执行以下命令应当返回对应的域名即解析成功。当然,域名解析成功并不代表两端互通,需要配置相应的tap管道作为qemu与宿主机的通信管道,因为qemu默认是无法访问宿主机网络的。

1
host 192.168.100.2

然后qemu启动时的脚本需要更改,利用tap虚拟网卡搭建一个通信管道。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/sh
sudo qemu-system-x86_64 \
-m 4096M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet kaslr" \
-cpu kvm64,+smep \
-smp cores=2,threads=1 \
-nographic \
-s \
-netdev tap,id=n1,ifname=tap0,script=no,downscript=no \
-device virtio-net-pci,netdev=n1 \
-rtc base=localtime,clock=host

配置kdc server

首先需要创建Kerberos 数据库

1
sudo kdb5_util create -s

这里需要提供口令,作为数据库的主密码。然后启动KDC服务:

1
systemctl start krb5-kdc krb5-admin-server

再创建管理员和测试用户:

1
2
sudo kadmin.local -q "addprinc admin/admin"		# 创建admin用户,权限为`admin`
sudo kadmin.local -q "addprinc dog" # 创建普通用户dog

继续创建smb服务主体:

1
sudo kadmin.local -q "addprinc -randkey cifs/kdc.example.com"

然后该服务主体需要导出到密钥中,这个密钥是服务端用于解密票据时用的:

1
sudo kadmin.local -q "ktadd cifs/kdc.example.com"

此时会默认生成一个/etc/krb5.keytab文件。

进一步在宿主机(客户端)配置/etc/krb5.conf文件,设置其使用kerberos认证时访问的kdc服务器以及其获取TGT的相关配置:

1
2
3
4
5
6
7
8
9
10
11
[libdefaults]
default_realm = EXAMPLE.COM

[realms]
EXAMPLE.COM = {
kdc = kdc.example.com
admin_server = kdc.example.com
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM

然后服务端的配置文件应当保持一致。最后为ksmbd支持kerberos作配置。通过官方文档ksmbd.confksmbd.conf文件的相关描述,可发现3个配置选项:

1
2
3
4
5
6
7
8
9
10
11
kerberos keytab file (G)
Path of the keytab file for the service principal. If no value is given, it is the default keytab resolved with krb5_kt_default(3).
Default: kerberos keytab file =

kerberos service name (G)
Service principal name. If no value is given, it is cifs/ followed by the FQDN resolved with getaddrinfo(3).
Default: kerberos service name =

kerberos support (G)
Support for Kerberos 5 authentication. For the parameter to take effect, ksmbd.mountd must be built against Kerberos 5.
Default: kerberos support = no

对其作相应配置如下:

1
2
3
kerberos keytab file = /etc/krb5.conf
kerberos service name = cifs/kdc.example.com
kerberos support = yes

然后再重启KSMBD

1
sudo systemctl restart ksmbd

由于笔者的ksmbd服务和kdc服务同为qemu模拟的ubuntu服务器,因此不需要额外再配置一台机器。

修改服务器主机名

由于我的qemu模拟的主机名为”dog”。而实际的SPN为kdc.example.com。当ksmbd收到客户端送来的票据(cifs/kdc.example.com@EXAMPLE.COM),它会依次执行以下操作:

  1. 检查本地keytab是否有该SPN;
    • 重要的是:它会默认使用本机的主机名(dog),构建成cifs/dog@EXAMPLE.COM
  2. 尝试从keytab中找cifs/dog@EXAMPLE.COM,找不到;
  3. 最终SPNEGO认证失败

修改服务器主机名为kdc.example.com

1
sudo hostnamectl set-hostname kdc.example.com

重新登陆shell:

1
exec bash

验证:

1
hostname -f

重启ksmbd

1
sudo systemctl restart ksmbd

测试

客户端执行kinit dog可获得对应的票据。然后使用smbclient -k //kdc.example.com/share即可通过kerberos认证访问该服务。但目前为止一直没认证通过,正常口令认证是可访问成功的。…暂时跳过这一部分内容。

所以这是一个求助帖,或许有人能告知我为何Kerberos认证不了~

除了这一点,或许有其他文章能够使得触发这类漏洞的一个前提条件:kmalloc()失败,To be continue…


CVE-2025-37899: let me try try
https://loboq1ng.github.io/2025/07/05/CVE-2025-37899-let-me-try-try/
作者
Lobo Q1ng
发布于
2025年7月5日
许可协议