Blog

Basis-Absicherung für den eigenen Server (2/2)

In dem ersten Artikel zum Bereich Server-Härtung wurde bereits das Thema „Angriffsfläche“ besprochen. In diesem Artikel wird der Zugriff auf den Server sowie die Kontrolle von Ereignissen und die Sicherung von Daten behandelt. Abschließend betrachten wir den Server kurz aus der Sicht des Angreifers.

Zugriff

Die Betreiber eines Servers müssen nach wie vor Zugriff auf diesen haben, um Wartungsarbeiten, Änderungen und weitere Aufgaben durchführen zu können. Hierbei kann man grundsätzlich zwischen physischem und logischem Zugriff unterscheiden.

Der Server muss, wenn möglich, für Unbefugte unzugänglich gelegen sein, somit kann eine Vielzahl an Angriffen auf die Hardware verhindert werden.

Um gespeicherte Daten in Datenbanken oder Dateisystemen auch vor Offine-Angriffen zu schützen, sollte diese unbedingt verschlüsselt sein. Wie sichere Verschlüsselung aus Applikationssicht aussieht, lernen Sie in diesem Blogartikel. Etwas spezifischere, verschlüsselte Passwortspeicherung wird in diesem Artikel behandelt. Zusätzlich kann auch das Speichermedium selbst verschlüsselt werden. Hierfür sind Lösungen wie VeraCrypt oder Logical Volume Manager (LVM) und Linux Unified Key Setup (LUKS) empfehlenswert.

Jede Person, die auf den Server Zugriff haben muss, benötigt einen personalisierten Account. Vom Einsatz von Gruppen-Accounts sollte wenn möglich abgesehen werden. Dies erleichtert die Nachvollziehbarkeit von Ereignissen und lässt eine bessere Trennung von Rechten zu. Passwörter sollten einer bestimmten Komplexität und hoher Entropie entsprechen. Eine angemessene Passwortrichtlinie ist stark von dem Verwendungszweck abhängig.

Grundsätzlich sollten mindestens 12 Zeichen, Groß- und Kleinbuchstaben, Ziffern und Sonderzeichen enthalten sein. Die gewählten Passwörter sollten auch nicht auf Wörtern basieren, die in Wörterbüchern zu finden sind, oder im Zusammenhang mit der Applikation oder dem Benutzer stehen.

Am einfachsten ist für die Erstellung und Speicherung von sicheren Passwörtern ein Passwortmanager wie z.B. KeePass. Es existieren zahlreiche Passwortmanagerlösungen mit verschiedensten Funktionalitäten. Prinzipiell sind offline-Passwortmanager vorzuziehen, da diese nur lokal und nicht in der ominösen Cloud gespeichert werden – also auf Computern anderer Leute.

Alternativ können natürlich auch sicherere Verfahren verwendet werden, beispielsweise Public-Key-Authentifizierung für SSH oder Software für Einmal-Passwörter (OTP).

Das Prinzip der geringsten Rechte sowie der Grundsatz aus dem ersten Artikel – dass nur eine Aufgabe ausgeführt werden soll, diese aber gut – ist auch für den Zugriffsschutz geltend. Benutzer sollten nach Aufgaben getrennt werden und Benutzer mit unnötigen Rechten sollten vermieden werden. Beispielsweise sollte ein eigener Benutzer erstellt werden, der den Python-Webserver startet und nur die Rechte erhält, die dafür nötig sind. Für die Beispiele in diesem Artikel wurde der Benutzer „pyserver“ erstellt.

useradd -m pyserver
passwd pyserver

Benutzer

Im Falle der Kompromittierung eines Dienstes (z.B. Python-Webserver) hätte ein Angreifer somit ausschließlich die Rechte des ausführenden Benutzers. Schäden können somit abgeschwächt und das restliche System vor dem Angreifer geschützt werden. Der Benutzer, der den Python-Webserver (in diesem Beispiel manuell) startet, muss grundsätzlich nur in das zu bereitzustellende Verzeichnis (~/data) wechseln können und Python3 starten dürfen.

