Files
SKELETONKEY/modules/copy_fail_family/docs/DEFENDERS.md
T

245 lines
8.4 KiB
Markdown

# 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 <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:
```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 <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
```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.