Honeypot mit systemd und fail2ban

Da es eine Rückfrage zu der verlinkten Anleitung in diesem Beitrag bzw. Thema gab, habe ich das mit einem aktuellen Debian nochmal nachgebaut.

Aufbau

Im Grunde wird mit systemd auf bestimmten Ports gelauscht, die Anfragen an diese Ports über ein einfaches Skript in ein separates Log geschrieben, welches von fail2ban überwacht wird. Bei einem Match wird die IP-Adresse dauerhaft gesperrt. Zum Großteil basiert dieses Vorgehen weiterhin auf der Anleitung von gehirn-mag.net mit nur ein paar kleinen Anpassungen.

systemd und fail2ban ist auf den meisten Servern bereits vorhanden, d.h. für einen Honeypot ist keine weitere Software notwendig.

  • Getestet wurden die nachfolgenden Schritte mit einem frisch installierten Debian 12. Während der Installation wurde „Standard-Systemwerkzeuge“ ausgewählt.
  • Wird ein anderes Betriebssystem als Debian verwendet, muss man ggf. Pfade anpassen oder andere Tools verwenden.
  • Da mein privater Anschluss nur IPv4 kann, wurden IPv6-spezifische Dinge nicht getestet. Abhängig davon, ob IPv4 und/oder IPv6 verwendet wird, müsste man weitere Anpassungen vornehmen.
  • In der Standardinstallation sind net-tools und fail2ban nicht vorinstalliert, daher mit apt install net-tools fail2ban nachinstallieren und fail2ban einrichten.
  • Damit der Honeypot auch Anfragen erhält, müssen die Ports von außen erreichbar sein - u.a. muss eine Portweiterleitung in Router/Firewall eingerichtet sein (ggf. bestehende Firewall-Regeln prüfen).

Wichtig: systemd, fail2ban und ufw sind weit verbreitete Standard-Tools. Sollten Anpassungen notwendig sein oder Probleme auftreten, sollte die erste Anlaufstelle die offizielle Dokumentation des jeweiligen Tools sein. Sollten darüber hinaus noch Fragen auftauchen, lassen sich viele mithilfe einer Suchmaschine beantworten. Der Großteil der Fragen wurden schon mal gestellt und beantwortet. Eine Suchmaschine ist auch bei Fehlermeldungen sehr hilfreich, sodass man meistens das eigentliche Problem und eine passende Lösung findet.

Alle Schritte als root durchführen.

systemd

/etc/systemd/system/honeypot.socket

[Unit]
Description=Honeypot
After=network.target

[Socket]
# fuer IPv4 only
# ListenStream=0.0.0.0:7

# ist standardmaessig IPv6
# kann aber auch IPv4 sein, wenn IPv6 z.B. im Kernel deaktiviert wurde
ListenStream=7

Accept=yes
CollectMode=inactive-or-failed # fehlgeschlagene Verbindungen aus dem Speicher freigegeben

[Install]
WantedBy=multi-user.target

Die ListenStream-Zeilen enthalten die Portnummern, die als Honeypot verwendet werden. Diese lassen sich beliebig anpassen/erweitern.

Beispiel für eine Socket-Unit mit mehreren Ports
[Unit]
Description=Honeypot
After=network.target

[Socket]
ListenStream=7
ListenStream=20
ListenStream=22
ListenStream=23
ListenStream=115
Accept=yes
CollectMode=inactive-or-failed

[Install]
WantedBy=multi-user.target

Weitere Informationen findet man in der Dokumentation, u.a. eine Erklärung zu CollectMode

It is recommended to set CollectMode=inactive-or-failed for service instances activated via Accept=yes, to ensure that failed connection services are cleaned up and released from memory, and do not accumulate.

/etc/systemd/system/honeypot@.service

[Unit]
Description=Honeypot
After=network.target

[Service]
ExecStart=/usr/local/bin/honeypot.sh %I
StandardInput=socket
StandardOutput=append:/var/log/honeypot/honeypot.log
StandardError=append:/var/log/honeypot/honeypot.log
Restart=on-failure

[Install]
WantedBy=multi-user.target

