smb4k:KAuth 助手存在主要漏洞 (CVE-2025-66002, CVE-2025-66003)
#CVE #KDE #D-Bus目录
- 1) 引言
- 2) 特权挂载助手概述
- 3)
Smb4KMountHelper::mount()中的问题 - 4)
Smb4KMountHelper::unmount()中的问题 - 5) 其他说明
- 6) 建议的修复
- 7) 上游错误修复
- 8) 可能的变通方案
- 9) 重现器
- 10) CVE 分配
- 11) 协调披露
- 12) 时间线
- 13) 参考文献
1) 引言
smb4k 是一个 KDE 桌面相关的实用程序,允许非特权挂载 Samba/CIFS 网络共享。SUSE 安全团队早在2017年就审查过其特权 KAuth 助手组件,这导致了 CVE-2017-8422 的发现(通用 KAuth 认证绕过)和 CVE-2017-8849(通过 smb4k 挂载助手进行本地 root 漏洞利用)。
今年9月,我们被要求重新考虑将 smb4k 包含在 openSUSE Tumbleweed 中。由此产生的审查表明,挂载助手仍然缺乏输入验证,受竞态条件影响,并且其现有验证逻辑中存在一个错误。这导致了本地攻击向量,允许拒绝服务甚至本地 root 漏洞利用。
许多 Linux 发行版以及一些 BSD 可能受到本报告中描述的问题的影响。我们向上游提供了协调披露,并充分利用了最长90天的非披露期,以获得解决所有问题的补丁。该补丁位于 commit 0dea60194a 中,它是 smb4k 4.0.5 错误修复版本的一部分。
以下部分简要概述了特权挂载助手。第3节探讨了助手挂载方法中发现的问题。第4节则探讨了助手卸载方法中发现的问题。第5节包含关于助手代码质量和安全隐患的进一步说明。第6节讨论了我们向上游建议的解决这些问题的修复方案。第7节详细介绍了上游最终实现的错误修复。第8节建议了可以应用于避免本报告中发现问题的可能变通方案。第9节提供了问题的重现器。
本报告基于 smb4k 4.0.4 版本。
2) 特权挂载助手概述
smb4k 中有问题的特权挂载助手组件相对较小,可以在文件 smb4kmounthelper.cpp 中找到。该助手以完全 root 权限运行,并实现了两个可通过 D-Bus 访问的 KAuth 操作:挂载和卸载网络共享。根据 Polkit yes 设置,这两个操作都允许本地用户在活动会话中无需认证即可执行。
3) Smb4KMountHelper::mount() 中的问题
3.1) 任意目标目录可用于挂载网络共享
助手对所需 Samba 共享的挂载目标目录没有任何限制。这意味着共享也可以挂载到 /bin 等位置。如果客户端可以控制网络共享的内容,则这允许通过在共享上放置精心制作的二进制文件(例如用于 /bin/bash),实现本地 root 漏洞利用,这些二进制文件在某个时候将被特权进程执行。
如果攻击者无法控制共享的内容,那么这可以作为本地拒绝服务攻击向量,因为重要的系统程序将变得不可访问。
为了解决这个问题,我们建议只允许将网络共享挂载到非特权用户无法控制的预定义位置。
3.2) 任意命令行参数可传递给 mount.cifs
客户端可以在 mh_options 参数中指定任意额外的命令行参数,这些参数将传递给 mount.cifs 程序。挂载助手构建的命令行如下所示
/sbin/mount.cifs <URL> <mountpoint> <options>...
所有这些参数,除了 mount.cifs 程序本身的路径,实际上都由客户端控制。这里调用的不是通用 mount 程序,否则客户端已经可以在系统中执行任意挂载。相反,攻击者受限于特殊用途的 mount.cifs 二进制文件所提供的功能。
mount.cifs 程序支持大量的挂载选项。调查每个选项的影响将超出本报告的范围。但是,存在一个简单的提权向量:向命令行传递 filemode=04777,uid=0 会导致网络共享挂载上的每个文件都获得 setuid-root 权限。如果网络共享的内容由攻击者控制,那么这可以很容易地用于将攻击者控制的 setuid-root 程序引入系统。即使修复了问题 3.1),这也可以实现本地 root 漏洞利用。
其他 mount.cifs 选项,如 port=<port>,可用于将内核指向由攻击者自己控制的 CIFS 服务器,该服务器侦听 localhost 上的非特权端口。这样,本地攻击者就可以提供必要的精心制作的网络共享,以执行本报告中描述的漏洞利用,而无需依赖外部网络资源。
为了解决这个问题,我们建议将 mh_options 限制为允许参数的白名单,并验证选项的值,以防它们包含有问题的设置。
3.3) 客户端可以控制传递给 mount.cifs 的 KRB5CCNAME 环境变量
客户端可以在 mh_krb5ticket 参数中提供任意路径;挂载助手将此路径放置到 KRB5CCNAME 环境变量中,供 mount.cifs 子进程使用。这是为了允许使用客户端的 Kerberos 凭据挂载网络共享。
客户端可以传递指向其通常无法访问的文件系统位置的路径。在多用户场景中,这可能允许,例如,通过传递指向其他用户凭据缓存的路径来劫持其他用户的 Kerberos 凭据。如果 mount.cifs 将文件内容输出到系统日志或标准错误(其输出通过 D-Bus 返回给客户端),这还可能导致 /etc/shadow 等文件的信息泄露。
此外,此路径可用于文件存在性测试或本地拒绝服务攻击(通过指向 /dev/zero 或命名 FIFO 管道等特殊文件)。
为了解决这个问题,我们建议不要传递路径,而是从客户端向助手传递一个已经打开的文件描述符,以避免以 root 权限打开任意文件。
4) Smb4KMountHelper::unmount() 中的问题
4.1) 挂载路径验证失败时缺少 return 语句
这与上面关于挂载的问题 3.1) 类似。在 smb4kmounthelper.cpp 第177行中,有一个 if 块,用于处理客户端提供的 mh_mountpoint 路径与 KMountPoint::currentMountPoints() 返回的任何可用 Samba 挂载不匹配的情况。
问题在于,这个 if 块只设置了错误消息,但实际上没有通过 return 终止函数执行。这意味着验证无效,并且本地用户尽管有检查,仍然可以卸载任意文件系统。
这是一个主要的本地拒绝服务攻击向量,可能导致系统完全中断。在某些特殊情况下,当文件系统位置因挂载其他文件系统而变得不可访问时,甚至可能导致信息泄露或权限提升(我们可以想象这种情况,例如在容器设置的上下文中)。
4.2) 任意命令行参数可传递给 umount
与上面的问题 3.2) 类似,特权助手将客户端在 mh_options 中提供的任意命令行参数转发到 umount 程序的命令行。这发生在 smb4kmounthelper.cpp 第187行。基本上,umount 程序将像这样被调用
/sbin/umount <options>... <mount-point>
假设问题 4.1) 已修复,<mount-point> 参数不能由客户端任意选择,而必须匹配现有的“cifs”、“smbfs”或“smb3”类型挂载路径。只要存在这样的挂载路径,客户端就可以传递任意额外的挂载点作为“选项”,这些挂载点也将被卸载。这是问题 4.1) 的一个较轻的版本,如果满足所述先决条件,会导致本地拒绝服务。
除此之外,umount 提供了各种选项,可以影响其操作方式。一个突出的选项是 -N --namespace ns,它会导致程序在任意挂载命名空间中卸载文件系统。这可能会影响特权进程、其他用户的容器或受限进程。
为了解决这个问题,我们建议将 mh_options 限制为允许参数的白名单。
4.3) 影响 KMountPoint::currentMountPoints() 的竞态条件
这并非 smb4k 本身的问题,而是实现 KMountPoint API 的 KIO 库中的问题。在我们的测试中,我们使用了该库的 v6.17.0 版本。
挂载助手的 umount() 函数尝试通过将客户端提供的输入路径与内核报告的系统中当前挂载进行比较来验证。只有活动的“cifs”、“smbfs”和“smb3”文件系统挂载才应该被卸载。为此,当前挂载文件系统列表从以下位置获取
KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded | KMountPoint::NeedMountOptions);
currentMountPoints() 的实现依赖于 libmount 库来检索挂载点列表。libmount 库提供了一种经过验证的实现,用于安全解析 /proc/self/mountinfo 等文件,我们几年前曾对其进行过审查并认为其稳健。然而,在安全地从 libmount 获取信息后,KIO 库在此基础上执行了一些操作,这可能导致安全相关问题。
一个小问题出现在 kmountpoint.cpp 第365行,其中在每个挂载条目的目标挂载目录上调用 stat()。这可能会访问不受信任的路径,也可能来自 FUSE 文件系统,在某些情况下,如果 stat() 阻塞,则可能导致本地拒绝服务。此外,在执行 stat() 调用时,预期的挂载点可能已被卸载,从而允许路径指向任意文件(也包括符号链接),这将导致 currentMountPoints() 返回的信息的 m_deviceID 字段中包含不正确的信息。
稍后,代码尝试在第382行“解析 GVFS 挂载点”。实现此逻辑的resolveGvfsMountPoints() 函数查找源设备名称为“gvfsd-fuse”的挂载条目。对于每个这些挂载点,该函数将列出挂载的目录内容,并查找形式为 <type>:<label> 的目录条目,其中 type 指的是预期在该处找到的文件系统类型。然后,该函数根据此信息合成额外的挂载条目,这些条目将返回给调用者,显示为功能齐全的常规挂载。
这有两个问题。首先,这些操作都受竞态条件的影响;挂载表条目可以随时更改。其次,Linux 系统中非特权用户创建具有任意源设备名称的挂载点存在一种常见方式。这就是 fusermount setuid-root 工具,用于挂载 FUSE 文件系统。本地用户可以像这样创建一个假的 gvfsd-fuse 挂载点
$ export _FUSE_COMMFD=0
$ mkdir $HOME/mnt
$ fusermount $HOME/mnt -ononempty,fsname=gvfsd-fuse
$ mount | tail -n1
gvfsd-fuse on /home/$USER/mnt type fuse (rw,nosuid,nodev,relatime,user_id=1000,group_id=100)
默认的 FUSE 配置阻止 root 用户访问非 root 控制的 FUSE 文件系统。为了克服这个限制,攻击者可以执行以下步骤
- 创建一个如上所示的伪造
gvfsd-fuse挂载。 - 触发 smb4k 挂载助手中的
unmount()逻辑。 - 在
currentMountPoints()从 libmount 获取挂载信息之后,但在调用resolveGvfsMountPoints()之前,尝试卸载$HOME/mnt。 - 在此位置放置符合预期格式的目录,例如
cifs:mymount。当然,这些目录可以提前放置在那里。 - 成功时,
currentMountPoints()函数将向挂载助手返回一个合成条目,其中列出非特权用户$HOME/mnt/cifs:mymount中的 CIFS 挂载。
使用这种方法,即使修复了问题4.1)和4.2),也可以绕过挂载助手 unmount() 函数中的验证步骤。
KMountPoint 逻辑中还存在其他潜在问题,例如在 finalizeCurrentMountPoint() 中,如果调用者传递 KMountPoint::NeedRealDeviceName 标志,则会解析源设备名称。这为具有伪造源设备名称的非特权 FUSE 挂载提供了另一个影响结果的机会,例如执行文件存在性测试或以其他方式欺骗 KIO 库的调用者。
由于这些问题,从 currentMountPoints() 获取的信息目前不能用于作为安全相关决策的基础。通常,root 根本不应该执行这些额外的查询。该库可以检查 geteuid() == 0,以防止在特权上下文中执行此危险逻辑。
对于非特权应用程序,我们可以想象添加一个标志,如 KMountPoint::AllowUnsafe,它选择加入有问题的行为。只有了解潜在问题的应用程序才会传递此标志。
当我们报告此问题时,KDE 安全部门最初表示本节中描述的问题只会影响 smb4k,而不会影响 KMountPoint API 的其他用户。我们认为,仅根据一个库所谓的当前用户来判断其 API 是否安全是值得怀疑的。除此之外,即使使用此 API 的非特权进程也可能成为系统中其他用户精心制作 gvfsd 挂载信息的受害者。有人可能会争辩说,fusermount 工具本身就存在问题。然而,KMountPoint API 明确处理基于 FUSE 的文件系统,因此它应该准备好处理由此带来的特殊性。
当我们向 KDE 安全团队指出我们持续的担忧时,他们建议我们创建一个上游问题,或者理想情况下,我们自己提供一个错误修复。虽然我们乐于尽力提供帮助,但手头的问题是一个更大的 API 设计主题,我们认为负责任的上游开发人员应该仔细处理,这也让他们可以从这次经验中学习。因此,我们只创建了上游问题,正如他们建议的那样。
4.4) 任意网络共享挂载可被卸载
即使本节中讨论的所有其他问题都已修复,当前的挂载助手代码仍然允许卸载任意 Samba 共享,无论它们最初是由 smb4k 本身(针对同一用户或不同用户)挂载的,还是由系统中的其他组件(例如通过 /etc/fstab 中的固定条目)挂载的。
与上面的问题 3.1) 类似,我们建议将 smb4k 挂载限制在非特权用户无法控制的预定义位置,以解决此问题。
5) 其他说明
5.1) 多余的 mh_command 客户端参数
这两个助手操作都将客户端在 mh_command 中提供的任意路径与从 findMountExecutable() 或 findUmountExecutable() 返回的受信任的“mount”或“unmount”程序路径进行比较。这很奇怪。看起来这个比较是尝试修复 CVE-2017-8849 的遗留产物。这是多余的逻辑,不必要地增加了客户端和助手的复杂性,充其量只会引起混淆。
助手应该自行选择受信任的挂载程序,并完全停止考虑 mh_command 参数。
5.2) 冗余的“在线检查”代码
在 smb4kmounthelper.cpp 第38行 和 第205行 存在对在线网络接口的冗余检查。这段代码应该放在一个单独的函数中,以避免代码重复并提高可读性。
这个在线检查也是高度启发式的,非特权用户可能通过创建看起来在线的非特权伪网络设备来影响其结果。
5.3) mount.cifs 和 umount 遵循符号链接
Both mount.cifs 和 umount 都遵循路径参数中的符号链接。这意味着即使挂载助手试图验证指向客户端控制位置的路径,在实际的 mount.cifs 或 umount 实用程序运行之时,这可能已经被符号链接替换,并且挂载逻辑将操作在一个与助手预期完全不同的位置。
6) 建议的修复
除了上面问题背景中提到的个别建议之外,我们认为所发现问题的范围和严重性表明,需要对挂载助手实用程序进行重大重新设计,以可靠地解决所有问题。
以下是一些关于更大范围重新设计的建议
- 助手根本不应该允许挂载或卸载用户提供的路径。应该为此目的使用一个专用的目录,例如
/mounts/smb4k,并且只由root控制。需要某种形式的跟踪哪个挂载属于哪个用户(例如,将挂载的所有权赋予请求它的客户端)。这更像是 udisks 解决用户请求挂载设备的问题。 - 将任意参数从非特权客户端传递给
mount.cifs或umount无法安全工作。一个更抽象的接口,具有明确定义的挂载或卸载设置,将有助于限制客户端的自由度。这还将改善助手接口与具体实现的解耦,这样助手就可以例如更改实现以直接调用mount()和umount()系统调用,而不是通过挂载实用程序。
7) 上游错误修复
在一个月的时间里,我们与 smb4k 上游开发者讨论了各种版本的补丁,最终在90天最长禁运期结束后,及时找到了一个可行的补丁,以便发布本报告。错误修复的主要方面如下:
- 对于
mount和unmount,客户端传递的选项现在经过更严格的审查,并且只允许白名单中的设置。 filemode挂载选项虽然仍然基本受支持,但现在会进行检查以确保不存在特殊文件位。uid和gid挂载选项现在只能设置为调用者的 UID/GID,而不能再设置为任意 ID。- 网络共享挂载现在被限制在一个以
/run/smb4k为根的目录层次结构中。这样,非特权用户就不能再在挂载目标路径中放置符号链接了。挂载被放置在每个 UID 的子目录中,这样不同的客户端就不能再互相影响彼此的挂载。 - 为了传递 Kerberos 凭据,客户端现在将已打开的文件描述符传递给挂载助手,从而避免了在不受信任的路径上操作的任何问题。
- 有问题的
KMountPointAPI 不再使用,已被 Qt 的QStorageInfoAPI 取代。形式上,有了受信任的挂载树位置,根本不再需要调查现有的挂载点,但上游开发者目前仍倾向于保留这个额外的验证步骤。
我们感谢 smb4k 上游开发者 Alexander Reinholdt 与我们合作,并及时完成了补丁,以便发布。通过这种方式,smb4k 中一系列长期存在的问题最终得以解决。
8) 可能的变通方案
如果上游错误修复不能立即使用,可以考虑以下建议来消除本报告中描述的攻击面
- 将挂载和卸载助手操作的 Polkit 认证要求提高到
auth_admin。这样,有问题的逻辑只能由已经具有特权的用户访问。然而,这与 smb4k 允许非特权挂载和卸载网络共享的初衷相悖。 - 将 D-Bus 对挂载助手实用程序的访问权限限制为
smb4k等可选组成员。结合安全免责声明,这将允许真正想使用此功能的用户选择加入。
9) 重现器
KAuth D-Bus 接口不能通过 gdbus 等工具轻松调用,因为它需要序列化的 QVariantMap 作为输入。我们提供两个 C++ 程序,可用于对 smb4k 的挂载助手 API 进行独立测试,以重现本报告中描述的攻击向量:smb4k_mount.cpp 和 smb4k_unmount.cpp。重现器的源代码中有注释解释了如何编译和使用它们。
10) CVE 分配
形式上,本报告中的发现可以证明大量的 CVE 合理,但我们决定将其浓缩为由这些问题产生的两个主要方面
- CVE-2025-66002:由于缺少输入验证,本地用户可以通过 smb4k 挂载助手执行任意卸载。
- CVE-2025-66003:如果本地用户可以访问并控制 Samba 网络共享的内容,则可以通过 smb4k 挂载助手执行本地 root 漏洞利用。
当向上游提供的最长90天非披露期结束时,由于 KDE 安全团队缺乏反馈,我们按照最初向上游建议的方式分配了这些 CVE。
11) 协调披露
我们在9月11日联系了 KDE 安全团队,分享了本报告中描述的详细问题,并提供了协调披露。在最长90天的非披露期前近两个月,我们很难从 KDE 安全团队那里获得关于预期发布日期、他们是否认可这些发现,甚至他们是否希望进行协调披露的明确答复。
直到11月初,当 smb4k 上游开发者加入讨论并开始开发错误修复时,我们才看到一些明显的进展。然而,由于开发者资源有限,进展仍然缓慢。尽管如此,从那时起,讨论变得有帮助和合作,我们终于看到非披露时间确实得到了利用。在最长90天的禁运期即将到期前不到一周,我们成功地就解决所有问题的错误修复达成了一致。
总而言之,我们对本案例中协调披露的进展并不完全满意。我们认为 KDE 安全团队不愿沟通,也不愿协助协调披露。我们相信,通过向用户建议变通方案并在社区的帮助下公开开发错误修复,可以更快地解决问题。
12) 时间线
| 2025-09-11 | 我们将报告转发给 security@kde.org,提供协调披露。 |
| 2025-09-17 | 我们收到了 KDE 安全团队的确认函。 |
| 2025-09-29 | 由于没有收到上游的任何其他消息,我们至少要求确认报告中描述的问题,并正式决定是否需要协调披露。我们要求在10月2日之前得到反馈,否则我们将在我们这边发布信息。 |
| 2025-10-01 | 我们收到了 KDE 安全部门的回复,称他们正在处理该问题,但没有回答我们的问题。我们再次回复并试图澄清,我们无意给上游施加时间压力,但希望明确建立协调披露流程。 |
| 2025-10-02 | 我们得到了简短的回复,称他们无法给出预期的发布日期,并再次重复他们正在处理该问题。我们关于流程的问题仍然没有得到回答。我们再次解释说,我们希望参与审查潜在的错误修复,如果可以,我们将提供帮助,并且我们希望避免在没有任何明显进展的情况下浪费非披露时间。 |
| 2025-10-07 | KDE 安全团队告知我们,修复工作正在进行中,但没有提供进一步的细节。 |
| 2025-11-07 | smb4k 开发者 Alexander Reinholdt 直接联系了我们,分享了第一批建议的错误修复。 |
| 2025-11-12 | 我们对补丁中与安全相关部分提供了详细反馈,指出了仍然存在的问题以及新引入的问题。 |
| 2025-11-12 | KDE 安全团队就KMountPoint主题发表了意见,表示 smb4k 将是唯一使用此 API 的特权组件。 |
| 2025-11-13 | 我们回复了 KDE 安全团队,更详细地解释了我们对 KMountPoint API 的剩余担忧。 |
| 2025-11-16 | smb4k 开发者感谢我们对补丁的审查,并详细回复了我们的意见。他告诉我们他将着手开发后续补丁集。 |
| 2025-11-26 | smb4k 开发者告知我们,他需要更多时间才能提供改进版的补丁。 |
| 2025-11-26 | 我们感谢开发者持续的努力,但也提醒所有参与者,我们提供的最长90天非披露期即将于两周内结束。我们建议,如果在剩余时间内无法完成完整的错误修复,则可以发布临时变通方案(例如提高身份验证要求)。我们还建议此时联系 distros 邮件列表,以便其他 Linux 和 BSD 发行版在报告正式发布之前做好准备。 |
| 2025-11-27 | 关于 KMountPoint API,KDE 安全团队澄清说,他们理想情况下希望我们提供一个解决我们担忧的合并请求。 |
| 2025-11-28 | 我们按照最初向上游建议的方式分配了CVE,以将其作为附加信息提供给 distros 邮件列表。我们还将 CVE 分享给了上游。 |
| 2025-11-30 | 上游开发者与我们分享了一套改进的补丁。 |
| 2025-12-01 | 我们向上传开发者发送了新一轮的评论。新的补丁在许多方面仍然不足。 |
| 2025-12-01 | 我们将本报告草稿转发到 distros 邮件列表,宣布将于2025年12月10日发布问题。我们指出,此时没有可供分享的适当错误修复。 |
| 2025-12-03 | 我们收到了上游开发者建议补丁的又一个版本。 |
| 2025-12-04 | 这次我们没有发现任何剩余的安全问题,同意了补丁,但仍就一些质量和风格方面发表了评论。 |
| 2025-12-04 | 我们将上游开发者的错误修复转发到了 distros 邮件列表。 |
| 2025-12-05 | 我们要求上游开发者于2025年12月10日发布一个错误修复版本,他同意了。 |
| 2025-12-10 | 上游按计划发布了错误修复版本 4.0.5。 |
| 2025-12-10 | 本报告发布。 |