
🌐 Auch auf: English · Français · Español
Ein WordPress-uploads-Ordner soll deine Urlaubsfotos aufbewahren
und hin und wieder ein PDF. Er soll ausdrücklich keine PHP-Web-Shell
beherbergen – aber dieses „soll“ trägt da eine Menge Last. Das
Upload-Verzeichnis ist der mit Abstand häufigste Ort, an dem ein kompromittiertes
WordPress am Ende Angreifer-Code ausführt: Es ist beschreibbar, aus dem Web
erreichbar, und jeder vergisst es. Also habe ich meinem eine Nase verpasst: einen
Virenscanner, der jede Nacht am Upload-Ordner schnüffelt und mir sofort eine Mail
schickt, wenn ihm etwas stinkt. 👃 Und weil ein Rauchmelder, den man nie getestet hat, bloß Deckenschmuck ist, habe
ich ihn auch absichtlich auslösen lassen. Mehr dazu am Ende.🎯 Das Konzept (bewusst langweilig)
- 🌙 On-Demand, nächtlich. Kein Echtzeit-Daemon mit On-Access-Scan – das ist zu schwer für einen bescheidenen VPS, und ich bin praktisch der Einzige, der hier je etwas hochlädt. Ein Durchlauf pro Nacht ist der richtige Kompromiss: Im schlimmsten Fall liegt Malware ein paar Stunden herum, bis die Mail am Morgen kommt, nicht wochenlang.
- 🔇 Still, solange er nichts findet. Dieselbe Philosophie wie bei allem anderen auf der Kiste (rkhunter, Monit): Ein sauberer Scan verschickt nichts. Eine Mail vom Scanner heißt also immer „schau dir das jetzt an“, nie „alles gut, wie immer“.
- 🔎 Nur Erkennung. Er meldet, er löscht nichts und schiebt nichts in Quarantäne. Auf einem privaten Blog schaue ich mir einen Fund lieber selbst an, als dass ein automatischer Hausmeister heimlich eine False-Positive-Datei verputzt.
- 🧩 Per Ansible verwaltet (Rolle
blog_clamav), also reproduzierbar und versioniert – kein Snowflake-Skript, bei dem ich irgendwann vergessen habe, wie ich es gebaut habe.
📦 Installation & Konfiguration
Auf AlmaLinux 9 besteht das Ganze aus ClamAV plus seinem Signatur-Updater:dnf install clamav clamav-updateDie mitgelieferte freshclam.conf enthält eine Platzhalterzeile, die
verhindert, dass es überhaupt jemals läuft – der klassische Stolperstein beim
ersten Mal. Zeile raus, Signaturdatenbank einmal ziehen, Auto-Updates aktivieren:# kill the "Example" line or freshclam refuses to start
sed -i '/^Example/d' /etc/freshclam.conf
freshclam # first full signature download
systemctl enable --now clamav-freshclam # keep signatures fresh automaticallyDer eigentliche Scan ist ein kleines Skript unter /usr/local/sbin/wordpress-clamav-scan.sh. Es scannt nur das
Upload-Verzeichnis, nimmt Rücksicht auf den Rest der Kiste, überspringt absurd
große Dateien (Malware versteckt sich nicht in einem 300-MB-Video) und – das
Wichtigste – mailt nur bei einem Treffer:#!/bin/bash
SCAN_LOG="/var/log/clamav/wordpress-scan.log"
UPLOADS_DIR="/srv/wordpress-www/wp-content/uploads"
nice -n 19 ionice -c3 clamscan --recursive --infected --no-summary
\
--max-filesize=200M --max-scansize=200M
\
"$UPLOADS_DIR" > "$SCAN_LOG" 2>&1
RC=$?
# clamscan exit code 1 = something was found -> mail it, otherwise stay silent
if [ "$RC" -eq 1 ]; then
mail -s "[$(hostname -f)] ClamAV: infected file(s) found in WordPress uploads"
\
me@example.com < "$SCAN_LOG"
fi
exit 0Ein Cron-Eintrag startet es jede Nacht um 02:45 Uhr. Die Mail geht über dasselbe msmtp-Relay raus wie meine anderen Alarme (rkhunter, Monit), also
muss ich nur einen Mailweg am Laufen halten, nicht drei.45 2 * * * /usr/local/sbin/wordpress-clamav-scan.sh🧪 Ihn zum Bellen bringen: der EICAR-Test
Jetzt kommt der spaßige Teil. Um einen Virenscanner zu testen, lädst du keine echte Malware herunter – du nimmst EICAR, einen branchenweit standardisierten Teststring aus 68 Bytes, den jeder Scanner auf diesem Planeten anschlagen muss und der völlig harmlos ist (das ist keine ausführbare Malware, sondern ein druckbarer ASCII-String, der einzig dazu da ist, die Erkennung auszulösen). Ab damit in den Scanpfad, das echte Skript laufen lassen, zusehen, wie die ganze Kette zündet, dann wieder löschen:# drop the harmless EICAR test file into uploads
printf 'X5O!P%%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*\n'
\
> /srv/wordpress-www/wp-content/uploads/eicar-test.txt
# run the actual nightly script by hand
/usr/local/sbin/wordpress-clamav-scan.sh
# clean up immediately
rm -f /srv/wordpress-www/wp-content/uploads/eicar-test.txtClamAV hat ihn sofort erwischt – Eicar-Signature FOUND –,
das Skript beendete sich mit Exit-Code 1, verschickte die Mail, und kurz darauf landete das
hier in meinem Posteingang:From: homelab@apt-upgrade.me
Subject: [www.apt-upgrade.me] ClamAV: infected file(s) found in WordPress uploads
/srv/wordpress-www/wp-content/uploads/eicar-test.txt: Eicar-Signature FOUNDUnd um gründlich zu sein, habe ich auch das Log des Mail-Relays geprüft, das die
Übergabe bestätigte, statt sie einfach anzunehmen:host=smtp.strato.de tls=on auth=on ... recipients=you@example.com
smtpstatus=250 ... 'OK queued' exitcode=EX_OKErkennung ✔️, Mailweg des echten Skripts ✔️, Zustellung in meinen echten
Posteingang ✔️. Das ist der Unterschied zwischen „Ich habe einen Scanner
konfiguriert“ und „Ich habe einen Scanner, der funktioniert“ – beim zweiten hast
du zugesehen, wie er etwas fängt. 🎯 (Testdatei gleich danach gelöscht, Uploads
wieder sauber. Das war eine Übung, kein Einbruch.)⚠️ Ehrliche Grenzen
- Es ist ein nächtlicher Scan, keine Echtzeit. Ein bösartiger Upload wird beim nächsten Lauf um 02:45 Uhr erwischt, nicht in dem Moment, in dem er landet.
- Er scannt Uploads, den riskantesten Ordner – nicht die ganze Platte. Für die Betriebssystemebene gibt es ein anderes Werkzeug (das ist hier der Job von rkhunter).
- Erkennung, keine Entfernung. Die Mail ist das ganze Produkt; das Aufräumen ist eine menschliche Entscheidung.




