IT security, FreeBSD, Linux, mail server hardening, post-quantum crypto, DNS, retro computing & hands-on hardware hacks. Privater Tech-Blog seit 2003.

Schlagwort: SSH (Seite 1 von 2)

OpenPGP-Karte eingerichtet: Curve 25519 scheitert am Kartenlimit, die Unterschlüssel landen trotzdem in Hardware

Moin. Der letzte Beitrag endete mit einem Satz, der mir seitdem im Kopf herumspukt: „Nichts hier braucht eine Smartcard, und nichts hier braucht root.“ Das stand in der Restrisiken-Liste des Ed25519-Beitrags, gleich neben dem Eingeständnis, dass eine Smartcard das größte verbleibende Risiko zwar verkleinern, aber nicht auflösen würde. Genau diese Lücke wollte ich mir ansehen: taugt eine OpenPGP-Karte dafür, den Primärschlüssel offline vorzuhalten, statt ihn nur auf einem Cold-Storage-Stick liegen zu lassen?

OpenPGP-Smartcard mit drei hardwaregebundenen Unterschlüsseln für Signatur, Verschlüsselung und Authentifizierung; der Primärschlüssel bleibt separat offline gespeichert.

Die kurze Antwort: nein, technisch geht das nicht. Eine OpenPGP-Karte kennt genau drei Schlüsselslots, Signatur, Verschlüsselung und Authentifizierung. Für den Zertifizierungsschlüssel, also den, der die Unterschlüssel überhaupt erst beglaubigt, gibt es keinen Slot. Der Primärschlüssel mit der Fähigkeit [C] passt schlicht nicht drauf.

Die längere Antwort ist der eigentliche Grund für diesen Beitrag. Die Karte macht etwas anderes, mindestens genauso viel wert. Sie nimmt die drei Unterschlüssel auf, die du im Alltag tatsächlich benutzt, und macht sie hardwaregebunden. Nicht kopierbar, nicht extrahierbar, während der Primärschlüssel unverändert offline liegen bleibt. Diese Erwartungskorrektur gehört an den Anfang und nicht als Fußnote irgendwo im Text, weil sie der Grund ist, warum dieser Beitrag neben dem Ed25519-Beitrag überhaupt eine eigene Daseinsberechtigung hat.

Weißer Versandumschlag mit Pinguin-Sticker, darunter die Chipkarte mit sichtbarem Goldchip.
So kommt eine OpenPGP-Karte an, hier eine blanko Smart Card V3.4 vom FLOSS-Shop. Der Chip schimmert schon durch den Umschlag.

Das Testsetup

Bevor irgendein echter Schlüssel in die Nähe der Karte kommt, brauchte es eine Umgebung, in der ich ohne Risiko herumprobieren kann. Die Eckdaten:

KarteOpenPGP Smart Card V3.4, ID-1 blanko, FLOSS-Shop, Serie 0000D961, Chip von ZeitControl
ReaderREINER SCT cyberJack pinpad(a), Baujahr 2008
Demo-Schlüsselisoliertes GNUPGHOME, UID „OpenPGP Card Demo card-demo@example.invalid“
Warum isoliertScreenshots dürfen ungeschwärzt raus, und ich wollte mit factory-reset und wiederholten Fehlversuchen experimentieren können, ohne das eigentliche Setup zu gefährden

Der Reader ist derselbe cyberJack pinpad(a), den ich mir vor einer Weile aus Neugier auseinandergenommen hatte. Für diesen Beitrag zählt an ihm eigentlich nur eine Eigenschaft: er hat eine eigene Zifferntastatur samt Display, PINs gehen also nie über die Tastatur deines Rechners und damit auch nie durch dessen Speicher.

Reader-Setup: zwei Fallstricke, bevor überhaupt eine Karte drinsteckt

Der erste Stolperstein kam, bevor ich überhaupt eine Karte eingesteckt hatte. scdaemon, der Smartcard-Daemon von GnuPG, versucht standardmäßig, den internen CCID-Treiber zu benutzen. Der cyberJack spricht aber ein eigenes Vendor-Protokoll über PC-SC, kein CCID, und scdaemon quittiert das mit einem schlichten „No such device“. Die Lösung steht in der Konfigurationsdatei, nicht auf der Kommandozeile:

disable-ccid

Die Zeile gehört in scdaemon.conf. Danach findet gpg den Reader über den PC-SC-Layer, und pcscd übernimmt.

Der zweite Stolperstein trat immer wieder auf, sobald ich scdaemon während der Arbeit neu gestartet oder abgeschossen habe. Der Reader blieb dann als „busy“ hängen, pcscd quittierte jeden Zugriffsversuch mit pcsc_connect: sharing violation. Ein gpgconf --kill scdaemon allein hat das nie behoben. Zuverlässig geholfen hat nur ein Neustart des Dienstes selbst:

systemctl restart pcscd

Beide Punkte sind auf jeden anderen PC-SC-Reader mit eigenem Vendor-Protokoll übertragbar, nicht nur auf den cyberJack, deswegen stehen sie hier als eigener Abschnitt.

REINER SCT cyberJack pinpad(a) Kartenleser mit Zifferntastatur, LEDs und eingesteckter OpenPGP-Karte.
Der cyberJack pinpad(a) mit eingesteckter Karte. Alles, was danach an PIN eingegeben wird, läuft über die Tasten hier und nicht über die Rechnertastatur.

Erster Schritt: die Standard-PINs ändern

Der Karte liegt eine kleine Karteikarte bei, und die erklärt in aller Ruhe, warum der allererste Schritt kein optionaler ist:

Beiliegende Karteikarte mit Standard-PIN 123456 und Admin-PIN 12345678 sowie dem Hinweis auf die Sperrfolgen.
Standard-PIN 123456, Admin-PIN 12345678, bei jeder blanko Karte identisch. Drei falsche PIN-Versuche sperren die Karte, drei falsche Admin-PIN-Versuche löschen die Daten.

Standard-PIN und Admin-PIN sind bei jeder blanko Karte identisch, stehen also öffentlich in jedem Handbuch. Wer sie nicht ändert, hat effektiv gar keinen PIN-Schutz. In gpg --card-edit geht das über admin, gefolgt von passwd:

Pinentry-Dialog mit dem Hinweis, den Admin-PIN und den neuen Admin-PIN über das Pinpad des Kartenlesers einzugeben.
Pinentry zeigt nur den Hinweis, das Pinpad des Readers zu benutzen. Der Admin-PIN selbst taucht auf dem Bildschirm nie auf.
Pinentry-Dialog mit dem Hinweis, den PIN und den neuen PIN über das Pinpad des Kartenlesers einzugeben.
Derselbe Dialog für den normalen PIN. Der Retry-Zähler danach steht bei drei von drei, unverändert gegenüber vorher.

Wichtig an beiden Dialogen ist derselbe Punkt: der eigentliche PIN geht nie über den Rechner, weder alt noch neu. Pinentry zeigt nur einen Hinweis an, die Eingabe passiert komplett am Pinpad. Genau dafür gibt man das Geld für einen Reader mit eigener Tastatur aus, sonst könnte ein kompromittierter Rechner den PIN einfach mitschneiden.

Das erste Kartenlimit: Curve 25519 scheitert

Im jungfräulichen Zustand meldet die Karte für alle drei Slots rsa2048, keine Schlüssel gesetzt:

Terminalausgabe von gpg --card-status im jungfräulichen Zustand, Schlüsselattribute überall rsa2048, keine Schlüssel gesetzt.
Der Zustand direkt nach dem Auspacken. rsa2048 überall, keine Schlüssel, die Referenz für alles, was danach kommt.

Weil der Ed25519-Beitrag genau davon handelte, war meine erste Idee naheliegend: die Kartenattribute per key-attr auf Curve 25519 umstellen, dieselbe Kurve wie beim Primärschlüssel. Curve 25519 steht im Menü sogar als Default. Die Karte lehnt sie trotzdem ab, mit Statuswort 6A80, sowohl für den Signatur- als auch für den Verschlüsselungsslot:

