8.4 KiB
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):
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):
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:
# 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:
sudo ./dirtyfail --mitigate
Manual equivalent:
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 <name> 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:
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.
sudo install -m 0640 tools/99-dirtyfail.rules /etc/audit/rules.d/
sudo augenrules --load
sudo systemctl restart auditd
Search for events:
# 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 <key> 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
# 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.