net/sched act_pedit 部分写时复制 (partial-COW) 页缓存破坏 (罪魁祸首提交 899ee91156e5,存在于 v5.18 .. 在 v7.1-rc7 中修复)。packet_edit_meme.c 将其转化为无特权的本地提权:一个拥有 userns CAP_NET_ADMIN 的子进程覆盖了 setuid-root 二进制文件的已缓存 ELF 元数据,替换为 setgid(0)+setuid(0)+execve("/bin/sh") shellcode。
make
./packet_edit_meme
./packet_edit_meme --ubuntu # AppArmor 保护的 Ubuntu:先绕过 aa-exec
| 发行版 | 内核版本 | 选项 (Flag) | 结果 |
|---|---|---|---|
| RHEL 10.0 | 6.12.0 | (无) | ROOT |
| Debian 13 trixie | 6.12.90+deb13.1 | (无) | ROOT |
| Ubuntu 24.04.4 | 6.17.0-22 | --ubuntu | ROOT |
| Alpine edge | 7.0.10-0-stable | (无) | ROOT |
Alpine Linux (musl) 编译的 PIE 可执行文件中,可执行 PT_LOAD 段起始文件偏移量 ≥ 0x1000 — 超过了 pedit TCP 头部偏移量限制 511 字节。ELF 头部和程序头部位于第一个只读 PT_LOAD (文件偏移量 0)。此改编的利用方法:
使页面 0 变为可执行:通过在文件偏移量 0xB4 (phdr[2] + 4) 处,向第一个 PT_LOAD 的 p_flags 字段添加 PF_X。
拆分 shellcode 以避免破坏关键的 LOAD 程序头部:
e_entry → 0x28 (一个 e_shoff 字段,内核在执行时不会检查它)。0x28:一个 5 字节 jmp 0x1C8。0x1C8,覆盖内核在执行期间忽略的 PT_NOTE 程序头部。运行时自动发现目标,通过 find / -xdev -type f -perm -4000 -user root 而非硬编码路径。使用找到的第一个世界可读的 setuid-root ELF 二进制文件 — Alpine 的 /bin/su (-> /bin/bbsuid,权限 ---s--x--x) 会自动跳过,因为它无法被打开。
Ubuntu 通过两个 sysctl 参数禁止非特权的、未受约束的用户命名空间:
kernel.apparmor_restrict_unprivileged_userns # 禁止未受约束的 userns 创建
kernel.apparmor_restrict_unprivileged_unconfined # 强制未受约束的 change_profile 变为 STACK,
# 因此 aa-exec 宽松配置文件无法解除 userns 限制
--ubuntu 通过 aa-exec -p {trinity,chrome,flatpak} 重新执行 (这些配置文件包含 userns, 规则)。
| Ubuntu | userns | unconfined | aa-exec 绕过 |
|---|---|---|---|
| 24.04.4 | 1 | 0 | 有效 |
| 26.04 | 1 | 1 | 已封堵 |
一个静态 x86_64-linux-gnu 交叉编译器:
# Debian / Ubuntu
apt install gcc-x86-64-linux-gnu
# Alpine
apk add gcc musl-dev