Credential-Portknocking, Teil 2: Integration in SSH-Clients

Der zweite Teil des Tutorials beschäftigt sich mit verschiedenen Arten der Integration des Portknockings in die beiden beliebtesten SSH-Clients, OpenSSH und PuTTY, jeweils unter Linux und Windows, wodurch das Anklopfen während des Verbindungsaufbaus automatisiert wird.

· 9 Minuten zu lesen
Credential-Portknocking, Teil 2: Integration in SSH-Clients
🇬🇧
This article is also available in English.
ℹ️
Dieser Artikel ist Teil eines mehrteiligen Tutorials:
Teil 1: Implementierung in der Linux-Kernelfirewall
Teil 2: Integration in SSH-Clients
Teil 3: Noch nicht veröffentlicht.

Zum Ende des ersten Teils des Tutorials waren wir in der Lage, für den Verbindungsaufbau zum Server dessen SSH-Dienst durch Anklopfen manuell in der Firewall freizugeben. Diesen zusätzlichen Arbeitsschritt gilt es nun einzusparen, indem das Anklopfen in den SSH-Client integriert wird.

Dabei betrachten wir Beispiele für verschiedene Integrationsmethoden für die beiden beliebtesten SSH-Clients unter Linux (und anderen Unix- und Unix-artigen OS) sowie Windows; OpenSSH und PuTTY.

Wie auch im vorhergehenden Teil senden wir das Credential lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9 als UDP-Datagramm an Port 34567, um den SSH-Dienst des Servers server.schoen-technisch.de freizugeben.

Integration in OpenSSH

Der Client von OpenSSH sieht nicht direkt die Integration von Befehlen vor, die lokal vor dem Verbindungsaufbau ausgeführt werden sollen. Es gibt jedoch zwei Funktionen, die sich für unsere Anwendung zweckentfremden lassen:

  • Die Konfigurationsdirektive Match bietet das Kriterienschlüsselwort exec, das eigentlich dazu dient, den Rückgabewert eines Befehls als Kriterium zu verwenden. Innerhalb des Befehls ist u.a. der Platzhalter %h verfügbar, welcher zum Hostname des Servers expandiert.
  • Die Konfigurationsdirektive Host bietet die Subdirektive ProxyCommand, mit dem sich ein Befehl definieren lässt, über den die SSH-Verbindung mittels stdin und stdout aufgebaut werden soll. Im Befehl stehen u.a. die Platzhalter %h für den Hostname sowie %p für den SSH-Port des Servers zur Verfügung. Da sich Befehle verketten lassen, können wir die im vorhergehenden Teil beschriebenen Verbindungsproxys verwenden, um in einem ersten Aufruf das UDP-Datagramm mit dem Credential abzusetzen, und mit einem zweiten Aufruf stdin/stdout mit einer TCP-Verbindung zum SSH-Dienst des Servers zu verbinden.

Konfigurationsdateien des OpenSSH-Clients

Alle hier beschriebenen Konfigurationsoptionen sollen in eine Konfigurationsdatei des OpenSSH-Clients aufgenommen werden. Der OpenSSH-Client greift dabei auf eine systemweite sowie eine benutzerspezifische Konfigurationsdatei zu.

Die systemweite Konfigurationsdatei findet sich bei Linux-Systemen üblicherweise unter /etc/ssh/ssh_config, auf Windows-Systemen findet man sie unter %PROGRAMDATA%\ssh\ssh_config. Die benutzerspezifische Konfigurationsdatei hingegen ist unter Linux mit dem Pfad ~/.ssh/config bzw. unter Windows mit dem Pfad %USERPROFILE%\.ssh\config zu lokalisieren.
Mit Ausnahme spezieller Mehrbenutzerumgebungen ist es empfehlenswert, die benutzerspezifische Konfigurationsdatei zu verwenden.

Alternativ ist es auch möglich, eine ganz andere Konfigurationsdatei zu verwenden, und diese beim SSH-Aufruf mit -F anzugeben, oder aus einer der Standardkonfigurationsdateien heraus mittels Include-Direktive aus einer anderen Datei zu laden. Dies ist beispielsweise sinnvoll, wenn die Datei mit den Credentials auf einem USB-Stick, in einem verschlüsselten Verzeichnis, o.ä. abgelegt werden soll.

Integration per Match exec

Diese Methode eignet sich besonders für Nutzer von Linux und anderen Unix- und Unix-artige OS, die die Bash-Shell nutzen bzw. deren System diese mitbringt, da wir so gänzlich ohne einen TCP-Proxy arbeiten können.

