Zwei Überwachungskameras an einer grauen Ziegelsteinwand.

Rauschen im Log minimieren: Blind Botnets & Malicious Probes mit Fail2ban blockieren

Jeder, der einen öffentlich erreichbaren Webserver betreibt, kennt das Phänomen: Ein Blick in die Logfiles offenbart sekündliche Zugriffe auf Dateien wie wp-login.php, .env oder obskure Pfade, die auf dem eigenen Server überhaupt nicht existieren. Bei diesen Zugriffen handelt es sich selten um gezielte, menschliche Hackerangriffe. Es ist das permanente, automatisierte Hintergrundrauschen des Internets: sogenannte Blind Bots und Malicious Probes.

In diesem Beitrag schauen wir uns an, warum diese Scans ein Problem darstellen und wie wir sie mittels Fail2ban effizient blockieren, bevor sie die Performance unseres Apache-Webservers beeinträchtigen.

Das Problem mit den automatisierten Scans

Diese Botnetze agieren sprichwörtlich "blind". Sie scannen massenhaft das IPv4- und IPv6-Adressspektrum auf gut Glück ab. Sie wissen nicht, welche Applikationen tatsächlich auf dem Zielsystem laufen, sondern suchen automatisiert nach bekannten Schwachstellen in Content-Management-Systemen (wie WordPress), offenliegenden Konfigurationsdateien (.env) oder Fehlkonfigurationen.

Auch wenn ein einzelner HTTP-404-Fehler (Not Found) harmlos wirkt, summieren sich diese Scans schnell zu einem handfesten Problem:

  1. Ressourcenverbrauch: Jeder Request muss vom Apache-Webserver entgegengenommen, verarbeitet und protokolliert werden. Bei aggressiven Bot-Wellen bindet dies CPU und RAM. Zudem wird je nach Konfiguration für jede dieser Anfragen ein Datenbankeintrag in der sys_log Tabelle erzeugt, was zusätzliche Serverlast und Resourcenverbrauch zur Folge hat.
  2. Log-Spamming: Die Auswertung von Access- und Error-Logs wird massiv erschwert, wenn legitime Fehler in tausenden Zeilen von automatisiertem Müll untergehen.
  3. Erhöhtes Sicherheitsrisiko: Findet ein Bot zufällig eine ungeschützte Schwachstelle (z. B. eine vergessene Backup-Datei oder ein altes Skript), erfolgt die Kompromittierung vollautomatisch in Sekundenbruchteilen.

Beispiel: Logdateien auf einem Server, welcher eine TYPO3 Instanz hostet: Der Sicherheits-Grundsatz lautet: Hier gibt es absolut keinen legitimen Grund, warum ein Client auf Verzeichnisse wie /wp-content oder /wp-admin oder Dateien wie .env zugreifen sollte. Auch massenhaft Zugriff auf nicht existierende php Dateien sind ein eindeutiges Anzeichen dafür, dass der Server durch Bots gescannt wird. Solche Anfragen sind per Definition als böswillig einzustufen.

Beispieleinträge aus einem apache logfile

 

20.197.180.144 - - [22/Jun/2026:17:18:36 +0200] "GET /wp-admin/css/colors/midnight/about.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:37 +0200] "GET /bgymj.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:37 +0200] "GET /xx.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:37 +0200] "GET /file58.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:37 +0200] "GET /wp-links.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:38 +0200] "GET /solo1.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:38 +0200] "GET /flower.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:38 +0200] "GET /3.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:38 +0200] "GET /orm.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:38 +0200] "GET /ot.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:39 +0200] "GET /sixxis.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:39 +0200] "GET /2P.update.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:39 +0200] "GET /f3027655e14136.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:39 +0200] "GET /25d653587fdfd1.php HTTP/1.1" 401 381 "-" "-"
20.197.180.144 - - [22/Jun/2026:17:18:39 +0200] "GET /f35.php HTTP/1.1" 401 381 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:05 +0200] "GET /wp-admin/ HTTP/1.1" 301 245 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:05 +0200] "GET /wp-admin/ HTTP/1.1" 401 381 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:11 +0200] "GET /wp-admin/wp.php HTTP/1.1" 301 251 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:12 +0200] "GET /wp-admin/wp.php HTTP/1.1" 401 381 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:23 +0200] "GET /wp-admin/js/ HTTP/1.1" 301 248 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:23 +0200] "GET /wp-admin/js/ HTTP/1.1" 401 381 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:27 +0200] "GET /wp-admin/js/index.php HTTP/1.1" 301 257 "-" "-"
20.104.244.165 - - [23/Jun/2026:02:42:27 +0200] "GET /wp-admin/js/index.php HTTP/1.1" 401 381 "-" "-"

 