Terminalausgabe von gpg card-edit key-attr, Auswahl von Curve 25519 als Default endet mit Card error.
key-attr bietet Curve 25519 als Default an, aber die Karte quittiert die Umstellung mit Card error.

Tückisch daran: scdaemon meldet bei den ersten beiden Versuchen im Terminal nicht einmal einen klaren Fehler, sondern läuft einfach in die nächste Abfrage weiter. Wer sich auf den Bildschirmtext verlässt statt hinterher mit card-status nachzuschauen, merkt das Scheitern leicht gar nicht. Das FLOSS-Shop-Datenblatt zur Karte bestätigt im Nachhinein, warum: gelistet sind nur „NIST/ANSI“ und „Brainpool“, Curve 25519 taucht in der Aufzählung schlicht nicht auf. Funktioniert haben stattdessen NIST P-384 und Brainpool P-256:

Terminalausgabe von gpg card-edit key-attr, Auswahl von Brainpool P-256 für Signatur- und Verschlüsselungsschlüssel läuft sauber durch, daneben der Pinentry-Dialog für den Admin-PIN.
Brainpool P-256 für Signatur- und Verschlüsselungsslot läuft sauber durch, jede Umstellung verlangt erneut den Admin-PIN am Pinpad.

Für Signatur und Verschlüsselung bin ich bei Brainpool P-256 geblieben, den Authentifizierungsslot habe ich später aus einem eigenen Grund auf NIST P-384 umgestellt. Dazu gleich mehr, erst kommt aber ein zweites Kartenlimit, das mit Kurven gar nichts zu tun hat.

Der Admin-PIN-Timeout: kein Zufall, sondern ein hartes Zeitlimit

Bei den ersten Versuchen mit key-attr schlug die Admin-PIN-Eingabe am Pinpad immer wieder mit Statuswort 6400 fehl, ohne dass der PIN-Retry-Zähler sich bewegte. Mein erster Verdacht war eine Race Condition zwischen scdaemon und dem Reader. Also habe ich mitgestoppt, wie lange die Eingabe am Pinpad tatsächlich dauert, bis das OK gedrückt ist:

EingabedauerErgebnisFälle
3 bis 5 SekundenSW 9000, Erfolg8 von 8
7 bis 16 SekundenSW 6400, Fehler14 von 14

Keine Race Condition also, sondern ein hartes Zeitlimit von etwa fünf bis sechs Sekunden für die PIN-Eingabe am Pinpad selbst. Der PIN-Retry-Zähler bleibt bei einem Timeout unberührt, du verlierst also keinen Versuch, nur Zeit. Einen Konfigurationsschalter dagegen habe ich nicht gefunden, ich habe die komplette Flag-Liste der cyberjack.conf durchsucht. Die Option enable-pinpad-varlen in scdaemon.conf senkt die Fehlerquote spürbar, weil sie variable PIN-Längen am Pinpad erlaubt statt auf eine feste Länge zu warten, behebt das Problem aber nicht vollständig. Praktisch heißt das: PIN vorher im Kopf bereithalten, zügig eintippen, und bei einem Fehlschlag sofort denselben Schritt wiederholen statt lange zu überlegen.

Den Demo-Schlüsselsatz bauen

Mit den bekannten Kartenattributen im Kopf habe ich einen Demo-Schlüsselsatz gebaut, passend zugeschnitten: ein Ed25519-Primärschlüssel mit ausschließlich der Zertifizierungsfähigkeit [C], dazu drei Unterschlüssel in brainpoolP256r1 für Signatur und Verschlüsselung. Der naheliegende Befehl dafür scheitert allerdings:

gpg --quick-add-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926 brainpoolP256r1 sign
gpg: Wrong key usage

Bei NIST- und Brainpool-Kurven kennt der Quick-Befehl offenbar nur ECDH als Default-Verwendungszweck, eine Signaturfähigkeit lehnt er direkt ab. Der Umweg über den ausführlichen Editiermodus funktioniert dagegen anstandslos, dort lässt sich die Kurve explizit wählen und die Fähigkeit im Nachhinein umschalten:

gpg --expert --edit-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926
gpg> addkey
Please select what kind of key you want:
   (11) Existing key
Your selection? 11
Please select which elliptic curve you want:
   (6) Brainpool P-256
Your selection? 6
Possible actions for this ECDH key: Sign Encrypt
Current allowed actions: Encrypt
   (S) Toggle the sign capability
   (Q) Finished
Your selection? S
Your selection? Q

So entstanden, in Reihenfolge, der Verschlüsselungs-, der Signatur- und vorläufig auch der Authentifizierungs-Unterschlüssel, alle drei in Brainpool P-256. Der fertige Satz vor dem eigentlichen Übertragen auf die Karte:

Terminalausgabe von gpg list-secret-keys with-fingerprint, Ed25519-Hauptschlüssel plus drei Brainpool-P-256-Unterschlüssel für Verschlüsselung, Signatur und Authentifizierung.
Ed25519-Primärschlüssel mit reiner Zertifizierungsfähigkeit, darunter drei brainpoolP256r1-Unterschlüssel. Noch alle im lokalen Schlüsselbund, noch keiner auf der Karte.

keytocard für alle drei Unterschlüssel

Das eigentliche Übertragen läuft für jeden Unterschlüssel einzeln über keytocard, mit den üblichen Wiederholungen, wenn der Admin-PIN-Timeout wieder einmal zuschlägt:

gpg --edit-key 7C7A529A359FA9F8A13DF0868AEA158652F8C926
gpg> key 1
gpg> keytocard
Please select where to store the key:
   (2) Encryption key
Your selection? 2
gpg> key 1
gpg> key 2
gpg> keytocard
Please select where to store the key:
   (1) Signature key
Your selection? 1

Verifiziert wird das Ergebnis über gpg --card-status. Alle drei Fingerabdrücke auf der Karte stimmen mit dem lokalen Schlüsselbund überein, und dort steht jetzt ssb> statt nur ssb, also der Hinweis, dass der private Schlüsselteil nicht mehr lokal liegt, sondern nur noch als Verweis auf die Karte:

Terminalausgabe von gpg --card-status mit allen drei Unterschlüsseln auf der Karte, dazu der Schlüsselring-Abgleich mit ssb-Markierung für kartengebundene Schlüssel.
Alle drei Slots besetzt, alle drei Fingerabdrücke stimmen, und ssb> markiert im Schlüsselbund, dass der private Teil nur noch auf der Karte existiert.

Der SSH-Nachtrag: Brainpool kennt SSH nicht

Den Authentifizierungs-Unterschlüssel wollte ich zusätzlich als SSH-Schlüssel benutzen, dafür exportiert gpg-agent ihn über gpg --export-ssh-key. Für den Brainpool-Auth-Unterschlüssel scheitert das mit „Unknown elliptic curve“. Der Grund liegt nicht bei GnuPG: RFC 5656, der SSH-Standard für elliptische Kurven, kennt Brainpool schlicht nicht, nur nistp256, nistp384 und nistp521. Also habe ich den Authentifizierungsslot noch einmal neu erzeugt, diesmal mit NIST P-384, während Signatur und Verschlüsselung bei Brainpool P-256 geblieben sind, und den neuen Unterschlüssel erneut per keytocard übertragen.

Dabei sind mir zwei weitere Kleinigkeiten begegnet, beide nicht offensichtlich:

  • Meine eigene ~/.ssh/config mit einem globalen Host * / IdentitiesOnly yes blendet Card-Keys komplett aus, unabhängig davon, ob man den Agenten zusätzlich per -o IdentityAgent=... explizit angibt. Für einen sauberen Test half nur -F /dev/null, um die eigene Konfiguration ganz zu umgehen.
  • gpg-agent verlangt den Keygrip des Auth-Unterschlüssels explizit in sshcontrol, sonst kommt „agent refused operation“, und zwar noch bevor überhaupt ein Pinpad-Prompt erscheint. Kein PIN-Fehler, sondern eine reine Allowlist-Sache, die sich leicht mit einem PIN-Problem verwechseln lässt.

Drei Live-Tests, alle drei erfolgreich