Mit der folgenden Konfiguration klopft OpenSSH bei einem Verbindungsaufbau zum Server server.schoen-technisch.de mit einem UDP-Datagramm mit dem Credential lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9 an Port 34567 an:

Match final host server.schoen-technisch.de exec "echo -n 'lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9' >/dev/udp/%h/34567"

SSH-Clientkonfiguration für vollautomatisches Anklopfen beim Verbindungsaufbau mit Match exec unter Linux/Unix.

OpenSSH unterstützt interne Umschreibungen und Kanonisierung von Hostnames. Dies lässt sich beispielsweise auch für Aliase einsetzen. Das Kriterienschlüsselwort final sorgt dafür, dass die Match-Direktive nur im letzten Schritt der Auflösung des Hostnames zum Tragen kommt, denn anderenfalls würde ggf. mehrfach beim Server angeklopft.
Da es sinnvoll ist, für jeden Server ein eigenes Credential zu verwenden, wird die Match-Direktive mittels eines host-Kriteriums auf einen bestimmten Hostname beschränkt, bevor mit dem exec-"Kriterium" das eigentliche Anklopfen durchgeführt wird.

Ab sofort sollte somit die Verbindung zum Server auch ohne manuelles Anklopfen funktionieren:

$ ssh user@server.schoen-technich.de
Last login: Thu May 30 12:14:31 2024 from 10.0.0.2
[user@server ~]$ 

Erfolgreiche SSH-Verbindung mit automatischem Anklopfen.

In Umgebungen, in denen Bash zwar auf dem System vorhanden, nicht aber die Standard-Shell des Nutzers ist, kann bash -c verwendet werden, um den Befehl innerhalb einer Bash-Subshell auszuführen.
Alternativ können die im ersten Teil des Tutorials vorgestellten Tools OpenBSD netcat, Nmap ncat oder socat zur Übertragung des Credentials als UDP-Datagramm eingebunden werden.

Integration per ProxyCommand

Diese Methode eignet sich besonders für alle Fälle, in denen mit einem Anklopf-Tool gearbeitet werden soll, das gleichzeitig auch als TCP-Proxy verwendet werden kann, oder wenn eine Konfigurationsdatei bereits Host-Knoten verwendet, um serverspezifische Einstellungen festzulegen.

Dabei werden in der Konfigurationsdatei zunächst Host-Knoten je Server angelegt, der dann Subdirektiven hinzugefügt werden. Eine dieser Subdirektiven ist ProxyCommand, die wir zweckentfremden. Sie ist eigentlich nur für die Anbindung eines Proxy-Tools für den Aufbau der SSH-Verbindung anstelle der ssh-eigenen Netzwerkfunktionalität gedacht, sodass wir hier im Anschluss an das Anklopfen die Netzwerkverbindung zum Server über ein Tool als TCP-Proxy aufbauen müssen, welcher stdin und stdout per TCP mit dem SSH-Port des Servers verbindet. Der Anklopf- und der Proxy-Befehl werden dazu in den üblichen Linux-/Unix-Shells einfach mittels ; getrennt – unter Windows ist es etwas weniger straight-forward, dazu später mehr. Der Platzhalter %h wird dabei von OpenSSH durch den Hostname des Servers ersetzt, und %p durch den SSH-Port.

OpenBSD netcat unter Linux/Unix

Der resultierende OpenSSH-Konfigurationseintrag für Portknocking und Proxying mittels OpenBSD netcat unter Linux/Unix sieht dann wie folgt aus:

Host server.schoen-technisch.de
  ProxyCommand echo -n "lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9" | netcat -u "%h" 34567; netcat "%h" "%p"

SSH-Clientkonfiguration für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels OpenBSD netcat unter Linux/Unix.

Bei Bedarf kann der Host-Knoten zusätzlich auch noch um weitere Konfigurationsoptionen (man 5 ssh_config) ergänzt werden, bspw. User zur Angabe des Benutzernamens auf dem Server, Port zur Angabe eines abweichenden SSH-Ports, oder Hostname zur Angabe des Server-Hostnames oder seiner IPv4- oder IPv6-Adresse. Die Angabe von Hostname macht zum Beispiel dann Sinn, wenn man unter Host statt des vollständigen Hostnamen des Servers lieber ein Kürzel oder anderen Alias verwenden möchte, sodass ein Verbindungsaufbau mittels ssh <kürzel> möglich ist. Das selbe gilt, wenn man mit Hostname eine IP-Adresse statt eines Hostnames verwenden möchte, um von funktionierender DNS-Auflösung unabhängig und vor möglichen DNS-basierten Angriffen geschützt zu sein.