Präventionsmöglichkeiten: Proaktives Blockieren auf Netzwerkebene

Um die Last vom Webserver zu nehmen und Angreifer frühzeitig zu isolieren, reicht das Ausliefern einer Fehlerseite nicht aus. Die effektivste Lösung ist das dynamische Blockieren auf Netzwerkebene (Firewall) mithilfe eines Log-Analysators wie Fail2ban.

Fail2ban überwacht die Logfiles in Echtzeit, erkennt verdächtige Verhaltensmuster anhand von regulären Ausdrücken (Regex) und sperrt die Verursacher-IP temporär via UFW (Uncomplicated Firewall), iptables oder nftables.

Konkrete Umsetzung: Filter und Jail-Konfiguration

1. Der Filter: Beispiel in /etc/fail2ban/filter.d/apache-phpscan.conf

Dieser Filter sucht im Access-Log nach zwei Mustern:

  • Aufrufe von PHP-Dateien, die fehlschlagen (HTTP-Statuscode ungleich 200, z. B. 403, 404, 301, 302).
  • Explizite Zugriffe auf hochsensible Pfade (WordPress-Strukturen, Umgebungsvariablen, Exchange Autodiscover), unabhängig vom gelieferten Statuscode. Da wir diese Pfade nicht bedienen, wird jeder Aufruf als bösartiger Scan eingestuft. Die Regular Expressions müssen möglicherweise jeweils an das Logformat angepasst werden, vor allem wenn ein anderer Webserver / Proxy wie nginx modifizierte Logdateien verwendet werden.
[Definition]
failregex = ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) \S*\.php\S* HTTP/\d+(?:\.\d+)*" (?:30[12]|40[0134])
            ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) /+wp-content\S* HTTP/\d+(?:\.\d+)*" \d+
            ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) /+wp-admin\S* HTTP/\d+(?:\.\d+)*" \d+
            ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) /+wp-includes\S* HTTP/\d+(?:\.\d+)*" \d+
            ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) /+\.env\S* HTTP/\d+(?:\.\d+)*" \d+
            ^<HOST> - - \[.*\] "(?:GET|POST|HEAD) (?i)/+autodiscover/autodiscover\.xml\S* HTTP/\d+(?:\.\d+)*" \d+
ignoreregex =

 

2. Das Jail: /etc/fail2ban/jail.d/apache-phpscan.local

Das zugehörige Jail definiert die Log-Pfade und die Härte der Sperre. In unserem Beispiel: Erreicht eine IP innerhalb von 10 Sekunden (findtime) mehr als 5 Treffer (maxretry), wird sie für 5 Stunden (bantime = 18000) komplett über einen Eintrag in der Firewall ausgesperrt.

 

[apache-phpscan]
enabled  = true
port     = http,https
filter   = apache-phpscan
logpath  = /var/log/httpd/access_log
          /var/log/httpd/another_access_log
backend = auto action = iptables-multiport[port="http,https"] findtime = 10 maxretry = 5 bantime = 18000

 

Fazit & Best Practice

Mit minimalem Konfigurationsaufwand sorgt dieses Fail2ban-Setup dafür, dass automatisierte Scanner nach wenigen Requests gegen eine digitale Wand laufen. Das schont die Ressourcen des Apache-Webservers und hält die Logfiles sauber.

Wichtiger Praxistipp vor dem Livegang: Teste deine Konfiguration immer vorab mit dem integrierten Fail2ban-Tool gegen deine echten Logfiles, um sicherzustellen, dass die regulären Ausdrücke fehlerfrei matchen:

 

fail2ban-regex /var/log/httpd/access_log /etc/fail2ban/filter.d/apache-phpscan.conf

 

Sonstige hilfreiche Hinweise:

 

# Liste aller durch den Fail2Ban Filter erzeugen Einträge
iptables -L f2b-apache-phpscan -n -v

# Status und Statistiken des Fail2Ban Filters ausgeben
fail2ban-client status apache-phpscan