/usr/local/bin/honeypot.sh

#!/bin/bash

echo "$(date '+%Y-%m-%d %H:%M:%S') connection from $REMOTE_ADDR" >> /var/log/honeypot/honeypot.log
  • Skript ausführbar machen (chmod +x /usr/local/bin/honeypot.sh).
  • Log Verzeichnis anlegen (mkdir /var/log/honeypot/)
  • leeres Logfile anlegen, damit fail2ban im nächsten Abscnhitt keine Fehler wirft (touch /var/log/honeypot/honeypot.log)

Dienst starten und geöffnete Ports prüfen

systemctl daemon-reload
systemctl enable honeypot.socket
systemctl start honeypot.socket

Hinweis: honeypot@.service muss nicht aktiviert oder gestartet werden, da diese Datei nur ein Template für Service-Units ist. Die Socket-Unit erstellt daraus dann Service-Units.

Mit netstat -tuln lässt sich nun prüfen, ob die Ports geöffnet sind:

Proto Recv-Q Send-Q Local Address          Foreign Address         State
tcp        0      0 0.0.0.0:7              0.0.0.0:*               LISTEN
udp        0      0 0.0.0.0:68             0.0.0.0:*

Der systemd Abschnitt ist damit vollständig. Der oder die Ports sind geöffnet und Anfragen werden geloggt. Damit die Anfragen an den Honeypot auch geblockt werden, ist der nächste Abschnitt relevant.

fail2ban

/etc/fail2ban/filter.d/honeypot.local

[Definition]
datepattern = ^%%Y-%%m-%%d %%H:%%M:%%S
failregex = connection from <HOST>$
ignoreregex =

/etc/fail2ban/jail.d/honeypot.local

[honeypot]
backend = auto
enabled = true
filter = honeypot
logpath = /var/log/honeypot/honeypot.log
bantime = 604800 # = 1 woche
maxretry = 1
banaction = ufw

Abhängig von der übergeordneten Konfiguration (/etc/fail2ban/jail.local) können einzelne Zeilen auch weggelassen werden. Sollte diese Datei nicht existieren, muss fail2ban noch „eingerichtet“ werden → Suchmaschine nutzen.

fail2ban neu starten

systemctl restart fail2ban.service

Tests

Nachdem nun alles eingerichtet wurde, lässt sich das Ergebnis mit curl, fail2ban-client status honeypot und ufw status prüfen.

Mit einem bleibigen Clienten und bspw. cURL kann man einen Request an den Honeypot senden:

# curl SERVER-IP:PORT
curl 192.168.178.109:7

Am Client sieht das Ergebnis dann so aus:

curl: (56) Recv failure: Die Verbindung wurde vom Kommunikationspartner zurückgesetzt

fail2ban-client status honeypot

Status for the jail: honeypot
|- Filter
|  |- Currently failed:	0
|  |- Total failed:	1
|  `- File list:	/var/log/honeypot/honeypot.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	1
   `- Banned IP list:	192.168.178.50

ufw status

Status: active

To            Action      From
--            ------      ----
Anywhere      REJECT      192.168.178.50      # by Fail2Ban after 1 attempts against honeypot
5 „Gefällt mir“

Vielen Dank für die Anleitung. Das ist genau das, was ich für meinen kleinen Server noch gesucht habe.
Und was krass ist: innerhalb von zwei Stunden wurden 5 IP’s geblockt!!!

Was mir noch als Ergänzung einfällt: vielleicht könnte man noch erwähnen, dass man die per honeypot.socket definierten Ports auch in ufw und im Router freigeben muss. Sonst kommt ja von außen Niemand an die Ports und es wird auch nichts geblockt.

Guter Punkt, hab es ergänzt. Danke :+1:

1 „Gefällt mir“

Funktioniert das für TCP und UDP?
Ab wann wird eine Verbindung erkannt, bereits bei einem TCP-SYC-Packet, oder erst wenn wirklich eine Verbindung steht?
Ich frage da sich das System ggf. gegen einen ausnutzen kann wenn gespoofte Adressen ins Spiel kommen.
Gruß Sascha

