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)更高性能,因为它避免了用户空间和内核空间之间的频繁数据传输。具体的工作流程为:
启动和初始化。
当
ksmbd初始化时会启动一个forker线程。该线程会监听来自客户端的SMB请求。ksmbd使用端口445,负责接收客户端的连接请求。处理客户端连接
每当有新客户端连接时,
forker线程会接受连接,并为每个客户端请求创建一个新的线程。处理命令的并行性
ksmbd支持并行处理多个客户端请求。每当接收到一个新的SMB命令时,ksmbd会根据请求的类型决定是否将命令传递给用户空间的ksmbd.mountd进程。例如,DCE/RPC命令会被识别并通过用户空间处理。这种处理方式允许
ksmbd动态决定哪些请求可以直接由内核处理,哪些需要通过用户空间处理。利用Linux内核进行任务调度
ksmbd将命令处理任务作为工作项(workitems)进行排队,并由ksmbd-io内核工作线程处理。这种方式使得能够动态增加或减少工作线程。当负载增加时,内核启动更多的工作线程;当负载降低时,则会销毁多余的工作线程。命令的并行处理
在与客户端建立连接后,
ksmbd会为每个SMB命令串接一个独立的内核工作项,并将其排入ksmbd-io的工作队列。以实现并行处理多个客户端的请求。
CVE-2025-37778
环境配置
所需环境为:linux内核(v6.15-rc4版本为没有应用补丁的最新版),
编译内核
内核源码:
1 | |
内核增加SMB3 server support模块:
首先执行:
1 | |
依次选择File System,Network File System,然后选择smb3 server以模块(按M)形式编译,而不是内核的一部分编译。最后Save配置,保存为.config。开始编译:
1 | |
直接用
make -j$(nproc)也能够生成bzImage,但会编译出额外的东西。
编译成功后出现如下:
1 | |
关注一下这俩文件:vmlinux和arch/x86/boot/bzImage。其中,vmlinux是未经压缩的完整内核映像 ,其包含所有内核代码,数据段,调试信息,所有内核符号;是一个ELF可执行文件 ,主要用于调试(如gdb vmlinux),不能直接被QEMU直接使用来引导系统。
而bzImage(bootable zImage)是经过压缩处理、用于引导的内核镜像 。 可被QEMU直接用来引导系统。
接下来还需要编译模块:
1 | |
内核模块会被放在./modules下,模块的扩展名通常为.ko。
笔者认为到这儿已经可以了,因为
.ko文件其实就可以通过insmod /path/to/module.ko进行加载,只不过缺少相关依赖会报错。
紧接着安装内核模块:
1 | |
这个命令会把编译好的内核模块安装到/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 | |
这条命令是:选择架构和版本后,以当前目录为文件系统根目录进行构建。或许需要挂全局代理,笔者不挂全局代理发现不走代理。
构建完成后,先进入该文件系统进行一些简单的初始化操作。首先执行以下命令进入根文件系统:
1 | |
然后设置root密码:
1 | |
我们需要将内核模块cp进该文件系统。 也就是将安装到宿主机的/lib/modules/6.15.0-rc4目录直接复制进我们创建好的文件系统。
1 | |
最后用cpio打包成一个文件系统,供qemu使用:
1 | |
至此,我们有了一个文件系统,且这个文件系统可以自定义很多东西(通过chroot,借助宿主机内核自定义)。且这个文件系统已经安装好了内核模块。
QEMU模拟系统
以bzImage为镜像,以打包的rootfs.cpio为根文件系统,用qemu进行模拟。
qemu启动该linux内核命令脚本:
1 | |
进入后的网络配置(具体网卡号用ip address查看):
1 | |
可以通过
apt update来确定是否有网络
如果是bridge模式,那么就是这样的(宿主机为192.168.100.1/24):
1 | |
宿主机的配置为:
1 | |
推荐使用tag,因为后面为了支持Kerberos认证。qemu的user模式端口转发不支持高级协议的数据包转发。
加载ksmbd模块
随后,可以加载ksmbd模块:
1 | |
最终成功加载:
1 | |
此刻,内核空间现在支持使用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 | |
安装好了之后,配置ksmbd共享。其中配置文件ksmbd.conf是用于定义SMB共享的核心文件。可以参考/usr/local/etc/ksmbd/ksmbd.conf.example文件,需要配置一个ksmbd.conf文件。
1 | |
然后自定义里面的内容。后续再创建用户/密码。
1 | |
创建好之后,就是启动ksmdb用户空间守护进程:
1 | |
安装一个smbclient进行测试:
1 | |
证明qemu模拟的系统成功启动了ksmbd服务,值得一提的是,验证服务是否启动成功直接查看进程,而不是用systemctl。因为这里并不是使用systemd启动的ksmbd:
1 | |
上述状态显示:
ksmbd.mountd正在运行并监听共享配置;内核层的ksmbd模块已经在处理来自lo(本地回环)和enp0s3(实际网卡)的SMB请求;
由于笔者只想用qemu模拟一个ksmbd Server,因此大部分的依赖和软件包都是在文件系统借助宿主机完成的。最终实际到qemu启动的系统中,只需要简单加载模块modprobe ksmdb和运行用户空间ksmbd。修改conf时也在模拟的根文件系统中完成。
倘若有需要,可申请一个虚拟磁盘给该系统,使得每次qemu启动该系统后做的操作由该虚拟磁盘保留,这样重启系统后状态就不会被重置。
关于原博客的简单分析
CVE-2025-37778是一个UAF漏洞。该问题发生在处理远程客户端发送的”session setup”请求时的Kerberos认证路径中。
如果krb5_authenticate检测到到会话状态是SMB2_SESSION_VALID,那么它就会释放sess->user。根据以下代码逻辑:
1 | |
这段代码认为当释放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 | |
然后宿主机和ksmbd服务器(qemu模拟的)需要配置对应的域名解析。在各自的/etc/hosts中加上:
1 | |
验证是否配置域名解析成功,执行以下命令应当返回对应的域名即解析成功。当然,域名解析成功并不代表两端互通,需要配置相应的tap管道作为qemu与宿主机的通信管道,因为qemu默认是无法访问宿主机网络的。
1 | |
然后qemu启动时的脚本需要更改,利用tap虚拟网卡搭建一个通信管道。
1 | |
配置kdc server
首先需要创建Kerberos 数据库
1 | |
这里需要提供口令,作为数据库的主密码。然后启动KDC服务:
1 | |
再创建管理员和测试用户:
1 | |
继续创建smb服务主体:
1 | |
然后该服务主体需要导出到密钥中,这个密钥是服务端用于解密票据时用的:
1 | |
此时会默认生成一个/etc/krb5.keytab文件。
进一步在宿主机(客户端)配置/etc/krb5.conf文件,设置其使用kerberos认证时访问的kdc服务器以及其获取TGT的相关配置:
1 | |
然后服务端的配置文件应当保持一致。最后为ksmbd支持kerberos作配置。通过官方文档ksmbd.conf对ksmbd.conf文件的相关描述,可发现3个配置选项:
1 | |
对其作相应配置如下:
1 | |
然后再重启KSMBD:
1 | |
由于笔者的ksmbd服务和kdc服务同为qemu模拟的ubuntu服务器,因此不需要额外再配置一台机器。
修改服务器主机名
由于我的qemu模拟的主机名为”dog”。而实际的SPN为kdc.example.com。当ksmbd收到客户端送来的票据(cifs/kdc.example.com@EXAMPLE.COM),它会依次执行以下操作:
- 检查本地keytab是否有该SPN;
- 重要的是:它会默认使用本机的主机名(
dog),构建成cifs/dog@EXAMPLE.COM
- 重要的是:它会默认使用本机的主机名(
- 尝试从keytab中找
cifs/dog@EXAMPLE.COM,找不到; - 最终SPNEGO认证失败
修改服务器主机名为kdc.example.com:
1 | |
重新登陆shell:
1 | |
验证:
1 | |
重启ksmbd:
1 | |
测试
客户端执行kinit dog可获得对应的票据。然后使用smbclient -k //kdc.example.com/share即可通过kerberos认证访问该服务。但目前为止一直没认证通过,正常口令认证是可访问成功的。…暂时跳过这一部分内容。
所以这是一个求助帖,或许有人能告知我为何Kerberos认证不了~
除了这一点,或许有其他文章能够使得触发这类漏洞的一个前提条件:
kmalloc()失败,To be continue…