Eine grundsätzliche Einschränkung des Benutzers kann über eine restriktive Shell (rbash) erreicht werden. Dem Benutzer kann diese restriktive Shell über die passwd-Datei zugewiesen werden.

vi /etc/passwd
[…]
pyserver:x:1001:1001::/home/pyserver:/bin/rbash
[…]

Die in der Quelle angeführten Limitationen würden es dem Benutzer aber z.B. nicht mehr erlauben, in das bereitzustellende Verzeichnis zu wechseln. Dies kann jedoch über das .bashrc-Skript ausgeführt werden. Somit wechselt die Shell nach der Anmeldung des Benutzers in das bereitzustellende Verzeichnis.

echo „cd data“ >> /home/pyserver/.bashrc

Um die ausführbaren Befehle für den Benutzer zu definieren, kann das Shell-Profil editiert werden, das bei Login des Benutzers ausgewertet wird.

Als privilegierter Benutzer wird der Pfad (PATH) für ausführbare Dateien in diesem Profil geändert. Das Ziel ist es, einen kontrollierten Pfad zu definieren, der nur Verweise auf ausführbare Dateien enthält, die effektiv von dem Benutzer benötigt werden.

vi /home/pyserver/.profile
[…]
PATH="$HOME/bin"

In diesem Verzeichnis werden alle ausführbaren Dateien verlinkt, die vom Benutzer benötigt werden (python3). Danach werden dem Benutzer alle Rechte entzogen, die es ermöglichen könnten, diese Einschränkungen zu verändern.

ln -s /usr/bin/python3 /home/pyserver/bin/python3
chown root:root /home/pyserver/bin
chmod 755/home/pyserver/bin
chmod 755/home/pyserver/.profile
chmod 755/home/pyserver/.bashrc

Meldet sich der Benutzer nun am System an, befindet sich dieser in der restriktierten Shell (rbash), in dem bereitzustellenden Verzeichnis und hat zusätzlich nur die definierten Befehle (python3) zur Verfügung.

Die Einschränkung durch die restriktive Shell gilt nur für Anmeldungen über SSH oder am Terminal, der Benutzer ist dadurch effektiv nur in den Szenarien eingeschränkt. Für tiefergehende Schutzmaßnahmen können Lösungen wie SELinux oder Grsecurity eingesetzt werden.

Außerdem ergeben sich durch den Zugriff auf Python3 für den Benutzer weitere Möglichkeiten, auf das System zuzugreifen. Mehr dazu in dem Abschnitt „Aus der Sicht des Angreifers“.

Diese Konfiguration ist nur für diesen speziellen Fall so umsetzbar und muss der Applikationsumgebung angepasst werden, um die Ausführung der geplanten Aufgaben reibungslos zu ermöglichen.

Capabilities

Mit Capabilities wird einem die Möglichkeit geboten, einem Benutzer bestimmte privilegierte Rechte zu geben, ohne diesem aber komplett root-Rechte zu erlauben. Dadurch könnte man es einem Benutzer beispielsweise erlauben, einen Webserver auf Port 80 zu betreiben, was im Normalfall root-Rechte benötigt, ohne diesem effektiv root-Rechte zuzuteilen. Eine tiefergehende Einführung zu Linux Capabilities finden Sie hier.

SSH

Für externe Zugriffe auf den Server wird normalerweise die Secure SHell (SSH) verwendet. Um diesen Zugriff möglichst sicher zu gestalten, werden mehrere Anpassungen vorgenommen.

Im ersten Schritt werden die Benutzer limitiert, die über SSH zugreifen dürfen. Diese könne entweder über die Optionen AllowUsers oder AllowGroups definiert werden.

In der Konfigurationsdatei /etc/ssh/sshd_config werden diese definiert. Soll beispielsweise der Benutzer pyserver (und der Benutzer serveradmin) über SSH Zugriff haben, kann dieser mit der folgenden Konfiguration erlaubt werden. Grundsätzlich sollte der root-Benutzer keinen Zugriff über SSH haben – Benutzer, die root-Rechte über SSH benötigen, sollten diese über den sudo-Befehl nach der Anmeldung erlangen. Aus diesem Grund wird dem root-Benutzer der Zugriff über SSH verweigert.