Zum Schluss die eigentliche Nagelprobe, alle drei Unterschlüssel einzeln gegen die Karte getestet, mit dem Pinpad als einzigem Ort für die PIN-Eingabe.

Signatur. Eine Testnachricht clearsignen und gleich wieder verifizieren:

Terminalausgabe eines clearsign- und verify-Durchlaufs mit der Karte, Ergebnis Good signature im ultimate Vertrauensstatus.
clearsign fragt den PIN am Pinpad ab, verify bestätigt danach eine gute Signatur mit dem ECDSA-Schlüssel der Karte.

Verschlüsselung. Ein vollständiger Roundtrip aus Verschlüsseln und Entschlüsseln, wieder mit PIN-Abfrage am Pinpad beim Entschlüsseln, der Klartext kommt danach unverändert zurück:

echo "Test von der OpenPGP-Karte" | gpg --encrypt --recipient card-demo@example.invalid | gpg --decrypt
gpg: encrypted with brainpoolP256r1 key, ID ..., created ...
      "OpenPGP Card Demo (throwaway key for blog/card demo, not for real use) <card-demo@example.invalid>"
Test von der OpenPGP-Karte

SSH. Ein Login gegen den eigenen Rechner über den Auth-Unterschlüssel, mit einem nur für den Test temporär ergänzten und danach wieder entfernten Eintrag in authorized_keys:

ssh-add -L
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQ... cardno:0005 0000D961
ssh -F /dev/null -o IdentityAgent=$(gpgconf --list-dirs agent-ssh-socket) kernel@localhost

Der Login fragt am Pinpad nach dem PIN und ist danach ein ganz normales SSH-Login, der einzige Unterschied gegenüber einem Schlüssel auf der Festplatte ist der Prompt am Reader.

Fazit: was die Karte wirklich bringt

Zurück zur Ausgangsfrage. Nein, eine OpenPGP-Karte hält deinen Primärschlüssel nicht offline vor, weil sie keinen Zertifizierungsslot hat und ihn deswegen gar nicht aufnehmen kann. Wer genau das sucht, bleibt bei einem verschlüsselten Cold-Storage-Medium, wie im Ed25519-Beitrag beschrieben.

Was die Karte stattdessen bringt, ist aus meiner Sicht mindestens genauso viel wert: die drei Unterschlüssel, mit denen du tatsächlich täglich arbeitest, Signatur, Verschlüsselung und Authentifizierung, wandern hardwaregebunden auf ein Stück Plastik. Kein keytocard lässt sich rückgängig machen, kein Angreifer mit Zugriff auf deine Festplatte bekommt das Schlüsselmaterial zu fassen, denn es liegt dort gar nicht mehr. Der Primärschlüssel bleibt davon komplett unberührt und weiterhin offline, genau da, wo er laut Restrisiken-Liste vom letzten Mal ohnehin hingehört. Die Smartcard aus dem Satz „Nichts hier braucht eine Smartcard“ wollte etwas anderes lösen, als eine Karte lösen kann. Für das, was sie tatsächlich kann, würde ich sie nach diesem Test jederzeit empfehlen, nur eben mit einem echten statt einem Wegwerf-Demo-Schlüssel und mit etwas Geduld für den Admin-PIN-Timeout.

Siehe auch

Wenn du selbst gerade zwischen Karten oder Kurven schwankst, oder wenn dir am Kartenlimit oder am PIN-Timeout noch ein Detail auffällt, das ich übersehen habe, dann dürft ihr mich sehr gerne fragen.

SSH-Bruteforce, DigitalOcean und AbuseIPDB – warum Blocken das Problem nicht löst

Aus gegebenem Anlass möchte ich ein paar Gedanken zu DigitalOcean aufschreiben. Nicht, weil ich glaube, dass DigitalOcean ein grundsätzliches Problem hat oder etwas falsch macht. Sondern weil DigitalOcean in meinen Logs seit Jahren besonders auffällt. Am Ende steht DigitalOcean hier eher sinnbildlich für ein größeres Thema.

Wer Systeme im Internet betreibt, kennt das Spiel. Server werden dauerhaft von außen angefasst. SSH-Ports werden gescannt, Login-Versuche laufen durch, Webseiten bekommen Requests auf bekannte Pfade, WordPress-Logins, XML-RPC, das volle Programm. Das ist kein gezielter Angriff, sondern automatisiertes Dauerrauschen. Bots, Skripte, Scanner, manchmal Security-Tools, manchmal schlicht schlecht konfigurierte Kisten.

Darstellung von automatisierten SSH-Bruteforce-Angriffen und Server-Härtung in Cloud-Umgebungen

Findet so ein Bot irgendwo ein offenes Loch, einen Standard-Login, ein vergessenes Passwort oder eine ungepatchte Schwachstelle, dann geht es weiter. Meistens wird erst einmal weitere Software nachgeladen. Der Host wird Teil eines Botnetzes, scannt selbst weiter, verteilt Spam, nimmt an DDoS-Aktionen teil oder schürft Kryptowährungen. Nichts davon ist neu, nichts davon ist überraschend.

Was mir allerdings seit mindestens vier Jahren auffällt: Ein sehr großer Teil dieser Brute-Force-Versuche, insbesondere auf SSH, kommt bei mir aus Netzen von DigitalOcean. Nicht ein bisschen mehr, sondern konstant irgendwo im Bereich von achtzig bis neunzig Prozent. Über Jahre. Über verschiedene Systeme hinweg.

Der erste Reflex liegt nahe. Wenn so viel aus einem Netz kommt, warum blockt man dann nicht einfach alle Netze dieses Anbieters? Dann ist Ruhe. Und wenn das alle machen würden, müsste der Anbieter ja reagieren. Der Gedanke ist verständlich. Ich hatte ihn selbst. Er ist aber aus meiner Sicht der falsche.

Ein pauschales Blocken ist im Grunde nichts anderes als eine Decke über das eigentliche Problem zu werfen. Das Problem ist damit nicht weg, es ist nur woanders. Die Bots wechseln dann eben zum nächsten Cloud-Provider. Außerdem produziert man sich damit ganz eigene Probleme. DigitalOcean-Netze komplett zu sperren heißt auch, legitimen Traffic auszusperren. APIs, Dienste, Kunden, Monitoring, externe Abhängigkeiten. Je nach Setup schneidet man sich damit schneller ins eigene Fleisch, als einem lieb ist.

Relativ schnell landet man dann bei Reputation-Diensten wie AbuseIPDB. Dort melden Betreiber IPs, von denen Scans, Brute-Force-Versuche oder andere Auffälligkeiten ausgehen. Auch ich melde dort seit Jahren IPs, automatisiert und manuell. Formal funktioniert das gut. IPs bekommen Scores, werden gelistet, tauchen in Datenbanken auf.

Das Problem ist nur: Diese Systeme arbeiten IP-basiert. Und genau das passt schlecht zur Realität moderner Netze. In Cloud-Umgebungen sind IPs kurzlebig. Heute gehört sie einem kompromittierten Host, morgen einem völlig legitimen Kunden. Ein hoher Abuse-Score sagt wenig über den aktuellen Nutzer dieser IP aus. Reputation ist träge, Infrastruktur ist schnell.

