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

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

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

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-8933 — C proof-of-concept for CVE-2026-8933, a snap-confine local privilege escalation using cgroup freezing, symlink races, and udev rule injection to execute commands as root. | Kitploit
工具/GitHubGitHub/yifengchen-cc/cve-2026-8933
Privilege EscalationContainer SecurityVulnerability AnalysisExploitation
GitHubyifengchen-cc/cve-2026-8933

CVE-2026-8933

C proof-of-concept for CVE-2026-8933, a snap-confine local privilege escalation using cgroup freezing, symlink races, and udev rule injection to execute commands as root.

查看仓库
14天前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2026-8933 — snap-confine set-capabilities 本地提权 PoC

复现 Qualys 公告《Local Privilege Escalation in set-capabilities versions of snap-confine》(https://cdn2.qualys.com/advisory/2026/07/21/snap-confine-set-capabilities.txt)。

在本机(Ubuntu 25.10 / snapd 2.73+ubuntu25.10.1 / 内核 6.17)实测:从 uid 1000 出发,最终由 systemd-udevd 以 root 执行命令,/tmp/pwned 得到 uid=0(root) gid=0(root) groups=0(root),与公告最后演示的效果一致。

复现过程中的踩坑记录、以及第一次评估为什么误判"不可利用",见 NOTES.md。

文件

文件说明
poc.c完整 PoC(单文件)
Makefile构建(链接系统已安装的 libfuse3.so.3.17.4)
include/libfuse3 3.17.4 头文件(匹配系统运行时库)
NOTES.md踩坑记录 + 可达性分析方法论复盘

漏洞原理

cmd/snap-confine/mount-support.c(snapd 2.73):

sc_bootstrap_mount_namespace()      // 第 475 行起
  479:  mkdtemp("/tmp/snap.rootfs_XXXXXX")          <- 目录先归调用者所有
  491:  sc_do_mount(scratch, scratch, NULL, MS_BIND)
  498:  sc_do_mount("none", scratch, NULL, MS_UNBINDABLE)
  503:  sc_do_mount("none", scratch, "tmpfs", ...)
  504:  sc_replicate_base_rootfs(scratch, rootfs_dir, mounts)

sc_replicate_base_rootfs()
  330:  chown(scratch, 0, 0)
  418:  fd = open(full_path, O_CREAT | O_TRUNC, 0644)   <- 缺少 O_NOFOLLOW
  422:  fchown(fd, 0, 0)

行号取自本机 snapd 2.73 的源码(cmd/snap-confine/mount-support.c)。Qualys 公告引用的是它自己那份文件副本,对应的行号是 511 / 523 / 530 / 535 / 536 / 362 / 450 / 454 —— 语句一一对应,只是行号不同。

本机的 /usr/lib/snapd/snap-confine 是 mode=755 + file capabilities(不是 setuid-root),因此它以攻击者的 euid 运行、却持有近 root 的能力:它创建的目录和 文件在一小段时间内属于攻击者,而第 418 行的 open() 会跟随符号链接。

反汇编确认:本机 open@plt 的标志位是 0x240(O_CREAT|O_TRUNC,无 O_NOFOLLOW);已修复的 2.77.1 同一点为 0x20240。

PoC 的六个步骤

  1. 把 snap-confine 放进自己的 cgroup:systemd-run --user --scope --unit=snap.<snap>.<app>。该 scope 属于当前用户,cgroup.freeze 可写, 于是可以逐个系统调用地放行 snap-confine。
  2. 在正确的位置冻结:SNAPD_DEBUG=1 时 snap-confine 会打印 associated snap application process ... with device cgroup。该行出现在 设备 cgroup 的 BPF 程序 attach 之后、mkdtemp() 之前。不能更早冻结: BPF 校验器在存在 freeze trap 时返回 EAGAIN,snap-confine 会直接退出。 stderr 接的是一个 pty(不是管道):否则 stdio 会块缓冲,marker 往往等到 mkdtemp() 和 bind mount 之后才被冲出来。 另外 scope 以 CPUQuota=1% 启动:从 marker 到 mkdtemp() 只有几百微秒的 CPU 时间,与抓取进程的唤醒延迟同量级;限流把这段窗口在真实时间里拉长到 毫秒级,冻结确认后立刻把 cpu.max 恢复到 100%。
  3. 把 scratch 目录换成指向我们自己目录的符号链接:mount(2) 会跟随目标 路径里的符号链接,于是 snap-confine 的 bind/unbindable/tmpfs 全都落在 <scratch>.dA 上,而复制循环仍然使用 scratch 目录这个字符串。
  4. 等 tmpfs 挂好(ns_mount_count(sc, .dA) >= 2)后,把符号链接改指 <scratch>.dB,并在 dB 里放进与 base rootfs 顶层普通文件同名的符号链接, 指向 /run/udev/rules.d/97-cve-2026-8933-<pid>-<rand>.rules。此时复制循环 的路径解析落到 dB —— 一个我们能完全控制的普通目录。tmpfs 被留在 dA,不再 妨碍我们。 注意:.dA 上只有 1 个挂载时是 bind mount(/tmp 本身就是 tmpfs,bind 后 fstype 仍是 tmpfs),必须等到第 2 个挂载(真正的 tmpfs)出现才能改指,否则 snap-confine 的 mount --make-unbindable 会因为路径不再是挂载根而 EINVAL。
  5. 赢得 open()/fchown() 之间的窗口:复制循环对第一个普通文件执行 open(O_CREAT|O_TRUNC)(跟随符号链接,以 root 身份在 /run/udev/rules.d 下创建文件),随后 fchown(fd,0,0)。文件在这一瞬间仍属于我们。PoC 同时用 两种手段:
    • 主路径:继续用 freezer 每次只放行约一个系统调用,一旦 stat() 看到文件 且 uid 仍是自己,立刻 chmod 0666 + 写入 udev 规则(此时 snap-confine 被冻结,窗口被"撑开");
    • 兜底:若干 spinner 进程以 chmod() 热循环抢占同一个窗口。spinner 抢到 之后文件是 root 所有但模式为 0666 —— 模式位在 fchown(0,0) 之后依然 保留,所以主路径同样能把它写完(代码里判断的是"能不能写",不是"是不是 属主")。
  6. 触发 udev:snap-confine 的 AppArmor profile 允许 /run/udev/** rw。 挂载/卸载一个 FUSE 文件系统会让内核为 bdi 子系统发出 add uevent, systemd-udevd 随即以 root 执行规则里的 PROGRAM=。

用法

make
./poc --diagnose            # 只读自检,不改动任何系统状态
./poc                       #  exploit,默认最多 5 次尝试
./poc --attempts 8          # 增加尝试次数
./poc --file default256.png # 只用 base rootfs 里的这一个普通文件
sudo ./poc --cleanup        # 清理(仅在异常中断留下残留时需要)

参数:

参数说明
--snap NAME用哪个 snap 的 rootfs(默认 firefox,也可用环境变量 CVE8933_SNAP)
--app NAME该 snap 的 app(默认取 meta/snap.yaml 里 apps: 下的第一个)
--file NAME只用 base rootfs 里的这一个普通文件;没有匹配就报错退出
--attempts N尝试次数(默认 5)
--step US / --step2 US两个阶段的每片 CPU 时间(微秒),调试用
--keep保留 <scratch>.dA / .dB 目录和符号链接(调试用)
--diagnose只读自检
--cleanup清理历史残留(需要 root)

日志写入 /tmp/cve8933.log,snap-confine 的调试输出写入 /tmp/cve8933-debug.txt。

CVE8933_PAYLOAD_CMD='<shell 命令>' 可以替换 payload 主体(用于演示执行其它 root 命令),此时不会自动清理;自定义命令仍然要写 /tmp/pwned,否则 PoC 会 认为这次没有成功。

实测结果

$ ./poc
[+] WIN: uid=0(root) gid=0(root) groups=0(root)
[+] exploit succeeded, evidence in /tmp/pwned

/tmp/cve8933.log 中对应的一次成功过程(2026-10-23,改动后复测):

[    0.256] === attempt 1/5 ===
[ 1062.158] marker seen after 750.5 ms
[ 1067.530] scratch dir /tmp/snap.rootfs_oFBraI replaced by a symlink to ...dA
[ 1100.887] bind mount + tmpfs on ...dA after 417 slices
[ 1100.974] scratch directory now points to ...dB with 4 symlinks -> ...rules
[ 1108.782] took the file over while snap-confine was stopped (242 slices)
[ 1108.820] rule written after 797.1 ms
[ 1108.832] target: uid=0 gid=0 mode=0666 size=73 content="ACTION=="add", ..."

(早先一次 8 次尝试的运行是第 2 次才成功:第 1 次的冻结确认太晚,snap-confine 在同一个时间片里跑过了 bind mount。)

/run/udev/rules.d/ 中曾短暂出现:

-rw-rw-rw- 1 root root 73 ... 97-cve-2026-8933-85431-485b.rules

内容为 ACTION=="add", SUBSYSTEM=="bdi", PROGRAM="/tmp/cve-2026-8933-payload.sh", udevd 日志确认已加载("marked world-writable ... Proceeding anyway"),payload 以 root 执行。

关于成功率

第 5 步的窗口(open() 与 fchown() 之间)只有一两个系统调用宽。主路径 (freezer 单步 + 冻结时接管)在多数尝试中成功;偶尔冻结确认得晚,snap-confine 会在同一时间片内跑过 bind mount,该次尝试直接失败并重试。默认 5 次尝试; 实测连续三次运行分别在 2、1、2 次尝试内成功。把 --attempts 调大即可。

清理与复原

正常运行时 PoC 自清理:payload 以 root 身份卸载 snap-confine 传播出来的所有 /tmp/snap.rootfs_* 挂载、删除这些目录、删除 /run/udev/rules.d/97-cve-2026-8933*.rules 和两个脚本。测试前先记录基线,跑完逐项核对,实测每次成功运行后:

mounts under /tmp/snap.rootfs_*: 0
leftover directories:            0
/run/udev/rules.d:               只有 90-netplan.rules / 99-netplan-ens33.rules
/etc/passwd md5:                 24c3aa8f7b7bdf6fa770b46601abc1a0(未改变)
user scopes:                     0

唯一有意保留的是 /tmp/pwned(root 所有,即公告演示中的证据文件)。删除它需要 root:sudo ./poc --cleanup。

如果多次尝试全部失败(例如系统负载很高),可能残留若干 /tmp/snap.rootfs_* 挂载和目录;sudo ./poc --cleanup 会一并清掉。

安全说明

本 PoC 仅用于漏洞研究与验证:

  • payload 默认只执行 id > /tmp/pwned 并自清理,不修改任何凭据文件 (/etc/passwd、/etc/shadow 均未触碰,md5 可核对);
  • 不改动主机名、网络、proxy,不安装/卸载软件包,不重启;
  • 每次运行都会自动卸载自身产生的挂载并删除临时文件。
下载工具