Dirty Frag & Copy Fail: Echtzeit-Erkennung mit Wazuh und automatischer Gegenwehr
File Integrity Monitoring versagt bei Page-Cache-Exploits - nur Syscall-Monitoring schlägt an.
Wer nach dem Auftauchen von Dirty Frag und Copy Fail instinktiv die Dateiüberwachung in seinem SIEM aktiviert, um auf der sicheren Seite zu sein, liegt leider komplett falsch - beide Exploits schreiben ausschließlich in den RAM-seitigen Page Cache, ohne die Festplattenkopie jemals zu berühren. Die einzig wirksame Echtzeiterkennung ist verhaltensbasiert: auditd auf Syscall-Ebene kombiniert mit passenden Wazuh-Regeln. Kritische Infrastrukturen sollten hier unbedingt handeln. Dieser Beitrag zeigt den aktuellen Stand meines vollständigen Setups - inklusive Active Response, die den Exploit-Prozess killt oder den kompromittierten Rechner automatisch vom Netz nimmt.
Warum klassische FIM-Überwachung hier blind ist
Wer den Artikel zu Dirty Frag gelesen hat, kennt das Prinzip bereits: Der Exploit überschreibt nicht die Datei /usr/bin/su auf der Festplatte, sondern die im RAM gehaltene Page-Cache-Kopie davon. Der Kernel verwendet bei späteren Zugriffen die manipulierte RAM-Version, ohne dass ein Writeback auf den Storage erfolgt. Aus diesem Grunde kann File Integrity Monitoring (FIM) diese Manipulation nicht sehen.
Konkret sieht das so aus:
Klassischer FIM-Ansatz:
Datei von Disk lesen -> Hash berechnen -> Vergleich -> kein Befund
Dirty Frag / Copy Fail Realität:
/usr/bin/su auf Disk = UNVERÄNDERT (identischer Hash)
/usr/bin/su im RAM = ENTHÄLT ROOT-SHELL-ELF
FIM-Ergebnis = BLIND
Das gilt für beide Schwachstellen: Copy Fail (CVE-2026-31431) nutzt AF_ALG-Sockets mit splice() über algif_aead, Dirty Frag (CVE-2026-43284 / CVE-2026-43500) erreicht denselben Effekt über vmsplice + splice durch den esp_input()- bzw. rxkad_verify_packet_1()-Decrypt-Pfad. Die Schnittstelle, über die der Page Cache korrumpiert wird, ist verschieden - das blinde Fleck-Problem für FIM bleibt dasselbe.
Das ist übrigens auch der Grund, warum die Copy-Fail-Mitigation (algif_aead blacklisten) keinen Schutz gegen Dirty Frag bietet: Beide teilen zwar denselben authencesn-Sink im Kernel, werden aber über völlig unterschiedliche Code-Pfade erreicht.
Die Exploit-Kette beider Varianten
Für das Verständnis der Erkennungsregeln ist es wichtig, die genauen Syscall-Sequenzen zu kennen.
Dirty Frag - Variante ESP (CVE-2026-43284)
unshare(CLONE_NEWUSER|CLONE_NEWNET) <- User Namespace: CAP_NET_ADMIN intern
-> XFRM SA via Netlink registrieren <- ESP-SA anlegen
-> vmsplice(ESP-Header in Pipe) <- [!] Angreifer-Bytes in Pipe pflanzen
-> splice(Zieldatei in Pipe) <- [!] Page Cache der Zieldatei in Pipe
-> splice(Pipe an UDP-Socket) <- Kernel-Decrypt-Pfad aktivieren
-> esp_input() AEAD-Decrypt <- 4 Bytes deterministisch in Page Cache
-> execve(/usr/bin/su) <- Korrumpiertes SUID-Binary läuft als root
Auf Ubuntu 24.04+ ist diese Variante durch apparmor_restrict_unprivileged_userns=1 standardmäßig blockiert - auditd protokolliert den Versuch trotzdem (AppArmor-AUDIT-Event), was Regel 200002 auslöst. Der Exploit-Binary switcht dann automatisch auf Variante 2.
Dirty Frag - Variante RxRPC (CVE-2026-43500)
add_key("rxrpc", ...) <- Kein Privilege erforderlich - Session Key K
-> socket(AF_RXRPC=35) <- rxrpc.ko wird automatisch geladen
-> vmsplice(RxRPC-Header) <- [!] Wire-Header in Pipe
-> splice(Zieldatei) <- [!] /etc/passwd Page Cache in Pipe
-> splice(Pipe an UDP) <- Decrypt-Pfad: rxkad_verify_packet_1()
-> 8 Bytes in /etc/passwd <- root-Eintrag: leeres Passwortfeld
-> su - <- Root-Login ohne Passwort
Diese Variante umgeht AppArmor vollständig, läuft ohne jede Erhöhung im Namespace und ist auf allen Linux-Kerneln seit Commit 2dc334f1a63a (Juni 2023) verwundbar - ein Kernel-Patch existiert bisher nicht.
Copy Fail (CVE-2026-31431)
socket(AF_ALG=38, SOCK_SEQPACKET, 0) <- Kein Privilege
-> bind() an authencesn <- Vulnerable Code Path
-> sendmsg() mit AAD-Payload <- Bytes 4-7 = Zielwert
-> splice() <- [!] Page Cache in writable SGL
-> recv() <- authencesn schreibt seqno_lo
-> execve(/usr/bin/su) <- Root
Erkennungsarchitektur
Das Detection-Repository von mym0us3r implementiert zwei voneinander unabhängige Erkennungsschichten, die weder den Kernel-Patchstand noch die Distribution voraussetzen.
| Schicht | Methode | Aktualisierungsfrequenz |
|---|---|---|
| Layer 1 | Verhaltensbasiert: auditd Syscall-Sensor + Wazuh-Regeln | Echtzeit |
| Layer 2 | Konfigurationsbasiert: SCA-Policy | Alle 12 Stunden |
Für Copy Fail existiert ein paralleles Repository: COPY-FAIL-Detection-with-Wazuh-4.14.4 mit den Regeln 199600-199607. Beide Regelsätze koexistieren problemlos auf demselben Wazuh-Manager - die auditd-Key-Namespaces (dirty_frag_* vs. copy_fail_*) und Regel-IDs überlappen nicht.
Layer 1 - auditd als Syscall-Sensor
auditd beobachtet die Syscall-Ebene des Kernels und schreibt Ereignisse nach /var/log/audit/audit.log. Wazuh liest diesen Log und verarbeitet ihn durch seine Regelmaschine.
Ein Detail ist dabei kritisch: Die ESP-Variante ruft unshare(CLONE_NEWUSER) auf und mappt sich selbst als uid=0 im neuen Namespace. Der Kernel-Audit-Subsystem schreibt diese Namespace-interne UID in SYSCALL-Events - ein Filter -F uid!=0 würde die anschließenden vmsplice/splice-Events des Exploit-Prozesses damit unsichtbar machen. Deshalb verwenden die Sensor-Regeln für vmsplice und splice stattdessen -F auid>=1000: Die auid (Login-UID) wird beim Login gesetzt und lässt sich durch Namespace-Remapping nicht verändern.
Die Sensor-Regeln werden als Datei /etc/audit/rules.d/cve-dirty-frag.rules abgelegt:
# Dirty Frag / CVE-2026-43284+43500 - auditd Sensor Rules
# Quelle: github.com/mym0us3r/DIRTY-FRAG-Detection-with-Wazuh-4.14.4
# vmsplice: Protokoll-Header in Pipe pflanzen (beide Varianten)
# auid statt uid - wegen User-Namespace-Remapping im ESP-Pfad
-a always,exit -F arch=b64 -S vmsplice -F auid>=1000 -F auid!=-1 -k dirty_frag_vmsplice
-a always,exit -F arch=b32 -S vmsplice -F auid>=1000 -F auid!=-1 -k dirty_frag_vmsplice
# splice: Zieldatei-Page-Cache in Pipe bringen (beide Varianten)
-a always,exit -F arch=b64 -S splice -F auid>=1000 -F auid!=-1 -k dirty_frag_splice
-a always,exit -F arch=b32 -S splice -F auid>=1000 -F auid!=-1 -k dirty_frag_splice
# unshare(CLONE_NEWUSER): ESP-Variante - User Namespace Escape
-a always,exit -F arch=b64 -S unshare -F uid!=0 -k dirty_frag_ns_escape
# add_key("rxrpc"): RxRPC-Session-Key pflanzen - ohne Privileges
-a always,exit -F arch=b64 -S add_key -F uid!=0 -k dirty_frag_add_key
# socket(AF_RXRPC): rxrpc.ko Auto-Load Trigger
-a always,exit -F arch=b64 -S socket -F a0=0x23 -F uid!=0 -k dirty_frag_rxrpc_socket
# kmod/modprobe Ausführung: esp4/esp6/rxrpc Modul-Load
-w /usr/bin/kmod -p x -k dirty_frag_modload
# execve /usr/bin/su: LPE-Nachweis - auid statt uid
-a always,exit -F arch=b64 -S execve -F exe=/usr/bin/su -F auid>=1000 -F auid!=-1 -k dirty_frag_execve_su
Für Copy Fail kommen analog die Regeln aus cve-2026-31431.rules in eine separate Datei /etc/audit/rules.d/cve-copy-fail.rules.
Layer 2 - Wazuh Detection Rules
Die neun Erkennungsregeln für Dirty Frag (IDs 200000-200008) gehen in /var/ossec/etc/rules/local_rules.xml. Sie bauen alle auf dem Wazuh-Built-in-Anker 80700 (decoded_as=auditd) auf. Die offiziellen Regeln werden direkt aus dem Repository bezogen:
git clone https://github.com/mym0us3r/DIRTY-FRAG-Detection-with-Wazuh-4.14.4.git
git clone https://github.com/mym0us3r/COPY-FAIL-Detection-with-Wazuh-4.14.4.git
Zur Orientierung - so sieht die Regelarchitektur aus (Level und Parent-Beziehungen):
| Regel-ID | Parent | Signal | Level |
|---|---|---|---|
| 200000 | 80700 | vmsplice() - Header in Pipe |
10 |
| 200001 | 80700 | splice() - Page Cache in Pipe |
10 |
| 200002 | 80700 | unshare(CLONE_NEWUSER) - ESP-Pfad |
12 |
| 200003 | 80700 | add_key("rxrpc") - RxRPC-Key |
8 |
| 200004 | 80700 | socket(AF_RXRPC) - Modul-Load |
10 |
| 200005 | 80700 | kmod/modprobe-Ausführung |
12 |
| 200006 | 80700 | execve /usr/bin/su mit euid=root |
6 |
| 200007 | 200001 + if_matched=200000 | CHAIN ESP: vmsplice + splice, gleiche PID, 120s | 15 |
| 200008 | 200001 + if_matched=200004 | CHAIN RXRPC: AF_RXRPC + splice, gleiche PID, 120s | 15 |
Die Kettenregeln 200007 und 200008 sind das Herzstück der Erkennung: Sie feuern, wenn zwei Signale vom selben Prozess innerhalb von 120 Sekunden eintreffen. vmsplice gefolgt von splice aus derselben PID ist die unvermeidliche Kern-Sequenz beider Exploit-Varianten - da führt kein Weg dran vorbei. Level 15 in Wazuh entspricht der höchsten Kritikalitätsstufe.
Zum Einfügen und Validieren:
# Regeln für Dirty Frag einbinden
cat DIRTY-FRAG-Detection-with-Wazuh-4.14.4/rules/local_rules.xml \
>> /var/ossec/etc/rules/local_rules.xml
# Regeln für Copy Fail einbinden
cat COPY-FAIL-Detection-with-Wazuh-4.14.4/rules/local_rules.xml \
>> /var/ossec/etc/rules/local_rules.xml
# Syntaxprüfung - muss mit Exit 0 und ohne Warnungen enden
/var/ossec/bin/wazuh-analysisd -t 2>&1 | tail -5
# Manager neu starten
systemctl restart wazuh-manager
Layer 3 - SCA Policy
Die Security-Configuration-Assessment-Policy oder kurz SCA-Policy (sca/cve-dirty-frag.yml) führt alle 12 Stunden automatisierte Konfigurationschecks auf allen enrollten Agenten aus. Sie prüft, ob die verwundbaren Kernel-Module geladen sind, ob auditd läuft und ob die Sensor-Regeln aktiv sind:
| Check-ID | Prüfung | Risiko |
|---|---|---|
| 43284001 | esp4-Modul nicht geladen |
HIGH |
| 43284002 | esp6-Modul nicht geladen |
HIGH |
| 43284003 | rxrpc-Modul nicht geladen |
HIGH |
| 43284004 | Module via modprobe.d disabled |
HIGH |
| 43284005 | auditd aktiv und läuft |
HIGH |
| 43284006 | Dirty Frag auditd Sensor-Regeln deployed | HIGH |
Ein frisches, noch nicht abgesichertes System erreicht einen Score von 50% - die Module sind zufällig nicht geladen, aber der Detection-Stack fehlt noch.
Die Policy wird auf dem Manager deployed und per agent.conf an alle Agenten verteilt:
# Policy auf Manager kopieren
cp DIRTY-FRAG-Detection-with-Wazuh-4.14.4/sca/cve-dirty-frag.yml \
/var/ossec/etc/shared/default/
chown root:wazuh /var/ossec/etc/shared/default/cve-dirty-frag.yml
chmod 660 /var/ossec/etc/shared/default/cve-dirty-frag.yml
In /var/ossec/etc/shared/default/agent.conf:
<agent_config>
<sca>
<policies>
<policy>/var/ossec/etc/shared/cve-dirty-frag.yml</policy>
</policies>
</sca>
</agent_config>
Da die SCA-Policy c:lsmod- und c:systemctl-Checks ausführt, muss auf jedem Agenten Remote Command Execution einmalig aktiviert werden:
echo "sca.remote_commands=1" >> /var/ossec/etc/local_internal_options.conf
systemctl restart wazuh-agent
Deployment Schritt für Schritt
Schritt 1: auditd installieren und aktivieren
# Ubuntu / Debian
apt install auditd audispd-plugins -y
systemctl enable --now auditd
auditctl -s | grep enabled # muss "enabled 1" zeigen
# RHEL / Amazon Linux
yum install audit -y
systemctl enable --now auditd
# SUSE
zypper install audit -y
systemctl enable --now auditd
Schritt 2: auditd Sensor-Regeln deployen
# Dirty Frag Regeln
cp DIRTY-FRAG-Detection-with-Wazuh-4.14.4/auditd/cve-dirty-frag.rules \
/etc/audit/rules.d/
# Copy Fail Regeln
cp COPY-FAIL-Detection-with-Wazuh-4.14.4/auditd/cve-2026-31431.rules \
/etc/audit/rules.d/
# Laden und verifizieren
augenrules --load
auditctl -l | grep -E "dirty_frag|copy_fail"
Erwartete Ausgabe (9 Dirty Frag + Copy Fail Regeln):
-a always,exit -F arch=b64 -S vmsplice -F auid>=1000 -F auid!=-1 -F key=dirty_frag_vmsplice
-a always,exit -F arch=b32 -S vmsplice -F auid>=1000 -F auid!=-1 -F key=dirty_frag_vmsplice
-a always,exit -F arch=b64 -S splice -F auid>=1000 -F auid!=-1 -F key=dirty_frag_splice
...
Schritt 3: auditd-Log in Wazuh einbinden
In /var/ossec/etc/ossec.conf auf jedem überwachten Host (oder per agent.conf verteilen):
<localfile>
<log_format>audit</log_format>
<location>/var/log/audit/audit.log</location>
</localfile>
Schritt 4: Wazuh-Regeln deployen (siehe Layer 2 oben)
Schritt 5: SCA-Policy deployen (siehe Layer 3 oben)
Validierung
Vor dem Scharfschalten empfiehlt sich ein wazuh-logtest mit synthetischen auditd-Events. Das prüft, ob die Regeln korrekt feuern, ohne einen echten Exploit auszuführen:
# Signal 1: vmsplice -> Regel 200000 erwartet
echo 'type=SYSCALL msg=audit(1778261582.230:1155): arch=c000003e syscall=316 \
success=yes exit=8 a0=3 a1=7fff00000000 a2=1 a3=0 items=0 ppid=1000 pid=93321 \
auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 \
fsgid=1000 tty=pts0 ses=58 comm="exp" exe="/home/kr/exp" subj=unconfined \
key="dirty_frag_vmsplice"' | /var/ossec/bin/wazuh-logtest 2>&1 | grep -E "rule|level|200"
Erwartete Ausgabe:
Rule id: '200000'
Rule level: '10'
Rule description: 'Dirty Frag CVE-2026-43284/43500: vmsplice()'
Für den Chain-Test (200007 - das eigentliche Alarmsignal):
# Zwei Events in einer Pipe - gleiche PID, vmsplice + splice
printf 'type=SYSCALL msg=audit(1777570010.000:200): arch=c000003e syscall=316 \
success=yes exit=8 a0=3 a1=7fff00000000 a2=1 a3=0 items=0 ppid=1000 pid=55001 \
auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 \
fsgid=1000 tty=pts0 ses=58 comm="exp" exe="/home/kr/exp" key="dirty_frag_vmsplice"\n\
type=SYSCALL msg=audit(1777570011.000:201): arch=c000003e syscall=275 success=yes \
exit=4 a0=4 a1=5 a2=6 a3=0 items=0 ppid=1000 pid=55001 auid=1000 uid=1000 \
gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts0 \
ses=58 comm="exp" exe="/home/kr/exp" key="dirty_frag_splice"\n' \
| /var/ossec/bin/wazuh-logtest 2>&1 | grep -E "rule|level|200"
Erwartete Ausgabe:
Rule id: '200000' Rule level: '10' <- vmsplice Signal
Rule id: '200007' Rule level: '15' <- CHAIN ESP CONFIRMED - SOFORT UNTERSUCHEN
Nach einer echten Exploit-Ausführung (im Lab!) kann so geprüft werden, ob auditd alle Events aufgezeichnet hat:
ausearch -k dirty_frag_vmsplice --start today 2>/dev/null | grep "exe=" | head -5
ausearch -k dirty_frag_ns_escape --start today 2>/dev/null | grep "exe=" | head -5
ausearch -k dirty_frag_execve_su --start today 2>/dev/null | grep "EUID=" | head -5
# Wazuh Alerts prüfen
grep -E "200002|200005|200006|200007|200008" /var/ossec/logs/alerts/alerts.log | tail -10
Active Response - Den Exploit-Prozess sofort killen
Sobald Regel 200007 oder 200008 feuert, ist die Exploit-Kette am Laufen. Ein manueller Eingriff dauert zu lange - hier kommt Wazuhs Active Response ins Spiel.
In Wazuh 4.x übergibt das Framework den kompletten Alert als JSON an das Active-Response-Skript via stdin. Das Skript liest data.audit.pid aus dem JSON und killt den Exploit-Prozess inkl. Parent.
Das Skript wird auf dem Agenten (dem überwachten Host) abgelegt:
# /var/ossec/active-response/bin/dirty-frag-kill.sh
# M. Meister - Dirty Frag / Copy Fail: Exploit-Prozess beenden
#!/bin/bash
LOG="/var/ossec/logs/active-responses.log"
# Alert-JSON von Wazuh via stdin lesen und PID extrahieren
PID=$(python3 -c "
import json, sys
try:
d = json.load(sys.stdin)
print(d['parameters']['alert']['data']['audit']['pid'])
except Exception:
print('')
" 2>/dev/null)
if [ -z "$PID" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: keine PID im Alert gefunden" >> "$LOG"
exit 1
fi
# PID muss numerisch sein
if ! [[ "$PID" =~ ^[0-9]+$ ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: ungültige PID '$PID'" >> "$LOG"
exit 1
fi
# Parent-PID des Exploit-Prozesses bestimmen
PPID=$(ps -o ppid= -p "$PID" 2>/dev/null | tr -d ' ')
EXE=$(readlink -f /proc/"$PID"/exe 2>/dev/null || echo "unbekannt")
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: KILL PID=$PID PPID=$PPID EXE=$EXE" >> "$LOG"
# Exploit-Prozess killen
kill -9 "$PID" 2>/dev/null && \
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: PID=$PID erfolgreich beendet" >> "$LOG"
# Parent killen (ausser init/systemd), um Respawn zu verhindern
if [ -n "$PPID" ] && [ "$PPID" -gt 2 ]; then
kill -9 "$PPID" 2>/dev/null && \
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: PPID=$PPID erfolgreich beendet" >> "$LOG"
fi
# Kernel-Module entladen, sofern noch geladen
for mod in rxrpc esp4 esp6; do
if lsmod | grep -q "^${mod} "; then
rmmod "$mod" 2>/dev/null && \
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-kill: Modul $mod entladen" >> "$LOG"
fi
done
exit 0
Berechtigungen setzen:
chmod 750 /var/ossec/active-response/bin/dirty-frag-kill.sh
chown root:wazuh /var/ossec/active-response/bin/dirty-frag-kill.sh
In /var/ossec/etc/ossec.conf auf dem Wazuh-Manager einbinden:
<!-- Dirty Frag / Copy Fail: Exploit-Prozess killen -->
<command>
<name>dirty-frag-kill</name>
<executable>dirty-frag-kill.sh</executable>
<timeout_allowed>no</timeout_allowed>
</command>
<active-response>
<disabled>no</disabled>
<command>dirty-frag-kill</command>
<location>local</location>
<!-- Dirty Frag Chains + Copy Fail Highest-Level Rules -->
<rules_id>200007,200008,199607</rules_id>
</active-response>
<location>local</location> bedeutet: das Skript läuft auf dem Agenten, der den Alert ausgelöst hat - also genau dort, wo der Exploit abläuft. 199607 ist die höchste Chain-Regel des Copy Fail Regelsets - die tatsächliche ID sollte aus dem jeweiligen Repository verifiziert werden.
Wichtig: Active Response ist kein Allheilmittel. Wenn der Exploit bereits execve(/usr/bin/su) aufgerufen hat (Regel 200006), ist die Page-Cache-Korruption bereits geschehen. Die Process-Kill-Antwort ist am wirkungsvollsten bei den frühen Chain-Signalen 200007/200008 - also bevor Root erlangt wurde. Das demonstriert auch, warum eine schnelle, automatisierte Antwort gegenüber manueller Triage einen echten Unterschied macht.Active Response - Host-Netzwerk-Isolierung
Bei einem bestätigten Compromise oder kritischen Alarm empfiehlt sich eine vollständige Netzwerk-Isolierung des Hosts. Das zweite Skript sperrt alle Verbindungen via iptables - mit einer wichtigen Ausnahme: Die Verbindung zum Wazuh-Manager bleibt offen, damit der Agent weiter Telemetrie sendet und ferngesteuert werden kann. Das habe ich bis jetzt noch nicht getestet, aber es scheint mir sinnvoll dies zu implementieren.
# /var/ossec/active-response/bin/dirty-frag-isolate.sh
# M. Meister - Host-Netzwerk-Isolierung bei LPE-Alarm (Wazuh Manager bleibt erreichbar)
#!/bin/bash
LOG="/var/ossec/logs/active-responses.log"
# Wazuh-Manager-IP aus agent config lesen
MANAGER_IP=$(grep -oP '(?<=<address>)[^<]+' /var/ossec/etc/ossec.conf 2>/dev/null | head -1)
if [ -z "$MANAGER_IP" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-isolate: FEHLER - Manager-IP nicht ermittelbar" >> "$LOG"
exit 1
fi
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-isolate: Isolierung gestartet, Manager=$MANAGER_IP" >> "$LOG"
# Bestehende Regeln sichern (für manuelle Wiederherstellung)
iptables-save > /var/ossec/logs/iptables-pre-isolate-"$(date +%s)".bak 2>/dev/null
# INPUT/OUTPUT/FORWARD leeren
iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
# Loopback immer erlauben
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Wazuh Manager: bidirektional erlauben (Port 1514 UDP/TCP Agent-Kommunikation)
iptables -A INPUT -s "$MANAGER_IP" -j ACCEPT
iptables -A OUTPUT -d "$MANAGER_IP" -j ACCEPT
# Established/Related Verbindungen zum Manager durchlassen
iptables -A INPUT -m state --state ESTABLISHED,RELATED -s "$MANAGER_IP" -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -d "$MANAGER_IP" -j ACCEPT
# Alles andere: DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-isolate: Host isoliert. Nur $MANAGER_IP zugelassen." >> "$LOG"
# Optional: Kernel-Module entladen
for mod in rxrpc esp4 esp6 algif_aead; do
rmmod "$mod" 2>/dev/null && \
echo "$(date '+%Y-%m-%d %H:%M:%S') dirty-frag-isolate: Modul $mod entladen" >> "$LOG"
done
exit 0
chmod 750 /var/ossec/active-response/bin/dirty-frag-isolate.sh
chown root:wazuh /var/ossec/active-response/bin/dirty-frag-isolate.sh
In /var/ossec/etc/ossec.conf auf dem Manager:
<!-- Dirty Frag / Copy Fail: Netzwerk-Isolierung -->
<command>
<name>dirty-frag-isolate</name>
<executable>dirty-frag-isolate.sh</executable>
<timeout_allowed>no</timeout_allowed>
</command>
<active-response>
<disabled>no</disabled>
<command>dirty-frag-isolate</command>
<location>local</location>
<rules_id>200007,200008,199607</rules_id>
</active-response>
Manager neu starten, damit die Active-Response-Konfiguration aktiv wird:
systemctl restart wazuh-manager
Die Isolierung lässt sich bei Bedarf manuell rückgängig machen:
# Auf dem betroffenen Agenten - Sicherung wiederherstellen
iptables-restore < /var/ossec/logs/iptables-pre-isolate-*.bak
# Oder alle Regeln flushen und Policies zurücksetzen
iptables -F && iptables -P INPUT ACCEPT && iptables -P OUTPUT ACCEPT && iptables -P FORWARD ACCEPT
Überlegung zum Produktivbetrieb: Wer auf dem überwachten Hostnftablesstattiptableseinsetzt oder SSH-Zugang für die manuelle Forensik erhalten muss, passt die ACCEPT-Regeln entsprechend an. Auf Systemen mit aktiven IPsec-Tunnels (strongSwan/Libreswan) sollte vor dem Deployen der Isolierungsregel geprüft werden, obesp4/esp6wirklich entladen werden dürfen - das zieht bestehende VPN-Verbindungen unweigerlich nach unten.
Das Thema Active Response mit KI-gestützter Incident-Analyse lässt sich weiter vertiefen - wie das aussehen kann, beschreibt der Artikel Wazuh on Steroids: AI-Powered Incident Analysis On-Demand.

Fazit
Dirty Frag und Copy Fail machen deutlich, dass traditionelle dateibasierte Integritätsprüfung gegen moderne Page-Cache-Exploits strukturell blind ist - nur verhaltensbasierte Syscall-Überwachung durch auditd kombiniert mit Wazuh-Chain-Regeln liefert zuverlässige Echtzeiterkennung. Mit den Active-Response-Skripten lässt sich der Detect-to-Respond-Zeitraum auf Sekunden reduzieren, bevor ein Angreifer seinen Root-Zugang konsolidieren kann.