Jan  6 22:58:08 honeypot03 sshd-session[61904]: Invalid user sonar from 64.23.228.101 port 38610
Jan  6 22:58:08 honeypot03 sshd-session[61904]: Connection closed by invalid user sonar 64.23.228.101 port 38610 [preauth]
Jan  6 23:02:13 honeypot03 sshd-session[62101]: Invalid user sonar from 64.23.228.101 port 38174
Jan  6 23:02:13 honeypot03 sshd-session[62101]: Connection closed by invalid user sonar 64.23.228.101 port 38174 [preauth]
Jan  6 23:06:12 honeypot03 sshd-session[62175]: Invalid user sonar from 64.23.228.101 port 35952
Jan  6 23:06:12 honeypot03 sshd-session[62175]: Connection closed by invalid user sonar 64.23.228.101 port 35952 [preauth]
Jan  6 23:10:10 honeypot03 sshd-session[62248]: Invalid user steam from 64.23.228.101 port 38236
Jan  6 23:10:10 honeypot03 sshd-session[62248]: Connection closed by invalid user steam 64.23.228.101 port 38236 [preauth]
Jan  6 23:14:17 honeypot03 sshd-session[62335]: Invalid user steam from 64.23.228.101 port 35952
Jan  6 23:14:18 honeypot03 sshd-session[62335]: Connection closed by invalid user steam 64.23.228.101 port 35952 [preauth]
Jan  6 23:18:22 honeypot03 sshd-session[62455]: Invalid user steam from 64.23.228.101 port 50096
Jan  6 23:18:22 honeypot03 sshd-session[62455]: Connection closed by invalid user steam 64.23.228.101 port 50096 [preauth]
Jan  6 23:22:24 honeypot03 sshd-session[62599]: Invalid user sugi from 64.23.228.101 port 53212
Jan  6 23:22:25 honeypot03 sshd-session[62599]: Connection closed by invalid user sugi 64.23.228.101 port 53212 [preauth]
Jan  6 23:26:26 honeypot03 sshd-session[62671]: Invalid user svnuser from 64.23.228.101 port 44820
Jan  6 23:26:26 honeypot03 sshd-session[62671]: Connection closed by invalid user svnuser 64.23.228.101 port 44820 [preauth]
Jan  6 23:30:26 honeypot03 sshd-session[62763]: Invalid user svnuser from 64.23.228.101 port 52156
Jan  6 23:30:27 honeypot03 sshd-session[62763]: Connection closed by invalid user svnuser 64.23.228.101 port 52156 [preauth]
Jan  6 23:34:30 honeypot03 sshd-session[62867]: Invalid user taryn from 64.23.228.101 port 54128
Jan  6 23:34:31 honeypot03 sshd-session[62867]: Connection closed by invalid user taryn 64.23.228.101 port 54128 [preauth]
Jan  6 23:38:41 honeypot03 sshd-session[62939]: Invalid user taryn from 64.23.228.101 port 39894
Jan  6 23:38:42 honeypot03 sshd-session[62939]: Connection closed by invalid user taryn 64.23.228.101 port 39894 [preauth]
Jan  6 23:42:44 honeypot03 sshd-session[63013]: Invalid user taryn from 64.23.228.101 port 57728
Jan  6 23:42:45 honeypot03 sshd-session[63013]: Connection closed by invalid user taryn 64.23.228.101 port 57728 [preauth]
Jan  6 23:46:45 honeypot03 sshd-session[63160]: Invalid user taryn from 64.23.228.101 port 38438
Jan  6 23:46:45 honeypot03 sshd-session[63160]: Connection closed by invalid user taryn 64.23.228.101 port 38438 [preauth]
Jan  6 23:50:49 honeypot03 sshd-session[63252]: Invalid user taryn from 64.23.228.101 port 54070
Jan  6 23:50:49 honeypot03 sshd-session[63252]: Connection closed by invalid user taryn 64.23.228.101 port 54070 [preauth]
Jan  6 23:54:55 honeypot03 sshd-session[63354]: Invalid user terrance from 64.23.228.101 port 57960
Jan  6 23:54:55 honeypot03 sshd-session[63354]: Connection closed by invalid user terrance 64.23.228.101 port 57960 [preauth]
Jan  6 23:59:05 honeypot03 sshd-session[63472]: Invalid user terrance from 64.23.228.101 port 47558
Jan  6 23:59:05 honeypot03 sshd-session[63472]: Connection closed by invalid user terrance 64.23.228.101 port 47558 [preauth]
Jan  7 00:03:11 honeypot03 sshd-session[64731]: Invalid user terrance from 64.23.228.101 port 42938
Jan  7 00:03:11 honeypot03 sshd-session[64731]: Connection closed by invalid user terrance 64.23.228.101 port 42938 [preauth]

Damit erklärt sich auch, warum Provider solche externen Feeds nicht einfach hart umsetzen. Würde man IPs automatisiert abschalten, nur weil sie in einer Datenbank schlecht bewertet sind, träfe man regelmäßig Unbeteiligte. False Positives wären vorprogrammiert. Rechtlich, operativ und wirtschaftlich ist das für Provider kaum tragbar.

Warum also fällt DigitalOcean so stark auf? Das kann ich nicht belegen, nur einordnen. DigitalOcean ist günstig, schnell, einfach. In wenigen Minuten hat man dort eine VM mit öffentlicher IP. Das ist für legitime Nutzer attraktiv, aber eben auch für Leute mit schlechten Absichten. Wenn Infrastruktur billig und niedrigschwellig ist, taucht sie zwangsläufig häufiger in Logs auf. Dazu kommt, dass viele Systeme dort von Menschen betrieben werden, die vielleicht noch nicht so tief im Thema Security stecken. Offene Dienste, schwache Konfigurationen, fehlendes Hardening – all das macht solche Hosts wiederum zu guten Kandidaten für Kompromittierung und Weiterverwendung.

Wichtig dabei: DigitalOcean selbst macht aus meiner Sicht nichts grundlegend falsch. Der Abuse-Prozess funktioniert. Meldungen lassen sich automatisiert einreichen, werden angenommen, werden beantwortet, werden bearbeitet. Ich habe das über Jahre hinweg genutzt, sowohl manuell als auch automatisiert. Das ist sauber umgesetzt.

Was sich dadurch aber nicht ändert, ist die Menge der Versuche. Die wird nicht weniger. Sie bleibt konstant. Einzelne Hosts verschwinden, neue tauchen auf. Abuse-Meldungen – egal ob direkt beim Provider oder über Plattformen wie AbuseIPDB – wirken immer nur lokal und zeitverzögert. Gegen ein strukturelles Phänomen kommen sie nicht an.

Aus Sicht eines Providers ist das auch logisch. Ein paar tausend fehlgeschlagene SSH-Logins sind kein Incident. Kein DDoS, kein Ausfall, kein messbarer Schaden. Das fällt unter Hintergrundrauschen. Niemand bezahlt dafür, dieses Rauschen global zu eliminieren. Und ehrlich gesagt: Das kann auch niemand realistisch leisten.

Die eigentliche Konsequenz daraus ist unbequem, aber klar. Man darf nicht erwarten, dass Provider oder Reputation-Datenbanken einem dieses Problem abnehmen. Scan- und Brute-Force-Traffic gehört heute zum Betrieb eines öffentlich erreichbaren Systems dazu. Die einzige Stelle, an der man sinnvoll ansetzen kann, ist das eigene Setup.

Saubere Konfiguration. Keine Passwort-Logins per SSH. Kein Root-Login. Rate-Limits. Monitoring, das zwischen Rauschen und echten Zustandsänderungen unterscheidet. Fail2Ban als Dämpfer, nicht als Illusion von Sicherheit. Und vor allem: Gelassenheit gegenüber Logs, die voll sind, aber nichts bedeuten.

DigitalOcean ist hier nicht der Feind. AbuseIPDB ist kein Allheilmittel. Beides sind sichtbare Teile eines größeren Bildes. Das eigentliche Thema ist, wie man Systeme so betreibt, dass dieses Hintergrundrauschen irrelevant wird.

Siehe auch: SSH-Server absichern mit MFA

Quantensichere Kryptografie mit OpenSSH auf FreeBSD 15 richtig konfigurieren

Mein FreeBSD 15 kommt mit OpenSSH 10.0p2 und OpenSSL 3.5.4.
Beide bringen inzwischen das mit, was man aktuell als quantensichere Kryptografie bezeichnet. Oder genauer gesagt das, was wir Stand heute für ausreichend robust gegen zukünftige Quantenangriffe halten.

Illustration zu quantensicherer Kryptografie mit OpenSSH auf FreeBSD 15. Dargestellt sind ein Quantenchip, kryptografische Symbole, ein Server, ein SSH Schlüssel sowie der FreeBSD Daemon als Sinnbild für post-quantum Key Exchange und sichere Serverkommunikation.