Falls beim Aufbau aller SSH-Verbindungen unter Verwendung der SSH-Konfigurationsdatei ein Anklopfen am selben UDP-Port mit dem selben Credential stattfinden soll, kann statt Host <hostname|kürzel> auch Host * verwendet werden, um die folgenden Subdirektiven für alle Verbindungen zu setzen.

Nmap ncat unter Linux/Unix

Um Nmap ncat statt OpenBSD netcat als Anklopf-Tool und TCP-Proxy zu verwenden, sieht das Schnipsel in der SSH-Clientkonfiguration so aus:

Host server.schoen-technisch.de
  ProxyCommand echo -n "lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9" | ncat --send-only -u "%h" 34567; ncat "%h" "%p"

SSH-Clientkonfiguration für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels Nmap ncat unter Linux/Unix.

socat unter Linux/Unix

Für socat hingegen kann ein Konfigurationsschnipsel dieser Art verwendet werden:

Host server.schoen-technisch.de
  ProxyCommand echo -n "lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9" | socat -t 0 STDIO "UDP-SENDTO:%h:34567"; socat STDIO "TCP:%h:%p"

SSH-Clientkonfiguration für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels socat unter Linux/Unix.

Bei socat ist darauf zu achten, dass bedingt durch die Verwendung von : in der Adresssyntax die Verbindung zu IPv6-Adressen, unabhängig davon ob über der Befehlszeile angegeben oder mittels Hostname-Subdirektive festgelegt, nicht ohne Anpassungen möglich ist. Soll direkt an einer IPv6-Adresse angeklopft werden, ist die Angabe des Platzhalters %h in beiden Befehlen der Befehlskette durch [%h] zu ersetzen.

Nmap ncat unter Windows

Seit Windows 10 Release 1803 ist OpenSSH fester und standardmäßig installierter Bestandteil von Windows. Allerdings ist die Erzeugung von UDP-Datagrammen und TCP-Proxying nur eher umständlich mit Bordmitteln wie Powershell zu bewerkstelligen. Ncat aus dem Windows-Build von Nmap ist jedoch (anders als diverse andere netcat-Portierungen für Windows) gut gepflegt und uneingeschränkt zu empfehlen.

Unter Linux/Unix ist das Wrapping des ProxyCommand-Befehls optional und nur bei Verwendung einer sehr speziellen Shell notwendig. Unter Windows wird der Befehl jedoch nicht mit einem Standard-Befehlsinterpreter ausgeführt, sodass das Wrapping notwendig ist – in unserem Fall mit cmd /c, da dieser Befehlsinterpreter in allen Installationen verfügbar sein sollte.

Leider unterstützt der echo-Befehl von cmd keinen Parameter, der das Line Feed (bzw. bei Windows Carriage Return + Line Feed) am Zeilenende unterdrückt, sodass man sich eines Hacks bedienen muss; wir setzen mit set /p eine anonyme Variable und geben ihren Inhalt aus, dabei muss zur Bestätigung an set entweder ein Line Feed an stdin übergeben, oder stdin durch ein Pipe-In von NUL geschlossen werden. Das Verketten den Portknocking-Befehls und TCP-Proxying-Befehls erfolgt bei cmd mittels &.

Host server.schoen-technisch.de
  ProxyCommand cmd /c "echo | set /p="lduiSyvSwYZzwp9OfPiMtVRYj9n6R7e9" | ncat --send-only -u "%h" 34567 & ncat "%h" "%p""

SSH-Clientkonfiguration für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels Nmap ncat unter Windows.

Einschränkung bei Verwendung von ProxyCommand