vi /etc/ssh/sshd_config
[…]
AllowUsers pyserver serveradmin
PermitRootLogin no
[…]

Public Key Authentication

Auch wenn sicher gestaltete Passwörter für die Anmeldung über SSH verwendet werden, sollte die Public Key-Authentifizierung vorgezogen werden. Herkömmliche Angriffe für SSH-Anmeldungen wie Bruteforce Angriffe werden somit extrem erschwert bzw. unmöglich gemacht. Für den Server sollten neue öffentliche Schlüssel erstellt werden, damit sichergestellt werden kann, dass diese nicht bei der Installation mit nicht ausreichend zufälligen Werten generiert wurden. Die existierenden Schlüssel können nach der Generierung der neuen Schlüssel entfernt werden.

ssh-keygen -t ed25519 -f ssh_host_ed25519_key -N ""
ssh-keygen -t rsa -b 4096 -f ssh_host_rsa_key -N ""

In der SSH-Konfiguration auf dem Server werden diese Dateien dann angegeben:

vi /etc/ssh/sshd_config
[…]
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

Auf dem Client werden ebenso Schlüsselpaare erstellt.

ssh-keygen -t ed25519 -o -a 100
ssh-keygen -t rsa -b 4096 -o -a 100

Die öffentlichen Schlüssel (z.B. id_rsa.pub) müssen dann in den erlaubten Schlüssel für den Benutzer (~/.ssh/authorized_keys) am Server eingetragen werden. Hierfür wird einfach der Dateiinhalt des öffentlichen Schlüssels kopiert und am Server eingefügt. Alternativ kann das Tool ssh-copy-id verwendet werden. Für den Benutzer „pyserver“ ist dies nicht möglich, da dieser durch die rbash zu stark eingeschränkt ist.

ssh-copy-id benutzer@server

Nachdem alle Schlüsselpaare erstellt wurden, die SSH-Konfiguration dahingehend angepasst wurde und die öffentlichen Schlüssel für die SSH-Benutzer am Server registriert wurden, kann die Passwortauthentifizierung deaktiviert werden und der SSH-Dienst neugestartet werden. Sicherheitshalber sollte dieser Schritt ausgeführt werden, wenn auch physischer Zugriff zum Server möglich ist, falls bei der Konfiguration ein Fehler aufgetreten ist und Zugriff über SSH verweigert wird.

vi /etc/ssh/sshd_config
[…]
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
[…]
service ssh restart

Eine detailliertere Einschränkung der Schlüsselaustauschfunktionen, Verschlüsselungsalgorithmen und Hashfunktionen für SSH kann ebenfalls in der /etc/ssh/sshd_config-Datei definiert werden. Diese wird in diesem Artikel jedoch nicht tiefergehend besprochen. Details dazu finden Sie in diesem Artikel.

Kontrolle und Sicherung

Es ist auch nach einer grundsätzlichen Absicherung des Servers essentiell, Geschehnisse darauf im Auge zu behalten. Logdateien sind stark applikationsabhängig, aber generelle Logdateien wie Authentifizierungslogs (/var/log/auth.log) sollten ebenso betrachtet werden.

Das Tool logwatch ist hierbei hilfreich, indem es Logs definierter Dienste sammelt und per E-Mail verschickt. Die Konfiguration wird in diesem Artikel jedoch nicht weiter behandelt. Eine grundsätzliche Konfigurationshilfe kann hier gefunden werden.

OSSEC ist ebenso ein sehr hilfreiches Werkzeug, um den Server zu überwachen. Das Tool erlaubt das Beobachten von Log-Dateien, Prozessen und weiteren Vektoren.

