Hase und Igel…

Hallo zusammen! Wie ihr wisst habe ich seit einem halben Jahr neue Hardware im Einsatz und bei deren Inbetriebnahme viel neu installiert und konfiguriert. Aus heutiger Sicht kann ich sagen: Je mehr man sich damit beschäftigt, desto mehr Möglichkeiten tauchen auf.

Ein Teilbereich dessen, womit ich mich beschäftigt habe, heißt „Fail2Ban“ und es hilft dabei, den Server vor ungebetenen Besuchern zu schützen. Und davon möchte ich Euch ein bisschen erzählen. Denn ein Server wie dieser Blog wird, wenn er nur lange genug mit dem Internet verbunden ist, immer häufiger von Hackern ins Visier genommen. Einerseits wird mittels Skripten versucht, sich anzumelden und Administratorenrechte zu bekommen. Andererseits wird mittels verschiedener Scans und Tests versucht heraus zu finden, welche möglichen Schwachstellen bestehen könnten.

In beiden Fällen kann man versuchen, mit Fail2Ban darauf zu reagieren, das ist aber an zwei Voraussetzungen gebunden:

  • Es werden entsprechende Meldungen in ein Logfile geschrieben
  • Man kann das Logfile so durchsuchen, dass nur genau die problematischen Meldungen identifiziert werden.

Gesunde Paranoia

Ganz grundsätzlich bin ich halbwegs entspannt bei dem Gedanken an die genannten „Auskundschafungen“, weil ich bereits andere Maßnahmen implementiert habe. Alle Zugänge sind mit einer Zwei-Faktor-Authentifizierung in Form eines sich ändernden Codes oder mittels Zertifikat gesichert. Das Passwort erraten reicht also nicht. Andererseits bleibt die Nervosität, dass beim Abklopfen verschiedener Möglichkeiten vielleicht doch mal ein Fehler im Webserver oder der Webapplikation gefunden wird. Ich möchte diese „Besucher“ also möglichst effizient vom System fern halten.

Wie oben geschrieben, die Meldungen müssen dafür erst mal in einem Logfile ankommen und eindeutig identifizierbar sein.

WordPress

Bei WordPress war es lange so, dass man ein separates Plugin installieren musste, um Fehlermeldungen (wie z.B. einen fehlgeschlagenen Anmeldeversuch) sinnvoll zu protokollieren. Dieses Zusammenspiel aus WordPress-Plugin und Fail2Ban hat lange nicht zufriedenstellend funktioniert, sodass viele Betreiber ein weiteres Plugin in WordPress genutzt haben: LimitLoginAttempts. Das ist im Grunde ein Fail2Ban, das innerhalb von WordPress läuft. Dieses Plugin ist auch bei mir noch aktiv. Es hat aber für mich einen entscheidenden Nachteil: Jede Anfrage wird zunächst vom System empfangen, vom Webserver verarbeitet, an den PHP-Stack weiter gegeben und ein WordPress plugin überprüft dann, ob die Anfrage beantwortet wird oder die Adresse in der Sperrliste steht. Es sind also eine ganze Menge Komponenten beteiligt. Eine direkte Behandlung durch Fail2Ban wäre mir lieber, denn eine gesperrte Adresse wird direkt am Anfang in der Firewall blockiert und läuft nicht mehr durch die verschiedenen Softwarekomponenten – weniger Systembelastung und weniger Angriffsfläche.

Kürzlich hat mich das dermaßen genervt, dass ich das Thema noch einmal angegangen bin.

Problem 1a: HTTP 200

Logins (fehlerhafte und erfolgreiche) werden tatsächlich im Logfile des Webservers protokolliert. Dabei unterscheided WordPress zwei „Ergebnis-Codes“: 200 (OK) und 301 (permanently moved). Dummerweise bezieht sich der Code 200, der auch bei jedem normalen Abruf einer Seite erzeugt wird, auf fehlerhafte Logins, während wie Weiterleitung auf das Admin-Backend bei ERFOLGREICHER Anmeldung mit 301 protokolliert wird. Würde ich also den Code 200 blockieren, dann würde ich zusätzlich auch alle regulären Besucher der Webseite mit aussperren.

Problem 1b: Zweifaktorauthentifizierung

Ihr erinnert euch an meinen Kommentar weiter oben, dass alle Zugänge über eine Zwei-Faktor-Authentifizierung abgesichert sind? So auch der WordPress Login. Nachdem man User und Password korrekt eingegeben hat erscheint eine Seite, die den Einmal-Code abfragt. Und hier liegt ein weiteres Problem: DIESER Schritt wird noch mit HTTP 200 bestätigt. Erst wenn man den Einmal-Code erfolgreich gemeistert hat erfolgt wie Weiterleitung in das Admin-Menü mit HTTP 301. Habt ihr es gemerkt? Bei Verwendung von Zwei-Faktor-Authentifizierung kann ich im ersten Moment nicht zwischen einem ungültigen Login (HTTP 200) und einer gültigen weiterleitung zum zweiten Faktor (HTTP 200) unterscheiden. Ich würde mich also am Ende selbt aussperren, wenn ich HTTP 200 blockieren würde.

Noch ein Plugin…