Die Integration des Anklopf-Befehls in die SSH-Konfiguration geht einher mit einer kleinen Einschränkung: Die SSH-Parameter -4 und -6 bzw. die OpenSSH-Konfigurationsoption AddressFamily zur Festlegung der Adressfamilie für den Verbindungsaufbau auf IPv4 oder IPv6 zeigt keine Wirkung mehr, da die Namensauflösung des Hostnames und die Auswahl der Adressfamilie nun vom verwendeten Proxy-Tool abhängt, und nicht mehr von SSH. Dies lässt sich auf zweierlei Art umgehen:

  • Möglichkeit 1: Das verwendete Anklopf-/Proxy-Tool selbst wird angewiesen, das Datagramm bzw. die Verbindung per IPv4 oder IPv6 zu senden/aufzubauen. Bei allen drei hier vorgestellten Tools (netcat, ncat und socat) wird dies über die Befehlszeilenparameter -4 bzw. -6 gelöst.
  • Möglichkeit 2: Die Verbindung wird explizit zur IPv4- oder IPv6-Adresse des Servers aufgebaut. Dabei kann man die jeweilige IP-Adresse auch der Host-Sektion des Servers als Hostname-Direktive hinzufügen.
    Nur bei Verwendung von socat ist dabei auf die im zugehörigen Abschnitt oben beschriebene Einschränkung bei der Angabe von IPv6-Adressen zu berücksichtigen.

Integration in PuTTY

Der beliebteste grafische SSH-Client ist PuTTY, und auch mit diesem Client lässt sich die Proxy-Funktion analog zu OpenSSH für unser Portknocking zweckentfremden.

Dazu wird in der Kategorie ConnectionProxy unter Proxy type der Modus Local (run a subcommand to connect) ausgewählt und unter Command to send to proxy (for some types) dann unser aus den vorhergehenden OpenSSH-Beispielen bekannter ProxyCommand-Befehl eingetragen, welchen wir noch wie folgt anpassen müssen:

Den Hostname des SSH-Servers ersetzt PuTTY im Proxy-Befehl mit dem Platzhalter %host statt %h, den Port mit dem Platzhalter %port statt %p. Zusätzlich kennt PuTTY aber auch weitere Eingabefelder mit eigenen Platzhaltern, hier bspw. Proxy hostname und Port, welche wir für unser Credential und den für das Anklopfen zu verwendenden UDP-Port verwenden können. Auf diese Daten können wir über die Platzhalter %proxyhost und %proxyport zugreifen.

Über das Sitzungsmanagement von PuTTY können die Einstellungen dann gespeichert werden, entweder als Standardeinstellungen, oder (sinnvollerweise ergänzt um Hostname des Servers und ggf. Benutzername und Port) als serverspezifische Sitzung.

OpenBSD netcat unter Linux/Unix

Der resultierende Befehl mit OpenBSD netcat für Unix/Linux sieht dann so aus:

echo -n "%proxyhost" | netcat -u "%host" "%proxyport"; netcat "%host" "%port"

PuTTY-Proxybefehl für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels OpenBSD netcat unter Linux/Unix.

Die Befehlsketten mit Nmap ncat und socat aus den o.s. ProxyCommand-Direktiven für OpenSSH lassen sich bei Bedarf analog umformen.

Für unseren Beispielserver ergibt sich dann für PuTTY unter Linux/Unix diese Konfiguration:

Nmap ncat unter Windows

Analog zur Umschreibung des OpenBSD-netcat-ProxyCommands für PuTTY unter Linux/Unix sieht das für PuTTY angepasste ProxyCommand für Nmap ncat unter Windows entsprechend so aus:

cmd /c "echo | set /p="%proxyhost" | ncat --send-only -u "%host" "%proxyport" & ncat "%host" "%port""

PuTTY-Proxybefehl für vollautomatisches Anklopfen beim Verbindungsaufbau sowie Proxying mittels Nmap ncat unter Windows.

Das Ergebnis für unseren Beispielserver mit PuTTY unter Windows ist das folgende:

Rück- und Ausblick: Credential in andere Protokolle integrieren

In diesem zweiten Teil des Tutorials haben wir nun erarbeitet, wie einfach sich unser Credential-Portknocking in SSH-Clients integrieren lässt, selbst wenn diese eigentlich keine Funktion dafür vorsehen, und können uns nun trotz Portknocking wieder wie gewohnt ohne Zusatzschritte bei unserem Server anmelden.

Im dritten und letzten Teil der Serie werden wir lernen, wie das des Credentials sich unauffällig auch in Datagramme anderer Protokolle wie ICMP Echo Requests (Ping) oder SNMP-Abfragen integrieren lässt. Auf diese Weise lässt sich das Anklopfen unauffälliger gestalten und eventuell vorgeschaltete Firewalls ohne zusätzliche Portfreigabe durchqueren.


Vorheriger Teil: Credential-Portknocking, Teil 1: Implementierung in der Linux-Kernelfirewall

Credential-Portknocking in Linux-Kernelfirewall
Implementierung mittels nftables oder iptables direkt in der Linux-Kernelfirewall.