Fehlkonfig as a Serice

In die Netze von Internetprovidern speisen die Kunden alles ein, was nicht lokal abgehandelt wird. Gebraucht gekaufte IoT-Geräte, internen Traffic, der am Firmen-VPN vorbei leaked, etc. pp. Ein Großteil davon geht an private IPv4 Adressen, die grundsätzlich nicht erreichbar sind. Leider geben sich die meisten Geräte nicht mit Ablehnungen und Fehlermeldungen zufrieden, sondern bestehen auf einer Antwort. Und wenn sie dafür zig-Mal pro Sekunde anfragen müssen.

Catch All

Die Idee besteht nun darin, Anfragen an garantiert unzustellbare Adressen in einer Weise zu beantworten, dass die anfragenden Geräte zufrieden sind, aber keinen Schaden anrichten können. Gleichzeitig soll der – möglicherweise irritierte – Nutzer erkennen können, was da schief geht. Dazu wird zunächst eine Maschine aufgesetzt, die all den fehlerhaften Traffic abbekommt. Dazu wird der Traffic an nicht anderweitig bekannte, private IPs an diese Kiste geroutet:

(conf)# ip route 10.0.0.0/8 a.b.c.d
(conf)# ip route 172.16.0.0/12 a.b.c.d
(conf)# ip route 192.168.0.0/16 a.b.c.d

Warum nicht auch noch den Shared-IP-Space 100.64.0.0/10? Weil wir den selbst benutzen für die Masse der Endkunden.

Was kommt denn da so geflogen?

100.76.113.229.55323 > 192.168.40.12.389: Flags [S]
100.75.2.125.54724 > 192.168.121.1.20080: UDP
100.98.190.49 > 172.18.10.10: ICMP echo request
100.75.253.235.65019 > 10.3.76.152.389: Flags [S]
100.75.121.232.13564 > 10.10.10.3.18899: Flags [S]
100.75.196.34.63612 > 10.10.10.3.18899: Flags [S]
100.75.8.243.63753 > 10.1.7.20.53: 43789+ A? europe.smartscreen.microsoft.com. (50)
100.75.226.148.62734 > 10.10.10.3.18899: Flags [S]
100.74.166.72.59236 > 10.10.10.3.18899: Flags [S]
100.76.98.43.65100 > 192.168.2.200.22000: Flags [SEW]
100.75.177.69.51982 > 10.10.10.3.18899: Flags [S]

Da haben wir also Ping, LDAP (vermutlich Kontaktversuche in ein AD), DNS (natürlich), die Steuerkonsole für PV-Anlagen (what?) und verschiedene seltsame Ports. Und alles an wilde private Ziele.

Destination NAT

Um auf diese Anfragen reagieren zu können, müssen alle die Pakete auf die lokale Maschine umgeleitet werden. Und dann müssen die Antworten wieder so aussehen, als ob sie von dem eigentlich angefragten Servern stammen. Als lokale Adresse möchte ich die IP 127.0.0.2 auf einem zweiten Loopback oder Dummy-Interface benutzen.

FreeBSD

Nimmt man FreeBSD so gibt es die Implementationen: ipfw und pf. Beide beherrschen den das Umleitung von Pakten an ein anderes Ziel

ipfw add fwd 127.0.0.2 ip from any to 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 recv veth0
(pf) rdr pass inet from any to {10.0.0.0/8,172.16.0.0/12,192.168.0.0/16} -> 127.0.0.2

Wenn dann an dem betreffenden Port ein Dienst lauscht, so antwortet der und die Antwort geht von der lokalen IP wieder nach draußen:

100.79.236.62.42046 > 172.20.10.1.53: 40857*- ...
127.0.0.2.53 > 100.79.236.62.42046: 40857*- ...

Damit kann natürlich niemand etwas anfangen. Es müsst schon von der gleichen privaten IP eine Antwort kommen.

Aber wie? Mit NAT kann man die IP Adressen umschreiben, sich die Umschreibung merken und wieder zurück wandeln. Nur gibt es leider abertausende von möglichen Zielen, die zu natten wären. ipfw-NAT kennt redirect_address und akzeptiert dort auch den Spezialparameter 0.0.0.0.

ipfw nat 1 config if veth0 same_ports redirect_addr 127.0.0.2 0.0.0.0
ipfw add nat 1 from any to 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 recv veth0
ipfw add nat 1 from 127.0.0.2 to an xmit veth0

