# DIRTYFAIL — defender's playbook A one-page operational guide for sysadmins assessing and mitigating exposure to the Copy Fail and Dirty Frag CVE family on Linux hosts. If you're operating a fleet of Linux servers, the questions below are the ones to answer in order. --- ## 1. Am I vulnerable? **Quickest answer (no compilation):** ```bash curl -sSL https://raw.githubusercontent.com/KaraZajac/DIRTYFAIL/main/tools/dirtyfail-check.sh \ | bash ``` (Read the script first if you don't trust me — it's ~150 lines of plain bash, no curl-pipe-bash voodoo. Read-only on your system.) Exit code: `0` mitigated, `1` vulnerable, `2` couldn't determine. **Empirical answer (builds the C tool, runs the active probes):** ```bash git clone https://github.com/KaraZajac/DIRTYFAIL.git cd DIRTYFAIL && make ./dirtyfail --scan --active ``` The default `--scan` mode runs precondition checks (kernel version, module presence, LSM state) plus an active probe of the Copy Fail primitive against a sentinel file in `/tmp`. Adding `--active` extends the sentinel-STORE probe to the other four primitives (ESP v4, ESP v6, RxRPC, GCM) — this is the only way to distinguish a backported-patched kernel from an unpatched one without running the full exploit. The probes only modify temporary files in `/tmp`; `/etc/passwd` is never touched. **Per-CVE breakdown (manual checks):** | Question | Command | Vulnerable if | |---|---|---| | Is the algif_aead module reachable? | `lsmod \| grep algif_aead` + `grep algif_aead /etc/modprobe.d/*` | Loaded AND not blacklisted | | Are esp4/esp6 modules reachable? | `modinfo esp4 esp6` | Both present, not blacklisted | | Is rxrpc reachable? | `lsmod \| grep rxrpc` + `getsockopt(AF_RXRPC, ...)` | Module loadable from unprivileged context | | Is unprivileged userns hardened? | `cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns` | Returns `0` or file absent | | Does PAM accept empty passwords? | `grep nullok /etc/pam.d/common-auth` | "nullok" present without "nullok_secure" | --- ## 2. How do I mitigate? Three options, listed best-to-worst: ### A. Apply the upstream kernel patch (best) The fix is mainline commit `f4c50a4034e6` (merged 2026-05-07). Each distro's kernel package is on its own backport timeline: | Distro | Status (as of 2026-05-09) | |---|---| | Debian 13 (`6.12.86+deb13`) | ✅ patched | | Ubuntu 24.04 LTS | ❌ not yet patched (kernel 6.8.0-111) | | Ubuntu 26.04 LTS | ❌ not yet patched (kernel 7.0.0-15.15, predates upstream merge) | | AlmaLinux 10.1 | ❌ not yet patched (kernel 6.12 EL) | | Fedora 44 | ❌ not yet patched (kernel 6.19.10) | Run `apt list --upgradable linux-image-*` / `dnf check-update kernel` periodically and apply. ### B. Layered LSM mitigation (Ubuntu 26.04 model) If you're on Ubuntu 24.04 or 26.04, you can replicate Ubuntu 26.04's defense-in-depth approach without waiting for the kernel patch: ```bash # 1. Block unprivileged user namespaces from acquiring caps echo 'kernel.apparmor_restrict_unprivileged_userns = 1' \ | sudo tee /etc/sysctl.d/99-userns-restrict.conf sudo sysctl --system # 2. Verify the AA hardening is in effect: sudo unshare -U -r bash -c 'echo deny > /proc/self/setgroups 2>&1' \ || echo "OK: unprivileged userns has no caps (mitigation working)" ``` This blocks the EXPLOIT INFRASTRUCTURE (no caps in unprivileged userns), not the underlying kernel bug. Real-root exploitation still works. ### C. Module blacklist (`dirtyfail --mitigate` or manual) Heaviest hammer — blacklists every module that hosts a primitive. **Side effects: breaks IPsec, AFS, and any userspace using `AF_ALG` AEAD.** Automated: ```bash sudo ./dirtyfail --mitigate ``` Manual equivalent: ```bash sudo tee /etc/modprobe.d/dirtyfail-mitigations.conf <<'EOF' install algif_aead /bin/false install esp4 /bin/false install esp6 /bin/false install rxrpc /bin/false EOF sudo rmmod algif_aead esp4 esp6 rxrpc 2>/dev/null sudo sysctl vm.drop_caches=3 ``` Undo: `sudo ./dirtyfail --cleanup-mitigate` (or delete the conf files, then `sudo modprobe ` to reload as needed). ### D. Disable `pam_unix nullok` Optional belt-and-suspenders: even if a page-cache STORE lands, the exploit relies on PAM's `nullok` flag to convert "empty password field in /etc/passwd" into a successful `su`. Removing `nullok` from `/etc/pam.d/common-auth` (Debian/Ubuntu) or `/etc/pam.d/system-auth` (Red Hat family) closes that step: ```bash sudo sed -i 's/\bnullok\b//g' /etc/pam.d/common-auth # Debian/Ubuntu # Verify a passworded user can still log in normally before logging out! ``` --- ## 3. What should I monitor? Even after mitigation, the kernel bug remains until the patch lands. For detection: ### auditd rules (universal) A ready-to-load rules file ships in `tools/99-dirtyfail.rules`. It covers six syscall paths used by the exploit chain: XFRM netlink, add_key(rxrpc), unshare(CLONE_NEWUSER), AF_ALG socket creation, AppArmor `change_onexec` writes, and direct `/etc/passwd`/`/etc/shadow` modifications. ```bash sudo install -m 0640 tools/99-dirtyfail.rules /etc/audit/rules.d/ sudo augenrules --load sudo systemctl restart auditd ``` Search for events: ```bash # grep is more reliable than ausearch on distros that use ENRICHED # log_format (Debian 13, Fedora 44 — ausearch -k can return "no matches" # even when SYSCALL events with the key are present in the file). sudo grep -E 'type=SYSCALL.*key="dirtyfail-' /var/log/audit/audit.log | tail -20 # Or per-key, only the most recent entries: sudo grep 'key="dirtyfail-xfrm"' /var/log/audit/audit.log | tail -5 sudo grep 'key="dirtyfail-rxkey"' /var/log/audit/audit.log | tail -5 sudo grep 'key="dirtyfail-userns"' /var/log/audit/audit.log | tail -5 sudo grep 'key="dirtyfail-afalg"' /var/log/audit/audit.log | tail -5 ``` (`sudo ausearch -k ` is the documented tool for this and works on older distros, but enriched-format compat issues mean `grep` is the safer default.) The `dirtyfail-userns` rule fires on every legitimate `unshare -U` and rootless container start — pair it with `dirtyfail-xfrm` in a SIEM correlation rule (same auid, both within ~5s) for a high-fidelity alert. Tuning notes inline in the rules file. ### eBPF / falco (if you have it) Falco's `Sensitive mount opened for writing` and `Detect outbound connections to common miner pool ports` rule sets won't help directly, but a custom rule on `unshare(CLONE_NEWUSER)` followed by `sendto(SOCK_RAW, NETLINK_XFRM)` from a non-zero uid is high-fidelity. ### Cheap log signal ```bash # Hits if our exploit's marker bytes show up in /etc/passwd's page cache # (run periodically; doesn't catch every variant but is zero-cost) grep -E '^[^:]+::0:0:|^[^:]+:x:0000:' /etc/passwd ``` --- ## 4. Quick reference card ``` SCAN this host: curl ... | bash # bash check (no compile) ./dirtyfail --scan # preconds + Copy Fail probe (~1s) ./dirtyfail --scan --active # all 5 sentinel-STORE probes (~10s) ./dirtyfail --scan --active --json # same, machine-readable for SIEM MITIGATE (Ubuntu / fleet-wide): sudo ./dirtyfail --mitigate # one-shot defensive deployment sudo ./dirtyfail --cleanup-mitigate # undo MITIGATE (manual, no DIRTYFAIL): See section 2-C above. PATCH: apt list --upgradable | grep linux-image dnf check-update kernel MONITOR: /etc/audit/rules.d/99-dirtyfail.rules (see section 3) EMERGENCY (suspected compromise via this CVE class): sudo sysctl vm.drop_caches=3 # evicts page-cache exploits sudo systemctl restart sshd # forces re-read of /etc/passwd grep dirtyfail /etc/passwd # check for backdoor user rm -f /var/tmp/.dirtyfail.state # clean DIRTYFAIL state file ``` --- ## 5. Glossary - **Page-cache write**: kernel writes attacker-controlled bytes into the in-memory copy of a file (`/etc/passwd`, `/usr/bin/su`) without modifying the file on disk. Persists in RAM until eviction. - **PAM nullok**: configuration flag that permits authentication for accounts with an empty password field in `/etc/passwd` (or `/etc/shadow`). - **xfrm-ESP**: the kernel's ESP (Encapsulating Security Payload) implementation in the IPsec stack. The bug class affects in-place AEAD decrypt over splice-pinned page-cache pages. - **Userns capability stripping**: kernel-level enforcement that unprivileged user namespaces have no `CAP_NET_ADMIN` / `CAP_SYS_ADMIN`, blocking exploit infrastructure even when the underlying kernel bug is unpatched.