Quantensicher? Nein, das hat nichts mit Füßen zu tun, sondern tatsächlich mit den Quanten aus der Physik. Quantencomputer sind eine grundlegend andere Art von Rechnern. Googles aktueller Quantenchip war in diesem Jahr bei bestimmten Physiksimulationen rund 13.000-mal schneller als der derzeit leistungsstärkste klassische Supercomputer. Der chinesische Quantencomputer Jiuzhang wurde bei speziellen Aufgaben sogar als 100 Billionen Mal schneller eingestuft.

Kurz gesagt: Quantencomputer sind bei bestimmten Berechnungen extrem viel schneller als heutige klassische Rechner. Und genau das ist für Kryptografie ein Problem.

Als Vergleich aus der klassischen Welt: Moderne Grafikkarten haben die Zeit zum Knacken von Passwörtern in den letzten Jahren drastisch verkürzt.

  • Nur Zahlen: Ein 12-stelliges Passwort wird praktisch sofort geknackt.
  • Nur Kleinbuchstaben: wenige Wochen bis Monate.
  • Groß- und Kleinschreibung plus Zahlen: etwa 100 bis 300 Jahre.
  • Zusätzlich Sonderzeichen: 2025 noch als sehr sicher einzustufen mit geschätzten 226 bis 3.000 Jahren.

Quantencomputer nutzen spezielle Algorithmen wie den Grover-Algorithmus, der die effektive Sicherheit symmetrischer Verfahren halbiert. Ein ausreichend leistungsfähiger Quantencomputer könnte damit die benötigte Zeit drastisch reduzieren. Was heute Jahrhunderte dauert, könnte theoretisch auf Tage oder Stunden schrumpfen.

Stand 2025 sind solche Systeme zwar real und in der Forschung extrem leistungsfähig, werden aber noch nicht flächendeckend zum Brechen realer Kryptosysteme eingesetzt.

Heißt das also alles entspannt bleiben? Jein.

Verschlüsselte Datenträger lassen sich kopieren und für später weglegen. Gleiches gilt für aufgezeichneten verschlüsselten Netzwerkverkehr. Heute kommt man nicht an die Daten heran, aber es ist absehbar, dass das in Zukunft möglich sein könnte. Genau hier setzt quantensichere Kryptografie an. Ziel ist es, auch aufgezeichnete Daten dauerhaft vertraulich zu halten.

Ein praktisches Beispiel ist der Schlüsselaustausch mlkem768x25519. Wenn ihr diese Seite nicht gerade über Tor lest, ist die Wahrscheinlichkeit hoch, dass euer Browser bereits eine solche hybride, post-quantum-fähige Verbindung nutzt. Im Firefox lässt sich das einfach prüfen über F12, Network, eine Verbindung anklicken, dann Security und dort die Key Exchange Group. Taucht dort mlkem768x25519 auf, ist die Verbindung entsprechend abgesichert. Richtig, auf dem Screenhot seht ihr auch HTTP/3.

Image of mlkem768+x25519 in firefox.

Für diese Webseite ist das nicht zwingend nötig. Für SSH-Verbindungen zu Servern aber unter Umständen schon eher. Deshalb zeige ich hier, wie man einen OpenSSH-Server entsprechend konfiguriert.

Ich beziehe mich dabei bewusst nur auf die Kryptografie. Ein echtes SSH-Hardening umfasst deutlich mehr, darum geht es hier aber nicht.

Die zentrale Konfigurationsdatei ist wie üblich: /etc/ssh/sshd_config

Stand Ende 2025 kann ich folgende Konfiguration empfehlen:

KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com

Die Zeilen werden entweder an die bestehende Konfiguration angehängt oder ersetzen vorhandene Einträge. Da wir nicht einfach blind kopieren wollen, hier kurz die Erklärung.

Schlüsselaustausch:
Bevorzugt werden hybride Verfahren wie mlkem768 kombiniert mit x25519 sowie sntrup761 kombiniert mit x25519. Diese verbinden klassische elliptische Kryptografie mit post-quantum-resistenten Algorithmen. Damit ist die Verbindung sowohl gegen heutige Angreifer als auch gegen zukünftige Store-now-decrypt-later-Szenarien abgesichert. Curve25519 dient als bewährter Fallback. Klassische Diffie-Hellman-Gruppen sind nur aus Kompatibilitätsgründen enthalten.

Verschlüsselung:
Es werden ausschließlich moderne Algorithmen eingesetzt. Primär kommen AEAD-Ciphers wie ChaCha20-Poly1305 und AES-GCM zum Einsatz, die Vertraulichkeit und Integrität gleichzeitig liefern und bekannte Schwächen älterer Modi vermeiden. Ältere Verfahren wie CBC sind bewusst ausgeschlossen.

Integrität:
Zum Einsatz kommen ausschließlich SHA-2-basierte MACs im Encrypt-then-MAC-Modus. Dadurch werden klassische Angriffe auf SSH wie Padding-Oracles und bestimmte Timing-Leaks wirksam verhindert.

Serveridentität:
Als Hostkey-Algorithmus wird Ed25519 verwendet. Optional auch mit Zertifikaten oder hardwaregestützten Security Keys. Das bietet hohe kryptografische Sicherheit bei überschaubarem Verwaltungsaufwand.

Wichtig: Das funktioniert nur, wenn Server und Client diese Algorithmen auch unterstützen. Wer bereits mit SSH-Keys arbeitet, sollte prüfen, dass es sich um Ed25519-Keys handelt. Andernfalls sperrt man sich im Zweifel selbst aus.

Auf dem Server lässt sich die aktive Konfiguration prüfen mit:

sshd -T | grep -Ei 'kexalgorithms|ciphers|macs|hostkeyalgorithms'

Auf dem Client geht es am einfachsten mit:

ssh -Q kex
ssh -Q cipher
ssh -Q mac
ssh -Q key

So sieht man schnell, welche Algorithmen tatsächlich verfügbar sind.

Zur externen Überprüfung der SSH-Konfiguration kann ich außerdem das Tool ssh-audit empfehlen. Aufruf einfach per:

ssh-audit hostname oder IP -p PORT

Das liefert eine brauchbare Einschätzung der aktiven Kryptografie und möglicher Schwachstellen. Oh, wenn ihr schon dabei seit, vergesst nicht:

Hinweis zur Einordnung der Quantensicherheit:
Die hier gezeigte Konfiguration verbessert ausschließlich den Schlüsselaustausch (Key Exchange) durch hybride post-quantum-fähige Verfahren. Hostkeys und Signaturen in OpenSSH basieren weiterhin auf klassischen Algorithmen (z. B. Ed25519 oder ECDSA); standardisierte post-quantum-Signaturalgorithmen sind in OpenSSH aktuell noch nicht implementiert. Es existieren zwar experimentelle Forks (z. B. aus dem Open-Quantum-Safe-Projekt), diese gelten jedoch ausdrücklich nicht als produktionsreif und sind nicht Bestandteil des OpenSSH-Mainlines. Die hier gezeigte Konfiguration ist daher als pragmatischer Übergangsschritt zu verstehen, um „store-now-decrypt-later“-Risiken beim Schlüsselaustausch bereits heute zu reduzieren, ohne auf instabile oder nicht standardisierte Komponenten zu setzen.
Weiterführende Informationen zum aktuellen Stand der post-quantum-Unterstützung in OpenSSH finden sich in der offiziellen Dokumentation: https://www.openssh.com/pq.html

Siehe auch: Post-Quantum TLS für E-Mail — Postfix und Dovecot mit X25519MLKEM768 auf FreeBSD 15, Post-Quantum TLS für Nginx — X25519MLKEM768 auf FreeBSD 15 konfigurieren

Viel Spaß beim Nachbauen. Und wie immer: bei Fragen, fragen.

Sicherheitslücken melden: Mein Umgang mit einem Vulnerability Report

Vulnerability Report

Vor Kurzem habe ich einen Vulnerability Report erhalten. Ich freue mich über solche Hinweise. Sie helfen mir, mein Setup zu verbessern, bevor jemand eine Schwachstelle tatsächlich ausnutzt.

Der Report

Subject: Vulnerability Report: Vulnerable System Detected at openpgpkey.kernel-error.com

Hello Team,