Die Lösung liegt in einem zusätzlichen Plugin, in dem man kleine Code-Schnipsel erstellen und in WordPress ausführen lassen kann. EINES dieser Code-Schnipsel ersetzt den Status-Code von fehlerhaften Logins durch einen anderen Wert, sodass diese eindeutig im Logfile zu identifizieren sind.

Webserver

Nachdem wir die Logins im WordPress soweit im Griff haben, schauen wir uns jetzt den Webserver an. Denn auch hier laufen quasi sekündlich Angriffsmuster. Glücklicherweise muss hier nicht mit irgendwelchen Plugins und Code-Änderungen gearbeitet werden, denn der Webserver ist von Nautur aus ein geschwätziger Geselle. Das Problem ist eher, dass er viele verschiedene Meldungen erzeugt. Aber welche davon sind jetzt relevant?

Wie oben erwähnt, manche Angreifer versuchen durch Ausprobieren auf Schwachstellen zu stoßen. Dazu gehört auch der Versuch, Dateinamen und Verzeichnisse nach bestimmten Schemata zu erraten. Es wird also permanent versucht, irgendwelche Dateien zu öffnen in der Hoffnung, dass auf dem Server z.B. eine bestimmte Anwendung installiert ist oder manche Verzeichnisse nicht vor dem Zugriff durch den Webserver geschützt wurden.

In der Regel erzeugen diese Versuche einen Eintrag mit dem Hinweis „forbidden“, womit der Webserver signalisiert, dass ein Zugriff auf eine angefragte Datei durch Zugriffsbeschränkungen verboten war, oder „does not exist“ – Das gibt es nicht. Nun könnte man sagen: Naja, in beiden Fällen ist ja nix passiert. Im ersten Fall hat das Sicherheitskonzept funktioniert, im zweiten gab es die Datei eh nicht. Stimmt. Aber wenn ich diese Zugriffe nicht abstelle, dann

  • ermögliche ich den Angreifern „auf ewig“ weiter zu probieren und vielleicht doch mal nen Glückstreffer zu landen
  • müllen mir diese Meldungen die Logfiles voll, und in dem Durcheinander übersehe ich vielleicht doch mal eine kritische Meldung. Mir geht es auch um Logfile-Hygiene

Und wie identifizieren wir die relevanten Einträge jetzt?

Regeln und Ausdrücke 😉

Wir haben jetzt also sowohl die WordPress-Logins als auch die Webserver-Meldungen in Logfiles stehen und müssen uns die relevanten Einträge, die zu einer Sperrung führen sollen, heraus fischen. Dafür gibt es „Regular Expressions“, also „Reguläre Ausdrücke“. Das sind Suchmuster, die so beschrieben werden, dass ein Algorithmus effizient und zielgerichtet große Textmengen durchsuchen kann. Die sehen zum Beispiel so aus:

failregex = ^\s*\S+\s+\S+\[\d+\]: Connections: disconnected: <ADDR>::\d+ \(\S+\) \(\[AuthFailure\]

Der Vorteil hierbei ist, dass man eine Kombination aus konkretem Text (z.B. „Connections: disconnected“) mit Platzhaltern für einzelne oder mehrere Zeichen kombinieren und damit möglichst genau die gesuchte Meldung heraus fischen kann. Der Nachteil ist: Selbst ein Punkt oder Schrägstrich zuviel oder zuwenig oder an der falschen Stelle – nix funktioniert mehr.

Glücklicherweise gibt es die Möglichkeit, nach Erstellen der Suchmaske einen Testlauf durch zu führen. Mittels „fail2ban-regex“ kann ein bestimmtes Suchmuster auf ein ausgewähltes Logfile losgelassen werden und man sieht genau, welche Meldungen herausgezogen werden. Glaubt mir, man braucht ein paar Durchläufe – schon alleine weil im Internet verschiedene Beispiele zu finden sind und verschiedene Webserver (nginx, Apache…) bestimmte Meldungen leicht unterschiedlich ausgeben.

Meine erste Annahme war: Einmal Filter definieren und ich habe fertig. Die Realität sieht ein bisschen anders aus – und damit bin ich beim Titel dieses Beitrags:

Ja, die Logfiles werden deutlich weniger „vollgekleistert“, wenn erst mal ein paar Angreifer aussortiert sind. Aber ich entdecke in den verbleibenden Meldungen immer wieder Variationen, die ich ebenfalls blockieren will. Also gehe ich wieder an die Filterdefinition. Und nach 1-2 Tagen, wenn sich alles eingependelt hat und ich wieder mal ins Logfile schaue, dann entdecke ich den nächsten Sonderfall, den ich auch noch abfangen will. So fühlt sich dieser Kreislauf ein bisschen wie Hase und Igel an. Ich denke, ich hab jetzt alles beisammen und dann sehe ich doch noch einen im Logfile nach dem Motto „Ich bin schon (noch) da…“.

Immerhin sieht man anhand der Sperrlisten, dass die investierte Arbeit nicht gänzlich umsonst war:

Über 200 Adressen sind derzeit blockiert…

Und dann ist da noch die andere Frage: Wieviele Versuche (z.B. beim Passwort) will ich zulassen? Ich will mich ja nicht beim ersten Vertipper selber aussperren. Und wenn jemand gesperrt wird, wie lange soll er gesperrt bleiben? Aber das schauen wir uns vielleicht mal in einem anderen Artikel an…

Bloke