Der Traffic muss auf dem Hin- und auf dem Rückweg in die richtige NAT-Instanz geschoben werden, damit diese die richtigen Adressen umschreiben kann. In welche Richtung die NAT-Instanz arbeitet, hängt von der Richtung ab, in der das Paket das Interface passieren will. Es macht also einen Unterschied, auf welchem Interface man den Traffic zum NAT umleitet.

Unglücklicherweise funktioniert das aber überhaupt nicht. Es scheint daran zu liegen, dass die IP-Adressen auch aus einem Bereich stammen. Da scheint die Erkennung dessen, was zu tun ist, durcheinander zu kommen.

Linux

Also der Versuch mit Linux. Das alte iptables hat sich im Rahmen des Netfilter-Projektes weiter entwickelt:

flush ruleset

table ip fremdtraffic {
        chain catch {
                type nat hook prerouting priority dstnat; policy accept;
                ip daddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } counter dnat to 127.0.0.2
        }
}
table ip dienste {
        chain management {
                type filter hook input priority filter; policy accept;
                ip saddr m.g.m.t/net counter accept
                tcp dport { 22, ... } counter reject
        }
}

Die Tables sind Verwaltungs-Container, die man atomar einfügen, aktivieren und löschen kann. Sie gelten für einen bestimmte Art von Daten, hier IPv4.

Die Chains enthalten sequentiell abzuarbeitende Blöcke von Regeln gleicher Funktion (type) an der gleichen Eingriffstelle (hook). Endet ein Regel mit reject, wird das Paket verworfen. Endet eine Regel mit accept, geht's mit der nächsten Chain weiter. Nicht von Regeln erfasste Pakete bekommen die generelle Policy der Chain zu spüren, Die Reihenfolge (priority) der Chains wirkt pro Eingriffstelle (hook). Haben zwei Chains die gleiche Priorität, ist es unbestimmt, welche Chain zuerst dran kommt.

Von dem o.g. Regelwerk bleibt also übrig:

  • Der Hook prerouting führt seine einzige Regel aus, indem er Pakete an private IPs auf die lokale IP umschreibt (und sich das merkt)
  • Der Hook input führt seine einzige Regel für lokal adressierten Traffic aus und filtert Zugriffe auf Management-Dienste aus, wenn sie nicht von dem zulässigen Managment-Netz stammen.
  • Auf dem Rückweg greift das Nat automatisch ein und übersetzt die Adressen wieder zurück

Aktuell existieren keine Dienste, aber der Kernel kann schon ein paar Dinge selbst:

100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
172.18.10.10 > 100.98.190.49: ICMP echo reply
172.18.10.10 > 100.98.190.49: ICMP echo reply
172.18.10.10 > 100.98.190.49: ICMP echo reply
172.18.10.10 > 100.98.190.49: ICMP echo reply
100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
100.98.190.49 > 172.18.10.10: ICMP echo request
172.18.10.10 > 100.98.190.49: ICMP echo reply
172.18.10.10 > 100.98.190.49: ICMP echo reply
172.18.10.10 > 100.98.190.49: ICMP echo reply

Sieht schon mal gut aus. Der Kernel hat ein Rate-Limit, beantwortet also nicht alle Anfragen.

100.75.121.212.57726 > 192.168.0.101.55443: Flags [S]
192.168.0.101.55443 > 100.75.121.212.57726: Flags [R.]
100.75.196.199.55444 > 192.168.1.166.55444: UDP
192.168.1.166 > 100.75.196.199: ICMP 192.168.1.166 udp port 55444 unreachable

Auch Anfragen an unbekannte Dienste werden korrekt beantwortet: Dienst nicht verfügbar.

Das sieht man dann auch an den Zählern:

 # nft list ruleset
table ip fremdtraffic {
        chain catch {
                type nat hook prerouting priority dstnat; policy accept;
                ip daddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } counter packets 2598458 bytes 166126098 dnat to 127.0.0.2
        }
}
table ip dienste {
        chain management {
                type filter hook input priority filter; policy accept;
                ip saddr m.g.m.t/net counter packets 1880 bytes 333471 accept
                tcp dport { 22, ... } counter packets 21572 bytes 1231360 reject
        }
}

Post a comment

Verwandter Inhalt