I have identified a security issue in your system related to a vulnerability
(CVE-2023-48795) in Terrapin.

Vulnerability Details:
- CVE Identifier: CVE-2023-48795
- Vulnerability Type: javascript
- Severity: medium
- Host: openpgpkey.kernel-error.com
- Affected Port: 22

[...]

Best Regards,
Security Team

Erste Einschätzung

Terrapin hatte ich eigentlich schon überall gepatcht. Dann der Hinweis auf openpgpkey.kernel-error.com. Die Domain existiert als CNAME und gehört zur Web Key Directory (WKD), damit GPG-Keys automatisiert abgerufen werden können. Ich habe das als CNAME zu wkd.keys.openpgp.org angelegt, weil dieser Keyserver eine E-Mail-Validierung beim Hochladen durchführt.

Der betroffene SSH-Server gehört also gar nicht zu meiner Infrastruktur. Ich kann selbst nichts tun.

Vulnerability Type: JavaScript bei einem SSH-Problem auf Port 22? Der Finder hat vermutlich sein Standard-Template benutzt und nicht angepasst. Aber ich wollte trotzdem prüfen, ob seine Einschätzung zum SSH-Server zutrifft:

# ssh-audit openpgpkey.kernel-error.com (gekürzt)
(gen) banner: SSH-2.0-OpenSSH_8.4p1 Debian-5+deb11u3
(gen) software: OpenSSH 8.4p1

(cve) CVE-2021-41617  -- (CVSSv2: 7.0) privilege escalation via supplemental groups
(cve) CVE-2016-20012  -- (CVSSv2: 5.3) enumerate usernames via challenge response

(kex) ecdh-sha2-nistp256  -- [fail] suspected NSA backdoor
(kex) ecdh-sha2-nistp384  -- [fail] suspected NSA backdoor
(kex) kex-strict-s-v00@openssh.com  -- [info] Terrapin counter-measure present

(key) ssh-rsa (2048-bit)  -- [fail] broken SHA-1 hash algorithm
(key) ecdsa-sha2-nistp256 -- [fail] suspected NSA backdoor

(mac) hmac-sha1-etm@openssh.com  -- [fail] broken SHA-1 hash algorithm
(mac) hmac-sha1                  -- [fail] broken SHA-1

Sieht tatsächlich nicht optimal aus. NIST-Kurven, SHA-1, 2048-Bit RSA. Der Hinweis war also nicht unberechtigt. Ich habe dem Finder freundlich und dankbar geantwortet, aber darauf hingewiesen, dass das System nicht zu meiner Infrastruktur gehört. Die relevanten WHOIS-Informationen zur IP habe ich mitgeschickt.

Die Antwort

Thank you for your answer.

Let me know if you need anything else from myside

I hope this type of hard efforts deserves something reward

„Hard efforts“. Ich will das nicht schlechtreden. Im beruflichen Umfeld hätte ich mich vielleicht sogar für eine Kleinigkeit stark gemacht. Aber hier geht es um meine private Infrastruktur, und dann noch mit dem JavaScript-Hinweis und der Meldung zu einem fremden System. Das wirkt oberflächlich. Also habe ich ihn freundlich darauf hingewiesen.

Wie man mit Vulnerability Reports umgehen sollte

Wenn euch eine solche Nachricht erreicht: Schnell und freundlich reagieren. Den Report ernst nehmen, bewerten und eine angemessene Rückmeldung geben. Die Mühe des Finders wertschätzen. Zwei Wochen später mit „Anzeige ist raus!“ zu antworten wäre der falsche Weg. Es ist für jemanden deutlich aufwendiger, eine Meldung zu schreiben, als das Ganze in ein Darknet-Forum zu posten und dort ein paar XMR einzusammeln.

Macht es den Leuten einfach, euch zu kontaktieren. Eine security.txt oder klare Kontaktinformationen für eine Security-Mailbox helfen ungemein. Hauptsache, jemand kann seinen Report unkompliziert abgeben, und er wird von jemandem gelesen, der das bewerten kann.

Mehr zum Thema security.txt:
securitytxt.org | Wikipedia | BSI Allianz für Cybersicherheit

Zum Vergleich: Mein eigener SSH-Server

So sieht meine SSH-Konfiguration von außen aus:

# ssh-audit bsd01.kernel-error.de (gekürzt)
(gen) banner: SSH-2.0-OpenSSH_9.7 DemMeisterSeinRennAuto
(gen) software: OpenSSH 9.7

(kex) sntrup761x25519-sha512@openssh.com  -- [info] Post-Quantum Key Exchange
(kex) curve25519-sha256                   -- [info] default since OpenSSH 6.4
(kex) diffie-hellman-group16-sha512       -- [info] available since OpenSSH 7.3
(kex) kex-strict-s-v00@openssh.com        -- [info] Terrapin counter-measure

(key) ssh-ed25519                         -- [info] available since OpenSSH 6.5

(enc) aes256-gcm@openssh.com              -- [info] available since OpenSSH 6.2
(enc) aes128-gcm@openssh.com              -- [info] available since OpenSSH 6.2

(mac) hmac-sha2-256-etm@openssh.com       -- [info] available since OpenSSH 6.2
(mac) hmac-sha2-512-etm@openssh.com       -- [info] available since OpenSSH 6.2

Keine NIST-Kurven, kein SHA-1, kein RSA, Post-Quantum Key Exchange mit sntrup761. Nur Ed25519 als Host Key. Der Banner ist übrigens Absicht.

Fragen? Einfach melden.

FreeBSD SSH-Server absichern: MFA mit Google Authenticator einrichten​

SSH-Keys sind der Standard. Aber manchmal lässt es sich nicht vermeiden, dass ein Login nur mit Benutzername und Kennwort abgesichert ist. Um das aufzuwerten, lässt sich der SSH-Server mit einem zweiten Faktor ausstatten — hier mit dem Google Authenticator (TOTP) auf FreeBSD.

Installation

pkg install pam_google_authenticator

PAM-Konfiguration

In /etc/pam.d/sshd das Google Authenticator PAM-Modul als zweiten required-Eintrag nach pam_unix einfügen:

#
# PAM configuration for the "sshd" service
#

# auth
auth		required	pam_unix.so		no_warn try_first_pass
auth		required	/usr/local/lib/pam_google_authenticator.so

# account
account		required	pam_nologin.so
account		required	pam_login_access.so
account		required	pam_unix.so

# session
session		required	pam_permit.so

# password
password	required	pam_unix.so		no_warn try_first_pass

Die Reihenfolge ist wichtig: Erst das Kennwort (pam_unix), dann der TOTP-Code. Auf dem gleichen Weg lässt sich MFA auch für su, den Konsolen-Login oder SSH-Keys einrichten — einfach das entsprechende PAM-File anpassen.

sshd_config anpassen

In /etc/ssh/sshd_config muss Challenge-Response aktiviert sein:

# Seit OpenSSH 8.7 heißt die Option KbdInteractiveAuthentication
# ChallengeResponseAuthentication ist ein Alias und funktioniert weiterhin
KbdInteractiveAuthentication yes

Danach service sshd restart — aber vorher sicherstellen, dass man noch eine offene Session hat, falls etwas nicht stimmt.

Authenticator einrichten

Auf dem Smartphone den Google Authenticator installieren (oder eine andere TOTP-App wie Aegis, 2FAS oder den Microsoft Authenticator). Dann auf dem Server mit dem gewünschten Benutzer google-authenticator aufrufen:

cd ~
google-authenticator

Das Tool zeigt einen QR-Code im Terminal, den man mit der Authenticator-App scannt:

Danach den angezeigten Code einmal eingeben — fertig. Bei jedem Kennwort-Login wird jetzt zusätzlich der aktuelle TOTP-Code abgefragt.

Wichtig: Das Tool zeigt auch Backup-Codes an. Diese unbedingt sicher aufbewahren — wenn das Smartphone verloren geht, kommt man sonst nicht mehr rein. Die Konfiguration liegt in ~/.google_authenticator und kann dort auch nachträglich eingesehen werden.