ListenStream ist TCP. Möchte man UDP verwenden, müsste man ListenDatagram verwenden, siehe systemd.socket.html#Options

Der Handshake ist vollständig abgeschlossen (SYN, SYN/ACK, ACK), siehe tcpdump -i any port 7 -n -v

Details

Erstes Datenpaket (SYN)

08:39:09.300706 enp1s0 In  IP (tos 0x0, ttl 64, id 51058, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.178.54.41328 > 192.168.178.109.7: Flags [S], cksum 0xe623 (incorrect -> 0x393d), seq 1316605500, win 64240, options [mss 1460,sackOK,TS val 2848638436 ecr 0,nop,wscale 7], length 0

Zweites Datenpaket (SYN-ACK)

08:39:09.300758 enp1s0 Out IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.178.109.7 > 192.168.178.54.41328: Flags [S.], cksum 0xe623 (incorrect -> 0xce38), seq 1170679746, ack 1316605501, win 65160, options [mss 1460,sackOK,TS val 2366663873 ecr 2848638436,nop,wscale 7], length 0

Drittes Datenpaket (ACK)

08:39:09.301018 enp1s0 In  IP (tos 0x0, ttl 64, id 51059, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.178.54.41328 > 192.168.178.109.7: Flags [.], cksum 0xe61b (incorrect -> 0xf997), ack 1, win 502, options [nop,nop,TS val 2848638436 ecr 2366663873], length 0

Jetzt ist der Handshake abgeschlossen und es folgt ein Datenpaket vom Client an den Server:

08:39:09.301138 enp1s0 In  IP (tos 0x0, ttl 64, id 51060, offset 0, flags [DF], proto TCP (6), length 133)
    192.168.178.54.41328 > 192.168.178.109.7: Flags [P.], cksum 0xe66c (incorrect -> 0xebba), seq 1:82, ack 1, win 502, options [nop,nop,TS val 2848638436 ecr 2366663873], length 81

… dann die Bestätigung des Servers

08:39:09.301159 enp1s0 Out IP (tos 0x0, ttl 64, id 549, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.178.109.7 > 192.168.178.54.41328: Flags [.], cksum 0xe61b (incorrect -> 0xf93e), ack 82, win 509, options [nop,nop,TS val 2366663874 ecr 2848638436], length 0

… gefolgt vom serverseitigen zurücksetzen der Verbindung:

08:39:09.325380 enp1s0 Out IP (tos 0x0, ttl 64, id 550, offset 0, flags [DF], proto TCP (6), length 52)
    192.168.178.109.7 > 192.168.178.54.41328: Flags [R.], cksum 0xe61b (incorrect -> 0xf922), seq 1, ack 82, win 509, options [nop,nop,TS val 2366663898 ecr 2848638436], length 0

Clientseitig sieht das so aus:

curl --trace - 192.168.178.109:7

== Info:   Trying 192.168.178.109:7...
== Info: Connected to 192.168.178.109 (192.168.178.109) port 7 (#0)
=> Send header, 81 bytes (0x51)
0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..
0010: 48 6f 73 74 3a 20 31 39 32 2e 31 36 38 2e 31 37 Host: 192.168.17
0020: 38 2e 31 30 39 3a 37 0d 0a 55 73 65 72 2d 41 67 8.109:7..User-Ag
0030: 65 6e 74 3a 20 63 75 72 6c 2f 37 2e 37 34 2e 30 ent: curl/7.74.0
0040: 0d 0a 41 63 63 65 70 74 3a 20 2a 2f 2a 0d 0a 0d ..Accept: */*...
0050: 0a                                              .
== Info: Recv failure: Die Verbindung wurde vom Kommunikationspartner zurückgesetzt
== Info: Closing connection 0
curl: (56) Recv failure: Die Verbindung wurde vom Kommunikationspartner zurückgesetzt

Das ginge dann schon eher in Richtung zielgerichteter Aktionen. Im Grunde geht es parallel zu anderen Maßnahmen wie Geo-Blocking, dem Einsatz von Blocklisten oder das Blockieren ganzer IP-Adressblöcke um die Reduzierung des „Grundrauschens“.