101 lines
5.0 KiB
Plaintext
101 lines
5.0 KiB
Plaintext
# DIRTYFAIL — auditd detection rules
|
|
#
|
|
# Drop into /etc/audit/rules.d/, then reload:
|
|
#
|
|
# sudo install -m 0640 99-dirtyfail.rules /etc/audit/rules.d/
|
|
# sudo augenrules --load
|
|
# sudo systemctl restart auditd
|
|
#
|
|
# These rules generate audit events for the syscalls the DIRTYFAIL
|
|
# exploit chain uses. They are intentionally noisy on systems that
|
|
# legitimately use rootless containers, IPsec, or AFS — review the
|
|
# Tuning section before enabling on a production host.
|
|
#
|
|
# Search recorded events:
|
|
#
|
|
# sudo ausearch -k dirtyfail-xfrm
|
|
# sudo ausearch -k dirtyfail-rxkey
|
|
# sudo ausearch -k dirtyfail-userns
|
|
#
|
|
# Rules MUST stay on single lines — auditctl(8) does not honor
|
|
# backslash-newline continuations in rule files.
|
|
#
|
|
# Tested on: Debian 13, Ubuntu 24.04/26.04, AlmaLinux 10, Fedora 44.
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## 1. XFRM netlink registration from a non-root account
|
|
##
|
|
## socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM) is an extremely strong
|
|
## signal: legitimate use is "ip xfrm" (root) or `swanctl`/charon (root)
|
|
## or networkd (root). An unprivileged account creating this socket
|
|
## is the precondition for ESP v4/v6/GCM exploitation.
|
|
##
|
|
## socket() args: a0=family(16=AF_NETLINK) a2=protocol(6=NETLINK_XFRM)
|
|
## auid filter: ignore kernel/system processes (auid=4294967295)
|
|
## match interactive logins (auid >= 1000)
|
|
## ----------------------------------------------------------------- ##
|
|
-a always,exit -F arch=b64 -S socket -F a0=16 -F a2=6 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-xfrm
|
|
-a always,exit -F arch=b32 -S socket -F a0=16 -F a2=6 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-xfrm
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## 2. add_key("rxrpc", ...) — RxRPC session-key registration
|
|
##
|
|
## The rxkad-handshake forgery requires registering a rxrpc-typed key
|
|
## via add_key(2). On most servers this should never happen from an
|
|
## unprivileged uid; AFS clients that legitimately use this run as
|
|
## root or a service account.
|
|
## ----------------------------------------------------------------- ##
|
|
-a always,exit -F arch=b64 -S add_key -F auid>=1000 -F auid!=4294967295 -k dirtyfail-rxkey
|
|
-a always,exit -F arch=b32 -S add_key -F auid>=1000 -F auid!=4294967295 -k dirtyfail-rxkey
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## 3. unshare(CLONE_NEWUSER) from interactive accounts
|
|
##
|
|
## CLONE_NEWUSER == 0x10000000. Every DIRTYFAIL exploit mode does this
|
|
## once. WARNING: this fires on every legitimate `unshare -U`, every
|
|
## podman/buildah container start, every chrome/firefox sandbox spawn.
|
|
## Filter to executions you don't expect, or treat as low-fidelity noise
|
|
## that pairs well with the dirtyfail-xfrm key for high-fidelity alerts.
|
|
## ----------------------------------------------------------------- ##
|
|
-a always,exit -F arch=b64 -S unshare -F a0&268435456 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-userns
|
|
-a always,exit -F arch=b32 -S unshare -F a0&268435456 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-userns
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## 4. AF_ALG socket creation — Copy Fail / GCM precondition
|
|
##
|
|
## socket(AF_ALG, ...). a0=38 (PF_ALG). Legitimate uses: cryptsetup,
|
|
## kernel-side TLS offload, some QEMU paths. Suspicious from a shell
|
|
## account.
|
|
## ----------------------------------------------------------------- ##
|
|
-a always,exit -F arch=b64 -S socket -F a0=38 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-afalg
|
|
-a always,exit -F arch=b32 -S socket -F a0=38 -F auid>=1000 -F auid!=4294967295 -k dirtyfail-afalg
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## 5. Directly watch /etc/passwd and /etc/shadow for in-place modifications
|
|
##
|
|
## A successful exploit modifies the page-cache copy (which is what
|
|
## PAM reads), but these watches fire when /usr/bin/passwd, vipw, or
|
|
## anything else opens these files for writing. Useful as a baseline
|
|
## change-detection rule independent of DIRTYFAIL.
|
|
## ----------------------------------------------------------------- ##
|
|
-w /etc/passwd -p wa -k dirtyfail-passwd-write
|
|
-w /etc/shadow -p wa -k dirtyfail-shadow-write
|
|
|
|
## ----------------------------------------------------------------- ##
|
|
## Tuning notes
|
|
##
|
|
## - On servers running rootless containers, dirtyfail-userns will be
|
|
## high-volume noise. Either drop rule 3, or filter on `comm!=podman`
|
|
## etc. for your specific runtime.
|
|
## - On IPsec gateways, dirtyfail-xfrm fires for every legitimate SA
|
|
## install. Drop the rule or filter `comm` to your VPN daemon.
|
|
## - Pair dirtyfail-userns + dirtyfail-xfrm with a SIEM correlation
|
|
## rule: "same auid emits both within 5 seconds" → high-confidence
|
|
## exploit-attempt alert.
|
|
##
|
|
## Note: the AppArmor `change_onexec` rule that an earlier draft
|
|
## included is omitted — auditctl won't reliably match writes to
|
|
## /proc/self/attr/exec via -F path because the path is per-pid.
|
|
## Use the userns + xfrm pair instead for the bypass-detection signal.
|
|
## ----------------------------------------------------------------- ##
|