Ebenso wichtig, wie über Ereignisse auf dem Server Bescheid zu wissen, ist es, im Notfall eine Sicherheitskopie von Konfigurationen und Daten zu haben. Eine regelmäßige Sicherung der wichtigsten Dateien und Dokumente ist unumgänglich und muss dem Verwendungszweck angepasst sein. Aus diesem Grund wird das Thema in diesem Artikel nicht weiter behandelt. Es ist auch dringend sicherzustellen, dass die Wiederherstellung der Daten aus dem Backup auch funktioniert. Regelmäßige Restore-Tests sollten deswegen durchgeführt werden. Außerdem ist darauf zu achten, dass die Backups auch nicht mit Zugriff auf den Server manipuliert werden können. Gerade die neue Crypto-Ransomware-Welle, die auch immer mehr Firmen betreffen, kann man am einfachsten mit einer vernünftigen Backup-Strategie absichern.

Aus der Sicht des Angreifers

Abschließend kann der Server aus der Sicht des Angreifers betrachtet werden. Hier ergeben sich zwei Blickwinkel, nämlich extern und intern.

Auf Netzwerkebene, also extern beginnt ein Angreifer im Normalfall mit einem Scan aller offenen Ports des Zielsystems. Der Angreifer benötigt diese Informationen, damit er weiß, welche Angriffsfläche er zur Verfügung hat. Für diese Aufgabe gibt es einige Tools, u.a. nmap. Im folgenden vereinfacht dargestellten Beispiel wurden alle TCP-Ports mit einem SYN-Scan geprüft und versucht, eventuelle Dienste zu erkennen.

nmap -A -sS -p- -oA server_scan <ZIEL-IP>
[…]
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
[…]
8000/tcp open  http    SimpleHTTPServer 0.6 (Python 3.5.2)
|_http-server-header: SimpleHTTP/0.6 Python/3.5.2
|_http-title: Directory listing for /

Hier spiegelt sich wunderbar die Verringerung der Angriffsfläche wieder. Nur die unbedingt benötigten Ports sind nach außen geöffnet.

Sollte es einem Angreifer gelingen, einen laufenden Dienst (z.B. den Python3-Webserver) zu hacken und sich darüber auf dem System anmelden oder Befehle ausführen zu können, ergibt sich der zweite Blickwinkel – intern.

Um die Möglichkeiten des Angreifers in Bezug auf den Benutzer pyserver zu erforschen, kann man sich einfach mit diesem Benutzer am Terminal oder per SSH anmelden. Die restriktive Shell bietet hier einen gewissen Grundschutz durch die Limitierung der Möglichkeiten. Die restriktive Shell bietet jedoch keinen Schutz, wenn der Angreifer Zugriff auf das System über eine Schwachstelle im Python-Webserver erlangt, da diese für den Benutzer nur bei Anmeldung über SSH oder am Terminal gilt.

Der Benutzer hat jedoch die Möglichkeit, Python3 aufzurufen, was ein immenses Problem darstellt. Über Python oder auch andere Lösungen (e.g. Perl, Ruby, usw.) wird dem Benutzer ein Werkzeug gegeben, viele Einschränkungen zu umgehen. Über Python kann der Benutzer beispielsweise wieder normal Programme aufrufen, Daten auslesen, Verzeichnisse wechseln oder auch einfach eine nicht restriktive Shell aufrufen.

pyserver@server:~/data$ python3
[…]
>>> from subprocess import call
>>> call(["/bin/cat","/etc/issue"])
Ubuntu 16.04.3 LTS \n \l
>>> call(["/bin/bash"])
[…]
pyserver@server:~/data$ /usr/bin/id
uid=1001(pyserver) gid=1001(pyserver) groups=1001(pyserver)
pyserver@server:~/data$ echo $0
/bin/bash

Diese Möglichkeiten müssen während der Planung der Absicherung berücksichtigt werden.

Weiter ist der Benutzer durch die Konfiguration der Firewall eingeschränkt. Sollte Python verwendet werden, um alternative Ports nach außen zu öffnen, werden diese von der Firewall blockiert. Als weitere Möglichkeit hat der Angreifer die restriktive Shell zu umgehen oder fundamentale Schwachstellen im Betriebssystem auszunutzen. Dem kann man wiederum im Normalfall mit aktuellen Sicherheitsupdates entgegenwirken. Für weitergehende Sicherheitskonzepte können SELinux, AppArmor und Grsecurity verwendet werden.

Menü