Siehe auch: FreeBSD OpenSSH: OS-Banner sicher entfernen, SSH-Bruteforce, DigitalOcean und AbuseIPDB – warum Blocken das Problem nicht löst

Unter Linux ist die Einrichtung sehr ähnlich — das PAM-Modul heißt dort libpam-google-authenticator. Fragen? Einfach melden.

DNSSEC und SSHFP unter Linux Mint und Ubuntu zum Laufen bringen

Heute habe ich versucht, mich von meiner neuen Linux Mint Installation aus mit einem meiner SSH-Server zu verbinden. Mein SSH-Client hat mich direkt gefragt, ob ich dem Hostkey vertrauen möchte:

ssh username@hostname.kernel-error.org
The authenticity of host 'hostname.kernel-error.org (2a01:5a8:362:4416::32)' can't be established.
ED25519 key fingerprint is SHA256:kTRGVCMRLiHfvJunW2CbW5H3NZmn3Wkx2KnHJXl3iJu.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Für viele ist das normal — man tippt „yes“ und sieht die Meldung nie wieder. Aber diese Meldung hat ihren Grund. Beim ersten Verbindungsaufbau zeigt SSH den Fingerprint des Server-Hostkeys an, damit man prüfen kann, ob man wirklich mit dem richtigen Server spricht und nicht mit einem Angreifer. Wer eh immer „yes“ sagt, könnte den Check auch gleich in seiner ~/.ssh/config abschalten:

Host *
    StrictHostKeyChecking no

SSHFP — Hostkeys per DNS verifizieren

Es gibt einen besseren Weg: SSHFP-Records (RFC 4255). Man hinterlegt die Fingerprints der erwarteten Hostkeys als DNS-Einträge. Der SSH-Client prüft diese automatisch — vorausgesetzt die DNS-Antwort ist per DNSSEC abgesichert. In der ~/.ssh/config:

Host *
   VerifyHostKeyDNS yes

Meine DNS-Server unterstützen alle DNSSEC, mein lokaler Resolver auf dem Router auch, die SSH-Config stimmt — und trotzdem erscheint die Meldung. Also mit ssh -vvv debuggen:

debug1: found 2 insecure fingerprints in DNS

Insecure. SSH findet die SSHFP-Records, vertraut ihnen aber nicht, weil die DNS-Antwort nicht als DNSSEC-validiert markiert ist.

Das Problem: systemd-resolved

Schneller Test mit dig +dnssec gegen Google DNS:

dig +dnssec hostname.kernel-error.org @8.8.8.8
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

Das ad-Flag (Authenticated Data) ist gesetzt — meine DNS-Server liefern DNSSEC korrekt aus. Auch der lokale Router-Resolver liefert ad. Aber ohne expliziten @server:

dig +dnssec hostname.kernel-error.org
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

Kein ad. Was steht in /etc/resolv.conf? 127.0.0.53systemd-resolved. Der Stub-Resolver von systemd schluckt das AD-Flag.

Man könnte in /etc/systemd/resolved.conf einfach DNSSEC=yes setzen — bei mir ging danach aber gar keine DNS-Auflösung mehr. Das liegt am Stub-Resolver, den man ebenfalls umkonfigurieren müsste. Nennt mich oldschool, aber für meine Zwecke reicht der klassische Weg über die vom NetworkManager gepflegte resolv.conf.

Lösung: systemd-resolved abschalten

sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo rm /etc/resolv.conf

In /etc/NetworkManager/NetworkManager.conf in der [main]-Sektion:

dns=default

NetworkManager neu starten:

sudo systemctl restart NetworkManager
cat /etc/resolv.conf
# Generated by NetworkManager
search kernel-error.local
nameserver 10.10.88.1
nameserver fd00:424e:6eff:f525:454e:6eff:f525:4241

DNS-Auflösung geht. Aber SSH sagt weiterhin „insecure“. Es fehlen noch zwei Optionen in der resolv.conf.

edns0 und trust-ad

Erste Erkenntnis — edns0 muss aktiviert sein, damit DNSSEC-Daten überhaupt transportiert werden. In /etc/resolv.conf:

options edns0

Jetzt zeigt dig das ad-Flag. Aber SSH sagt immer noch „insecure“. Warum? Ein Blick in den SSH-Quellcode — die ldns-Bibliothek macht die DNSSEC-Validierung:

        /* Check for authenticated data */
        if (ldns_pkt_ad(pkt)) {
                rrset->rri_flags |= RRSET_VALIDATED;
        } else { /* AD is not set, try autonomous validation */
                ldns_rr_list * trusted_keys = ldns_rr_list_new();
                /* ... */
                if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs,
                     trusted_keys)) == LDNS_STATUS_OK) {
                        rrset->rri_flags |= RRSET_VALIDATED;
                }
        }

ldns prüft das AD-Flag im DNS-Paket. Aber die glibc setzt das AD-Flag in der Antwort nur dann, wenn trust-ad in der resolv.conf steht — sonst wird es aus Sicherheitsgründen herausgefiltert. Die vollständige Option:

options edns0 trust-ad

Und jetzt:

ssh username@hostname.kernel-error.org -vvv
[...]
debug1: found 2 secure fingerprints in DNS
debug3: verify_host_key_dns: checking SSHFP type 4 fptype 1
debug1: verify_host_key_dns: matched SSHFP type 4 fptype 1
debug3: verify_host_key_dns: checking SSHFP type 4 fptype 2
debug1: verify_host_key_dns: matched SSHFP type 4 fptype 2
debug1: matching host key fingerprint found in DNS

secure statt insecure. SSH verifiziert den Hostkey automatisch per DNSSEC — keine manuelle Fingerprint-Prüfung mehr nötig.

Rebootfest machen

Die manuell eingetragenen Optionen in der resolv.conf überleben keinen Reboot — der NetworkManager überschreibt die Datei. Per nmcli die Optionen dauerhaft im Netzwerkprofil setzen, für IPv4 und IPv6:

nmcli conn modify DEINE-PROFIL-UUID ipv4.dns-options edns0,trust-ad
nmcli conn modify DEINE-PROFIL-UUID ipv6.dns-options edns0,trust-ad

Die UUID des aktiven Profils findet man mit nmcli conn show. Beide Zeilen sind nötig — fehlt eine, greift es nicht.


Zusammenfassung: systemd-resolved unter Linux Mint und Ubuntu filtert das DNSSEC-AD-Flag heraus. Ohne AD-Flag kann SSH die SSHFP-Records nicht als vertrauenswürdig einstufen. Lösung: systemd-resolved abschalten, NetworkManager mit dns=default nutzen, edns0,trust-ad per nmcli setzen.

Wer einen DNSSEC-validierenden Resolver sucht — dns.kernel-error.de ist ein öffentlicher DNS-Resolver mit DNSSEC, DNS over TLS und DNS over HTTPS.

Und die offene Frage: Ich bin mit meinem FreeBSD-Wissen an das Thema gegangen. Wie macht man das als Linux-User mit systemd-resolved richtig? Schreibt mir, wenn ihr es wisst.

Siehe auch: SSH Host Keys per SSHFP

SSH-Brute-Force mit veralteter Implementierung: Angriffsmuster erkennen​

Wenn man mit einem System im Internet steht fummelt immer irgendein script kiddie oder bot an den Diensten herum. Oft ist hier eine IP Adresse aus China dabei. Dann probieren sie ein paar default logins und wandern weiter zur nächsten IP Adresse. Die Bots geben dem Ganzen in der Regel schon nicht mehr als drei Versuche, weil sie dann eh von irgendeinem Sicherheitssystem geblockt werden. Da es noch viele andere bots hinter anderen IP Adressen gibt, übermittelt der bot nur seinen Stand der Versuche an das Hirn des Botnetzes und der nächste, nicht geblockte bot, kommt und probiert es weiter…

Alles „kalter Kaffee“… In den letzten Wochen fallen mir zwei kleine Veränderungen auf.

old SSH Bot

Einmal kommen diese IP Adressen noch immer stark aus China… ABER sehr oft ebenfalls von DigitalOcean (USA). Zudem fallen mir die anderen Cloudprovider auf (Google, Microsoft, AWS…). Das verschiebt sich aktuell wohl etwas. Normalerweise kommt ganz viel aus China, dann ganz viel von verschiedenen dynamischen Endkundenanschlüssen auf der Erde. Jetzt kommt ganz viel aus China, dann unglaublich nahe daran Digitalocean, direkt gefolgt von der google-cloud und microsoft-cloud. Erst jetzt kommen die Endkundenanschlüsse und mischen sich mit Adressen aus der AWS-Cloud. Scheinbar haben die Amazonjungs irgendetwas „besser“ gemacht, um ihre Kunden davor zu schützen sich etwas „einzufangen“?!?

Zweitens scheint da ein Botnetz mit recht alter ssh Implementierung unterwegs zu sein. Oder es sucht halt speziell alte SSH-Server? Auf IoT Geräte mit alter Firmware tippe ich weniger, denn von diesen kommt ebenfalls etwas von Cloudanbietern. Bei denen unterstelle ich einfach mal, keine alten IoT Geräte im Einsatz zu haben, die infiziert sind. Naja… Oder es wird halt nach genau solchen Geräten gesucht. Warum alt? Weil ich so etwas in den Logs finde:

Apr  8 10:35:58 YOURMOM sshd[43201]: reverse mapping checking getaddrinfo for 4.3.2.1.serverdedicati.mum.your [1.2.3.4] failed.
Apr  8 10:35:58 YOURMOM sshd[43201]: Did not receive identification string from 1.2.3.4 port 34244
Apr  8 10:36:22 YOURMOM sshd[43202]: reverse mapping checking getaddrinfo for 4.3.2.1.serverdedicati.mum.your [1.2.3.4] failed.
Apr  8 10:36:22 YOURMOM sshd[43202]: Unable to negotiate with 1.2.3.4 port 36160: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1 [preauth]
Apr  8 10:36:42 YOURMOM sshd[43204]: reverse mapping checking getaddrinfo for 4.3.2.1.serverdedicati.mum.your [1.2.3.4] failed.
Apr  8 10:36:42 YOURMOM sshd[43204]: Unable to negotiate with 1.2.3.4 port 39556: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1 [preauth]

Wie ist das bei euch?

Siehe auch: SSH-Server absichern mit MFA, SSH-Bruteforce, DigitalOcean und AbuseIPDB – warum Blocken das Problem nicht löst, Raspberry Pi als Angriffsziel: SSH-Brute-Force auf den User pi

Fragen? Einfach melden.

FreeBSD OpenSSH: OS-Banner sicher entfernen

Im Standard ist der OpenSSH Server auf einem FreeBSD so konfiguriert, dass er jeweils die aktuelle Betriebssystemversion mit ausliefert.

Dieses sieht dann im Beispiel so aus:

telnet bsd01.testsystem 22
Trying 1.2.3.4...
Connected to bsd01.testsystem.
Escape character is '^]'.
SSH-2.0-OpenSSH_7.8 FreeBSD-20180909

Um hier zumindest die genaue OS Version zu verstecken reicht folgendes in der /etc/sshd_config:

#VersionAddendum FreeBSD-20180909
VersionAddendum DemMeisterSeinRennAuto

Testet man nun noch mal sieht man nur noch die Version:

telnet bsd01.testsystem 22
Trying 1.2.3.4...
Connected to bsd01.testsystem.
Escape character is '^]'.
SSH-2.0-OpenSSH_7.8 DemMeisterSeinRennAuto

Auf einem Debian basierten System wäre es hingegen:

DebianBanner no

Siehe auch: SSH-Server absichern

Fragen? Einfach melden.

Raspberry Pi als Angriffsziel: SSH-Brute-Force auf den User pi

In meinen Honeypots war 2019 ein deutlicher Trend sichtbar: SSH-Brute-Force-Angriffe zielten massiv auf den Benutzernamen pi. Im November 2018 waren 13 Prozent der Loginversuche mit dem User pi. Im März 2019 lag der Anteil bei 87 bis 88 Prozent.

Mar 25 06:20:21 pot06 sshd[93829]: Invalid user pi from xx.xx.xx.xx port 55312
Mar 25 06:20:21 pot06 sshd[93829]: Failed unknown for invalid user pi from xx.xx.xx.xx port 55312 ssh2
Mar 25 06:20:21 pot06 sshd[93827]: Invalid user pi from xx.xx.xx.xx port 55310
Mar 25 06:20:21 pot06 sshd[93827]: Failed unknown for invalid user pi from xx.xx.xx.xx port 55310 ssh2

Warum gerade pi?

Bis April 2022 legte Raspberry Pi OS automatisch den Benutzer pi mit dem Standardpasswort raspberry an. Millionen von Geräten liefen mit diesen Default-Credentials, viele davon direkt am Internet. SSH war standardmäßig aktiviert. Die Botnetze haben das erkannt und ihre Wörterbücher entsprechend angepasst.

Auffällig war auch die Herkunft: Die Quellen kamen nicht wie sonst überwiegend aus Asien, sondern vermehrt aus dem DACH-Raum. Das deutet darauf hin, dass die Angriffe erfolgreich waren und bereits kompromittierte Raspberry Pis in Deutschland, Österreich und der Schweiz als Sprungbrett nutzten.

Was sich geändert hat

Seit April 2022 erzwingt Raspberry Pi OS bei der Ersteinrichtung die Vergabe eines eigenen Benutzernamens und Passworts. Der automatische User pi wird nicht mehr angelegt. Das ist ein richtiger Schritt. Das Problem sind die Millionen von Altgeräten die noch mit den Default-Credentials laufen und nie aktualisiert wurden.

Wer einen Raspberry Pi am Internet betreibt: Standardpasswort ändern, SSH mit Key-Authentifizierung absichern, idealerweise mit Zwei-Faktor-Authentifizierung. Und den User pi am besten ganz durch einen eigenen ersetzen.

Fragen? Einfach melden.

Mal wieder auffällig viele Brute-Force SSH Angriffe….

Seit gestern fällt es irgendwie auf… Da rattert wieder etwas über die Systeme.

Probiert werden die User:

– root
– admin
– mail
– Alphanetworks
– MANAGER
– test
– Factory
– vodafone
– telecomadmin
– draytek
– super
– FIELD
– ADVMAIL
– WP
– HELLO
– citel
– SPOOLMAN
– comcast
– wlseuser
– OPERATOR
– PCUSER
– MGR
– patrol
– netadmin
– anonymous
– craft
– websecadm
– netman
– MD110
– supervisor
– tiger
– manuf
– PBX
– NETWORK
– MDaemon
– readonly
– davox
– scout
– blank
– coress
– usw. usw. usw…..

Spannenderweise mit immer neuen Adressen aus verschiedenen großen Netzen. Im groben lässt es sich „eindampfen“ auf:

– 117.253.0.0/16
– 109.161.236.0/22
– 189.51.16.0/20

Hier und da noch etwas verteiltes… Indien, Brasilien, Italien, wenn man dem WHOIS trauen kann. Was da wieder los ist? Dann wird wohl bald wieder mehr Spam kommen, hm?

Ich wünsche in jedem Fall ein paar klebrige Finger!

Jan 11 11:42:53 ssh-honeypot sshd[21822]: Invalid user MANAGER from 182.74.23.34
Jan 11 11:42:53 ssh-honeypot sshd[21822]: input_userauth_request: invalid user MANAGER [preauth]
Jan 11 11:42:56 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2
Jan 11 11:42:58 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2
Jan 11 11:43:01 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2
Jan 11 11:43:03 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2
Jan 11 11:43:05 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2
Jan 11 11:43:08 ssh-honeypot sshd[21822]: Failed password for invalid user MANAGER from 182.74.23.34 port 2657 ssh2

Siehe auch: SSH-Server absichern mit MFA, SSH Brute Force, Raspberry Pi als Angriffsziel: SSH-Brute-Force auf den User pi, SSH-Brute-Force mit veralteter Implementierung: Angriffsmuster erkennen​

Fragen? Einfach melden.

« Ältere Beiträge

© 2026 -=Kernel-Error=-RSS

Theme von Anders NorénHoch ↑