Ping ist keine Diagnose. Es ist ein Tunnel. ICMP Echo (die Anfrage, die eine Maschine sendet, und die Antwort, die sie zurückbekommt) trägt jedes Byte, das du hineinlegst, in beide Richtungen, über ein Protokoll, das die meisten Firewalls durchlassen, ohne hineinzusehen, und das die meiste Protokollierung zählt statt liest. Ein Netz, das „nur Ping erlaubt“, hat schon ein volles, verschlüsselbares VPN nach draußen, und jeder, der dieses Netz erreichen kann, kann eines fahren.

Das ist alt, und es ist dokumentiert. Loki1 hat die Technik 1996 in Phrack 49 veröffentlicht. Ptunnel2 trägt seit zwanzig Jahren ganze TCP-Sitzungen in Ping und ist auf jeder Linux-Kiste ein Paket entfernt. Kein Zero-Day. Das Protokoll verhält sich genau wie festgelegt.

Das ist die Ursache: der Standard, kein Fehler in irgendjemandes Produkt. Jeder Host auf der Welt ist verpflichtet, die Daten in einer Echo-Anfrage zu nehmen und sie unverändert direkt zurückzugeben. Beliebige Daten hinein, dieselben Daten heraus: das ist das Ganze, was ein Tunnel braucht, und es ist seit 1981 vorgeschrieben.

Die Behebung ist eine einzige enge Änderung. Verwirf ICMP Echo, Anfrage und Antwort, auf IPv4 und IPv6, an der Grenze, und behalte jede andere ICMP-Nachricht. Die Fehler — Time Exceeded, Packet Too Big, Destination Unreachable — tragen Last; blockiere die, und du zerstörst Path MTU Discovery und Traceroute für nichts. Das ist also nicht „ICMP blockieren“. Es ist, die eine Nachricht zu verwerfen, die eine Haftung ist, und die zu behalten, die die Wahrheit tragen.

Was deine Ausgangsregel tatsächlich erlaubt hat

Betreibst du ein Netz, in dem ausgehender Verkehr gefiltert wird (und wenn nicht, ist das ein eigenes Gespräch), dann ist das hier die Rechnung für eine Zeile darin. Die Regel, die sagt „blockiere alles ausgehende, erlaube ICMP, weil wir Dinge pingen müssen“, ist keine Ausgangsregel. Es ist ein voller Tunnel, dessen Unterlagen unter Diagnose abgelegt sind. Alles auf der Innenseite, das eine Echo-Anfrage senden und die Antwort lesen kann, kann Daten überallhin auf der Außenseite bewegen, die eine beantwortet, mit der Rate, die die Verbindung hergibt, und an jeder Inhaltskontrolle vorbei, die du gekauft hast. In den Protokollen ist es jemand, der prüft, ob das Internet läuft, und nichts weiter. Schadsoftware liefert das seit Jahren, aus genau diesem Grund. Leise, standardmäßig, und meist schon erlaubt.

Deshalb ist es ein Kanal erster Wahl für jeden, der schon drinnen ist und nicht drinnen sein sollte. Es ist das Wohnen, nicht der Einbruch mit Aufbruch. Wer einen Weg hinein und heraus sucht, der hält, meidet den Port, der einen Alarm auslöst. Er stellt einen Tunnel auf Echo und lässt ihn monatelang laufen. Ein Host, der viel pingt, ist ein Host, den niemand beobachtet. Zwei-Wege-Vernetzung in den Bestand: Befehl hinein, Daten heraus, über das eine Protokoll, das niemand drosselt, alarmiert oder liest. Und er ist verschlüsselt, wie jedes echte Werkzeug es verschlüsselt, auf dem Draht ist die Nutzlast also die zufällig aussehenden Byte, die ein Ping sowieso trägt, und die Inhaltsprüfung hat nichts zu lesen. Größe und Zeitverlauf können ihn noch verraten, an jemanden, der tatsächlich hinsieht. Ein Blick in das Paket kann es nicht.

Und die Maschine, die ihn fährt, muss niemandem gehören, der dort arbeitet. Alles, was es braucht, ist etwas, das dein Netz erreichen und einen Ping auf das Internet legen kann, und dein Netz zu erreichen ist leichter, als irgendwer zugeben mag. Das WLAN trägt über die Wände hinaus, in den Flur, den Parkplatz, die Wohnung darüber. Die Ethernet-Buchse in der Wand des Besprechungsraums, oder an der Rezeption, oder am leeren Schreibtisch am Fenster, ist sehr oft live und spricht mit allem, was du einsteckst, ohne ein 802.1X, das fragt, wer du bist. Ein Signal in Reichweite, oder eine Buchse, die niemand zugesperrt hat. Das ist die Eintrittsgebühr. Kein Ausweis, kein Konto, keine Einladung.

Stell dir den Besucher vor, den du eingeladen hast. Er ist in der Besprechung, freundlich, macht Notizen, trägt bei. Sein Laptop nicht. In dem Moment, in dem er dein Netz erreicht hat, über die Luft oder durch die Buchse unter dem Tisch, war er innerhalb der Wand, und wenn dieses Netz eine Echo-Anfrage abgeben kann, hat er einen Weg hinaus. Nichts wurde auf deinem Bestand abgelegt. Kein Konto, kein Recht auf irgendetwas, das dir gehört, nichts, das deine Endpunkt-Agenten fangen könnten, denn deine Agenten sind nicht auf seiner Maschine. Die Person ist am anderen Ende des Tisches. Der Verkehr verlässt das Haus durch deine Vordertür, verschlüsselt, nicht zu unterscheiden von einem Laptop, der mehr pingt, als er sollte.

Ein Besucher in Signalreichweite oder an einer offenen Buchse bekommt einen Tunnel hinaus, durch eine Firewall, die nur Pings siehtEin Besucher in Reichweite, oder an einer offenen Buchse, bekommt einen Weg hinausinnerhalb der Wand — dein NetzWLAN reicht inden Flur,Parkplatz,Wohnung darüberLaptop des Besucherskein Ausweis, kein Kontoeine live Wandbuchsekein 802.1X fragt, wer du bistGrenz-Firewallsieht nur PingsServerim InternetEcho-Anfrage →← Echo-Antwortbeliebiger IP-Verkehr, verschlüsselt, in den Pings reitend
Die Eintrittsgebühr ist Zugang zum Netz — ein WLAN-Signal in Reichweite, oder eine live Wandbuchse ohne 802.1X —, und der Ausgang ist Echo durch die Grenze. Für die Firewall ist es ein Host, der pingt; in diesen Pings ist beliebiger IP-Verkehr, verschlüsselt, gebunden an einen Server im Internet.

Die zwei Dinge, nach denen du greifen wirst, helfen nicht. NAT ist eine Übersetzungstabelle, kein Filter: eine Echo-Anfrage von einem Host innen öffnet eine Abbildung, geschlüsselt auf die ICMP-id, die NAT genau wie einen Port behandelt, und die Antwort kommt darüber zurück wie jeder andere Fluss. Ich habe einen Client hinter meinem eigenen Heim-NAT gefahren, und es hat nie bemerkt, dass das NAT da war. Ein eigenes Gast-VLAN ist nicht besser. Trennung hält den Besucher von deinen Servern fern, aber sie tut nichts, um ihn vom Internet fernzuhalten, und das Internet, mit einem Ping erreicht, ist die ganze Anforderung. Eine Kontrolle berührt das: lässt dieses Netz Echo hinaus?

Du prüfst das nicht weg, und du trennst es nicht weg. Du schließt die Tür. Alles danach ist, warum der Standard sie offen lässt, drei Wege, den Tunnel zu bauen, damit die Behauptung auf mehr als meinem Wort steht, und die eine Änderung, die ihn schließt.

Der Teil von ICMP, der dir nichts Nützliches schuldet

Fang mit dem an, was der Standard tatsächlich sagt, denn das ganze Argument ruht auf einem Satz, und Leute wischen ihn weg, ohne ihn zu lesen.

Eine Echo-Anfrage trägt ein Datenfeld. In IPv4 stellt RFC 7923 schlicht fest, dass „the data received in the echo message must be returned in the echo reply message“. IPv6 hat die Formulierung verschärft statt gelockert: RFC 44434 definiert das Feld als „zero or more octets of arbitrary data“ und verlangt dann, dass es „MUST be returned entirely and unmodified in the ICMPv6 Echo Reply message“.

Lies das als Betreiber, und es ist ein Keepalive. Lies es als jemand, der Daten bewegt, und es ist ein Geschenk. Der Standard verpflichtet jeden erreichbaren Host, einen Block Byte anzunehmen, den du wählst, und sie dir unverändert direkt zurückzuschicken, auf Verlangen. Beliebige Länge. Beliebiger Inhalt. Kein Handshake, kein Port, keine Anwendung am anderen Ende, die irgendetwas zustimmen muss. Der Kernel tut es, bevor irgendein Userland-Prozess einen Blick hineinbekommt.

Und es sind beide Familien. Der Wechsel zu IPv6 räumt das nicht weg. Es öffnet es weiter. RFC 792 sagte, die Daten „must be returned“; RFC 4443 sagt, sie „MUST be returned entirely and unmodified“, und das ist dieselbe Tür mit einem stärkeren Schloss, das sie offen hält. Der Kanal existiert also auf ICMP Echo und auf ICMPv6 Echo gleichermaßen, und eine Regel, die ihn auf einer Familie schließt und nicht auf der anderen, hat nichts geschlossen. Der Tunnel zieht bloß auf die Familie um, die du offen gelassen hast. Was du auch dagegen tust, tu es auf ip und ip6 zusammen.

Nichts sonst im Protokoll tut das. Ein Time Exceeded trägt den Header des Pakets, das gestorben ist, und nicht mehr. Ein Destination Unreachable ist ein Bericht über etwas, das schon passiert ist. Diese Nachrichten sagen dir Tatsachen über das Netz. Echo trägt, was immer du hineinlegst, in beide Richtungen, und nennt es eine Diagnose.

Fehler tragen Last. Echo nicht.

Das ist die Unterscheidung, die die „einfach ICMP blockieren“-Fraktion nie macht, und sie ist der ganze Punkt, ich mache sie also einmal und richtig.

ICMP-Fehler zu blockieren zerstört das Netz leise. Filtere Packet Too Big, und du tötest Path MTU Discovery: der Handshake läuft durch, kleine Übertragungen funktionieren, und alles, was ein Paket voller Größe trägt, hängt für immer, ohne etwas in den Protokollen. Auf IPv6 ist das nicht einmal Geschmackssache. Router fragmentieren nicht, RFC 48905 führt Packet Too Big also unter den Nachrichten auf, die eine Firewall „must not drop“, und warnt, dass ohne es „parts of the Internet will become inaccessible“. Filtere Time Exceeded, und du zerstörst Traceroute, das RFC 18126 als den Grund nennt, warum die Nachricht überhaupt Pflicht ist. Diese sind nicht wahlfrei. Sie sind die Rückmeldung, die das Netz selbstkorrigierend macht, und ich habe die Kosten, sie zu verlieren, letztes Mal ausführlich behandelt.

Verwirf jetzt Echo und geh suchen, was kaputtging. ping über die Grenze hört auf zu funktionieren. Das ist die Liste. Die ganze Liste.

Path MTU Discovery ist es egal, denn es läuft auf Packet Too Big, und das ist ein Fehler. Traceroute ist es egal, denn traceroute -T und -U laufen über TCP und UDP und lesen die Fehler, die zurückkommen. Nicht eines davon sendet ein Echo. Neighbour Discovery auf IPv6 ist es egal, denn das sind die Typen 133 bis 137, und die behältst du, oder das Segment stirbt. Der Trick mit der Rück-TTL aus dem letzten Beitrag funktioniert auf jeder Antwort, und ein TCP-Handshake gibt dir eine. Alles, was die Diagnosen des letzten Beitrags funktionieren ließ, funktioniert weiter, denn nicht eine Messung darin hat eine Echo-Anfrage gesendet.

Die zwei Hälften des Protokolls könnten sich also nicht unähnlicher sein. Die eine Hälfte ist das Netz, das dir die Wahrheit über sich selbst sagt, und du zerstörst sie auf eigene Gefahr. Die andere Hälfte ist ein vorgeschriebener Nutzlast-Rückgabedienst, der zufällig Diagnose heißt, und das Stärkste, was irgendwer dafür sagen kann, ihn offen zu halten, ist, dass ping praktisch ist. Es ist praktisch. Es ist auch der einzige Teil von ICMP, den ein Angreifer fahren kann, und ich kann dir genau zeigen, womit sie ihn fahren.

Behalte die ICMP-Fehler, ratenbegrenzt; verwirf nur Echo, beide Richtungen und beide FamilienZwei Hälften eines Protokolls, und nur eine davon ist eine HaftungBEHALTENratenbegrenzen, nie blockierenDestination Unreachableträgt, warum ein Paket nicht zugestellt werden konntePacket Too BigPath MTU Discovery hängt daranTime ExceededTraceroute hängt daranParameter Problemmeldet einen fehlerhaften HeaderNeighbour Discovery 133–137IPv6: das lokale Segment hängt daranZerstöre diese, und das Netz zerbricht leise.VERWERFENbeide Richtungen, beide FamilienEcho-AnfrageTyp 8 (IPv4) · Typ 128 (IPv6)Echo-AntwortTyp 0 (IPv4) · Typ 129 (IPv6)Nichts braucht diese außer demping-Befehl.Alles, was ein Angreiferdurch ICMP fährt, läuft durch sie.Ein Tunnel, auf einer Familie offen gelassen, ist nicht geschlossen.
Die ICMP-Fehler tragen Last: Path MTU Discovery, Traceroute und IPv6 Neighbour Discovery hängen alle an ihnen, und sie zu blockieren zerstört das Netz leise. Echo ist der einzige Teil, an dem nichts hängt außer ping, und der einzige Teil, den ein Angreifer fahren kann. Verwirf den, behalte den Rest, auf beiden Familien.

Der Beweis: ein VPN aus Ping

Das Werkzeug ist Hans7, geschrieben von Friedrich Schöller. Seine eigene Beschreibung ist eine Zeile: es „makes it possible to tunnel IPv4 through ICMP echo packets, so you could call it a ping tunnel.“ Es bringt an jedem Ende ein tun-Interface hoch, gibt ihnen Adressen und bewegt jedes Paket zwischen ihnen innerhalb von ICMP Echo. Für das Netz dazwischen ist es jemand, der einen Server pingt, und der Server, der antwortet. Für mich ist es eine Route.

Ein ganzes IP-Paket reist im Datenfeld eines einzelnen PingsEin ganzes Paket reitet in einem PingDas Paket, das deine SSH-Sitzung sendetIP-HeaderQuelle / ZielTCP-HeaderPort 22Anwendungsdatendeine Tastenanschläge, deine DateiHans kopiert das ganze Paket in das Echo-DatenfeldDas Paket, das auf dem Draht das Haus verlässtIP-Headerdu zum ServerICMP-Echo-AnfrageTyp 8 · id · seqEcho-Datenfelddas ganze Paket oben, unverändertFür die Grenz-Firewall ist das eine Echo-Anfrage, und das Protokoll erfasst einen Ping.Im Datenfeld ist ein Paket, gebunden überallhin, wohin das ferne Ende reichen kann.Der Standard verlangt vom fernen Ende, diese Daten direkt zurückzuschicken, die Antwort ist also auch ein Paket.
Jedes Paket, das der Tunnel trägt, wird in das Datenfeld einer ICMP-Echo-Anfrage kopiert. Der Standard verpflichtet das ferne Ende, diese Daten unverändert zurückzugeben, die Antwort trägt also auch ein Paket. Die Firewall zählt einen Ping; die Nutzlast geht überallhin, wohin das ferne Ende routen kann.

Ich habe es über meine eigene Leitung gefahren, ein Server auf einer öffentlichen Adresse und ein Client hinter meinem Heim-NAT, und echten Verkehr hindurchgeschoben. Keine künstlichen Pings mit gesetztem Flag. Eine SSH-Anmeldung und ein Dateiabruf, in Echo reitend.

Es ist ein Paket entfernt, wo ich den Server gefahren habe, und es baut sich aus Schöllers Quellcode überall sonst. Der Server muss Linux sein und braucht root, denn ein tun-Gerät zu öffnen und ein rohes ICMP-Socket brauchen es beide. Die Syntax ist bewusst klein:

# on the server (public IP), pick the tunnel network and a password
hans -s 10.8.0.0 -p '<password>'
# server takes 10.8.0.1; clients are handed 10.8.0.2 and up

# on the client, point it at the server's public address
hans -c <server-public-ip> -p '<password>'

Sind beide Enden oben, gibt es an jeder Seite ein neues Interface mit einer Adresse auf dem Tunnelnetz, und es verhält sich wie jede andere Punkt-zu-Punkt-Verbindung:

Nichts an dieser Sitzung weiß, dass sie in Ping steckt. SSH öffnet eine TCP-Verbindung zu 10.8.0.1, der Kernel routet sie hinaus über tun0, Hans wickelt jedes Paket als Nutzlast einer Echo-Anfrage ein, und das ferne Ende wickelt es aus und gibt es an sein eigenes tun0. Die Antwort kommt als Nutzlast einer Echo-Antwort zurück. Soweit es SSH angeht, spricht es über eine gewöhnliche Verbindung. Soweit es die Firewall angeht, hat niemand etwas geöffnet. Ein Host wird gepingt.

Wie es auf dem Draht aussieht

Das ist der Teil, der das Argument beendet, sieh dir also die Firewall-Sicht an statt meiner.

Was wirklich passiert und was die Firewall protokolliert, sind nicht dasselbe BildEin Tunnel, zwei BilderWas wirklich passiertdein Hosttun0 · 10.8.0.2der Servertun0 · 10.8.0.1überallhin, wohiner reichen kannSSH, Dateiabrufehinausgeroutetbeliebiger IP-Verkehr, durch den Tunnelals gewöhnliche PaketeWas die Grenz-Firewall sieht und protokolliertdein Host198.51.100.9der Server192.0.2.7Echo-AnfrageEcho-Antworticmp echo request 198.51.100.9 > 192.0.2.7icmp echo reply 192.0.2.7 > 198.51.100.9icmp echo request 198.51.100.9 > 192.0.2.7... als Pings gezählt, Inhalt ungelesenDerselbe Draht. Die Firewall sieht nie die Pakete darin.
Der Tunnel bewegt echten Verkehr zwischen zwei Hosts und dann hinaus überallhin, wohin der Server reichen kann. An der Grenze ist es nur Echo-Anfrage und Echo-Antwort — die Firewall protokolliert Pings und sieht nie die Pakete, die darin getragen werden.

Sitz auf dem äußeren Interface mit tcpdump und fang nur ICMP ein, während die SSH-Sitzung läuft. Kein TCP zu Port 22 überquert die Grenze. Was überquert, ist Echo-Anfrage und Echo-Antwort, hin und her, jede dicker als ein echter Ping, weil sie ein Stück eines TCP-Segments in ihrer Nutzlast trägt:

# on the boundary, watch only ICMP echo while traffic runs over the tunnel
tcpdump -ni <wan-iface> 'icmp[icmptype] = icmp-echo or icmp[icmptype] = icmp-echoreply'

Lies die zwei Dinge, auf die es in dieser Aufnahme ankommt. Erstens die Nutzlastlängen: ein normaler ping sendet 56 Byte und jede Zeile ist gleich groß, während diese sich unterscheiden und groß ausfallen, weil die Größe des Dings, das du bewegst, in die Größe des Pings durchsickert. Zweitens die Rate: ein Diagnose-Ping ist einer die Sekunde, und das ist eine Flut, weil es eine Datei bewegt. Keins von beiden ist verborgen. Beide sitzen in aller Öffentlichkeit auf einem Protokoll, das sich niemand ansieht.

Und das ist der ganze Punkt. Nicht, dass das klug ist oder schwer zu bemerken, wenn man hinsieht. Fast niemand sieht hin, denn die Kiste ist eingestellt, ICMP zu erlauben, die Protokolle zählen es als Pings, und der Alarm wurde auf die Ports abgestimmt, um die sich jemand zu sorgen erinnert hat. Der Verkehr verlässt das Haus wie eine Gesundheitsprüfung, und die Gesundheitsprüfung ist eine Route überallhin, wohin der Server reichen kann.

Ein richtiges Full-Tunnel-VPN, von Hand gebaut

Hans beweist, dass der Kanal da ist, aber es macht das Interface und die Adressierung für dich und gibt eine Route zurück, es zeigt dir also nicht ganz, was du gebaut hast. Um zu sehen, dass das ein VPN im vollen Sinn ist — der Verkehr der ganzen Maschine, der über Ping das Haus verlässt, keine Verbindung zwischen zwei benannten Hosts —, setz es von Hand zusammen. icmptunnel8, von Dhaval Kapil, ist das dafür, und seine eigene Beschreibung ist eine einzige Zeile: „Transparently tunnel your IP traffic through ICMP echo and reply packets.“ Dieselbe Idee, tun-Gerät und Echo-Nutzlasten, aber du legst die Verrohrung selbst, und nichts versteckt sich in einer Binärdatei.

Auf dem Server startest du den Tunnel, bringst das Interface hoch und tust dann das, was das ganze Spiel verrät: sag dem Kernel, selbst keine Pings mehr zu beantworten, damit seine eigenen Echo-Antworten nicht mit denen streiten, die der Tunnel sendet.

sudo ./icmptunnel -s 10.0.1.1                 # server mode; creates tun0, then blocks
# from a second shell, bring the interface up (iproute2, not the net-tools the repo ships)
sudo ip addr add 10.0.1.1/24 dev tun0
sudo ip addr add 2001:db8:1::1/64 dev tun0
sudo ip link set tun0 mtu 1472 up             # 1500 − 20 (IP) − 8 (ICMP); see below
# stop the kernel replying to pings — echo now belongs to the tunnel
sudo sysctl -w net.ipv4.icmp_echo_ignore_all=1
sudo sysctl -w net.ipv6.icmp.echo_ignore_all=1
# let the server route the client's packets onward
sudo sysctl -w net.ipv4.ip_forward=1

Lies die icmp_echo_ignore_all-Zeile noch einmal, denn sie sagt mehr, als sie aussieht. Dieses Stellrad ist das des Kernels selbst, und es kommt als Verteidigung: setz es, und, in den Worten des Kernels, es „will ignore all ICMP ECHO requests sent to it“9, ein Betreiber kann einen Host also ganz vom Ping-Radar nehmen. Es ist jetzt auf beiden Familien. net.ipv4.icmp_echo_ignore_all seit Jahren, und net.ipv6.icmp.echo_ignore_all später hinzugefügt, um es abzugleichen.10 Der Tunnel legt diesen Verteidigungsschalter aus dem umgekehrten Grund um: da der Kernel Echo nicht mehr selbst beantwortet, sind seine zwei Enden frei, Echo als reinen Transport zu nutzen. Was das Anzeichen ist. Die Leute, die den Stack geschrieben haben, behandeln Echo schon als etwas, das ein Host vernünftigerweise verweigern darf — die Regel in diesem Beitrag trifft dieselbe Entscheidung einmal, an der Grenze, für jeden Host dahinter.

Auf dem Client bringst du das Interface hoch und zeigst dann die Standardroute hinein. Nicht ein Host, der durch den Tunnel erreicht wird. Alles.

sudo ./icmptunnel -c <server-public-ip>       # client mode; creates tun0
sudo ip addr add 10.0.1.2/24 dev tun0
sudo ip addr add 2001:db8:1::2/64 dev tun0
sudo ip link set tun0 mtu 1472 up
# keep the route to the server itself OUT of the tunnel...
sudo ip route add <server-public-ip> via <gateway> dev <iface>
# ...then send everything else down it
sudo ip route replace default dev tun0

Die eigenen client.sh und server.sh des Projekts greifen weiter nach net-tools ifconfig und route. Die ip-Befehle oben sind die iproute2-Entsprechungen und machen dieselbe Aufgabe. Diese Route zum Server ist die Zeile, die Leute vergessen: halte sie aus dem Tunnel heraus, oder die Echo-Pakete, die den Tunnel tragen, versuchen, durch den Tunnel zu reisen, und nichts verlässt das Haus. Alles andere geht jetzt durch tun0, in Echo gewickelt, und erreicht den Server. Ob es weiter geht, ist eine Routing-Entscheidung. Eine einzelne Masquerade-Regel auf dem Server würde es unter der eigenen Adresse des Servers auf das öffentliche Internet legen, und sie ist absichtlich nicht hier, denn der Tunnel ist das, was gezeigt wird, und er braucht sie nicht.

Das ist ein Full-Tunnel-VPN, aus Ping gebaut, in einer Handvoll Befehle. Jedes Paket, das der Client sendet — Web, DNS, SSH, alles davon — wird in tun0 eingefangen und verlässt das Haus als Echo-Anfrage zum Server, und die Antworten kommen als Echo-Antworten zurück. Eine Maschine in einem Netz, das „nur ICMP erlaubt“, hat gerade ihren gesamten ausgehenden Verkehr an eine Kiste draußen übergeben, und die Grenze hat einen Host protokolliert, der gern pingt.

Selbst gebaut, in Python

Hier ist der Teil, der jeden beunruhigen sollte, der hofft, sich dagegen zu verteidigen, indem er ein Werkzeug erkennt. Weder Hans noch icmptunnel tut etwas, das du nicht selbst an einem Nachmittag schreiben könntest. Öffne ein tun-Gerät, wickle jedes Paket als Nutzlast einer Echo-Anfrage ein, wickle die aus, die zurückkommen. Das ist der ganze Mechanismus, und der nackte Tunnel sind etwa sechzig Zeilen Python aus der Standardbibliothek, mit überhaupt nichts zu installieren. Die Fassung unten legt eine Sache obendrauf: sie verschlüsselt die Nutzlast, und das ist der einzige Teil, der eine Abhängigkeit hereinzieht.

#!/usr/bin/env python3
# pingvpn.py — a VPN tunnel over ICMP echo, in one short file.
#
# Not a product. It exists to show the channel is trivial to rebuild, so a
# defence that hunts for a known tool is chasing the wrong thing entirely.
# Linux, needs root (a tun device and a raw ICMP socket both do), and the
# `cryptography` package for the AES (pip install cryptography).
#
#   server:  sudo python3 pingvpn.py --server
#   client:  sudo python3 pingvpn.py --client <server-public-ip>
#
# The payload is encrypted with AES-128-GCM under a pre-shared key before it
# goes on the wire, so what a firewall sees in the echo data is random bytes —
# the same as a real ping's padding, and nothing for content inspection to read.

import argparse
import fcntl
import hashlib
import os
import select
import socket
import struct
import sys

from cryptography.hazmat.primitives.ciphers.aead import AESGCM

TUNSETIFF    = 0x400454CA
IFF_TUN      = 0x0001
IFF_NO_PI    = 0x1000
MAGIC        = 0x4954          # 'IT' in the id field, so we ignore real pings
ECHO_REQUEST = 8
ECHO_REPLY   = 0

PSK   = b"change-me-to-a-shared-secret"          # pre-shared secret, both ends
KEY   = hashlib.sha256(PSK).digest()[:16]        # 128-bit key -> AES-128-GCM
AEAD  = AESGCM(KEY)


def open_tun(name=b"tun0"):
    fd = os.open("/dev/net/tun", os.O_RDWR)
    fcntl.ioctl(fd, TUNSETIFF, struct.pack("16sH", name, IFF_TUN | IFF_NO_PI))
    return fd


def encrypt(data):                                # -> nonce || ciphertext+tag
    nonce = os.urandom(12)
    return nonce + AEAD.encrypt(nonce, data, None)


def decrypt(blob):                                # raises on a packet that is not ours
    return AEAD.decrypt(blob[:12], blob[12:], None)


def checksum(data):
    if len(data) % 2:
        data += b"\x00"
    total = sum(struct.unpack("!%dH" % (len(data) // 2), data))
    total = (total >> 16) + (total & 0xFFFF)
    total += total >> 16
    return ~total & 0xFFFF


def build_echo(icmp_type, payload):
    head = struct.pack("!BBHHH", icmp_type, 0, 0, MAGIC, 0)
    csum = checksum(head + payload)
    return struct.pack("!BBHHH", icmp_type, 0, csum, MAGIC, 0) + payload


def main():
    ap = argparse.ArgumentParser(description="a VPN tunnel over ICMP echo")
    group = ap.add_mutually_exclusive_group(required=True)
    group.add_argument("--server", action="store_true")
    group.add_argument("--client", metavar="SERVER_IP")
    args = ap.parse_args()

    out_type = ECHO_REQUEST if args.client else ECHO_REPLY
    in_type  = ECHO_REPLY   if args.client else ECHO_REQUEST
    peer     = args.client        # None on the server until a client is seen

    tun  = open_tun()
    sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
    print("tun0 created. bring it up with an address and route, then send traffic.",
          file=sys.stderr)

    while True:
        readable, _, _ = select.select([tun, sock], [], [])

        if tun in readable:                       # a packet wants to leave this host
            packet = os.read(tun, 65535)
            if peer:
                sock.sendto(build_echo(out_type, encrypt(packet)), (peer, 0))

        if sock in readable:                      # something arrived over ICMP
            data, _ = sock.recvfrom(65535)
            ihl  = (data[0] & 0x0F) * 4           # skip the IP header the kernel adds
            icmp = data[ihl:]
            if len(icmp) < 8 or icmp[0] != in_type or icmp[4:6] != struct.pack("!H", MAGIC):
                continue
            try:
                packet = decrypt(icmp[8:])        # wrong key or a real ping -> skip
            except Exception:
                continue
            if args.server:
                peer = socket.inet_ntoa(data[12:16])   # reply to whoever sent
            os.write(tun, packet)                 # hand the carried packet to the stack


if __name__ == "__main__":
    main()

Das ist der ganze Tunnel. Er öffnet tun0 und ein rohes ICMP-Socket und schiebt Pakete zwischen ihnen: was den Host verlässt, wird als Echo-Anfrage gewickelt, oder auf dem Server als Echo-Antwort, und an das ferne Ende gesendet; was über ICMP ankommt, wird ausgewickelt und dem Stack zurückgegeben. Das id-Feld ist auf einen Wert festgenagelt, damit es über echte Pings hinwegsteigt, und der Server lernt aus der Quelle des ersten Pakets, das er sieht, wohin er antworten soll.

Die Verschlüsselung ist der Punkt, bei dem es sich zu verweilen lohnt, denn sie ist, was echte Werkzeuge tun, und sie ist, warum du das nicht fängst, indem du in das Paket siehst. Die Nutzlast wird mit AES-128-GCM unter einem vorab geteilten Schlüssel versiegelt, bevor sie gewickelt wird, die Byte im Echo-Datenfeld sind also nicht von der zufälligen Füllung zu unterscheiden, die ein echter ping trägt. Streich die zwei Krypto-Zeilen heraus, und der Tunnel läuft weiter auf nichts als der Standardbibliothek — der einzige Grund, warum er pip install cryptography braucht, ist das AES, und das AES ist genau der Teil, der einen lesbaren Tunnel in einen unlesbaren verwandelt. Bring ihn genauso hoch wie zuvor, ohne die Masquerade. Du brauchst sie nicht, um den Punkt zu beweisen:

# server: start it (creates tun0, then blocks), then configure from the same shell
sudo python3 pingvpn.py --server &
sudo ip addr add 10.9.0.1/24 dev tun0
sudo ip addr add 2001:db8:9::1/64 dev tun0
sudo ip link set tun0 mtu 1400 up
sudo sysctl -w net.ipv4.icmp_echo_ignore_all=1
sudo sysctl -w net.ipv6.icmp.echo_ignore_all=1

# client
sudo python3 pingvpn.py --client <server-public-ip> &
sudo ip addr add 10.9.0.2/24 dev tun0
sudo ip addr add 2001:db8:9::2/64 dev tun0
sudo ip link set tun0 mtu 1400 up
sudo sysctl -w net.ipv4.icmp_echo_ignore_all=1
sudo sysctl -w net.ipv6.icmp.echo_ignore_all=1
sudo ip route add <server-public-ip> via <gateway> dev <iface>
sudo ip route replace default dev tun0

Der Grund, es auszuschreiben, ist nicht das Werkzeug. Es ist, dass das Werkzeug wegwerfbar ist. Eine kurze Datei, keine Abhängigkeiten, bis du die Verschlüsselung hinzufügst, und jede Kopie, die jemand tippt, sieht auf dem Draht ein wenig anders aus. Eine Signatur, die diese eine fängt, fängt also nächste Woche nichts. Du kannst dich hier nicht herausblockieren, indem du die Software benennst, denn es gibt keine Software zu benennen.

Es braucht nicht einmal eine Shell. Die Logik ist Byte hinein, Byte heraus, es portiert sich also auf alles, das ein Socket öffnen kann. Selbst WebAssembly: der Browser-Sandkasten verweigert einer Seite ein rohes Socket normalerweise rundweg, aber wo diese Barriere aufgehoben ist (ein Browser, dem der Nutzer die Erlaubnis erteilt hat, auf einer Maschine, wo der Nutzer mit Rechten genug läuft, dass ein rohes Socket geöffnet werden kann), läuft dasselbe kurze Programm in einem Tab. Was die unbequeme Hälfte davon ist. Du kannst deinen Nutzern hier nicht vertrauen. Der Host, der den Tunnel fährt, ist auf der Innenseite, gehalten von jemandem, den du für sicher hieltest, weil er hinter der Firewall sitzt, und die Firewall ist das, wodurch getunnelt wird. Perimeter-Vertrauen nimmt an, die Bedrohung sei außerhalb der Wand. Diese fängt innerhalb an, jedes Mal.

Achte auf die MTU, und IPv6’ Boden von 1280

Der Tunnel ist auf dem Draht nicht kostenlos. Jedes Paket, das du trägst, gewinnt einen äußeren IP-Header und einen ICMP-Header, bevor es das Haus verlässt, das innere Interface muss also unter dem sitzen, was der Pfad tragen kann. Auf IPv4 kostet das den Angreifer fast nichts. Setz die innere MTU niedrig (icmptunnels 1472 sind 1500 weniger 20 für den äußeren IP-Header und 8 für den ICMP-Header, und das Python oben fällt auf 1400 für Spielraum), und wo die Zahl noch falsch ist, fragmentiert IPv4 das übergroße Paket und setzt es am fernen Ende wieder zusammen, statt es zu verwerfen. Zwischen dem niedrigen Boden, den du wählen kannst, und der Fragmentierung, die über den Rest hinwegtapeziert, läuft der Tunnel über fast jeden Pfad. Genau diese Nachgiebigkeit macht IPv4 zum bequemen Ort dafür.

IPv4' Fragmentierung und MTU-Spielraum lassen den Tunnel überall laufen; IPv6 hat keins von beiden, es scheitert also geschlossenWarum er auf IPv4 überall läuft und auf IPv6 geschlossen scheitertIPv4IP-Hdr20 BICMP8 Binneres Pakettun MTU 1472 BZu groß? IPv4 fragmentiert und setzt wieder zusammen — der Tunnel läuft über fast jeden Pfad.IPv6IPv6-Hdr40 BICMPv68 Binneres Paketkann nicht unter 1280 B1280 B harter Boden (RFC 8200)Zu groß? IPv6-Router verwerfen es, keine Fragmentierung — der Tunnel scheitert geschlossen.IPv4' Rückfalllösungen — ein Boden, den du wählen kannst, Fragmentierung für den Rest — machen den Tunnel verlässlich.IPv6 hat keins von beiden, der Angriff, der auf IPv4 fast überall läuft, ist auf IPv6 also zerbrechlich. Die sicherere Familie hier.Packet Too Big — ein Fehler, den du behältst, kein Echo — lässt einen Sender die passende Größe finden. Nicht maßstabsgetreu.
Der Tunnel verliert von jedem Paket einen äußeren IP- und ICMP-Header. IPv4 lässt dich die innere MTU abschaben und fragmentiert, was noch zu groß ist, es läuft also fast überall; IPv6 setzt einen harten Boden von 1280 Byte, und seine Router fragmentieren nicht, ein übergroßes Paket wird also verworfen und der Tunnel scheitert geschlossen — was IPv6 hier zur sichereren Familie macht.

IPv6 ist weniger nachgiebig, und ausnahmsweise ist das auf deiner Seite. Der Boden ist hart: RFC 820011 §5 verlangt, dass „every link in the Internet have an MTU of 1280 octets or greater“, und IPv6-Router fragmentieren nicht unterwegs. Das nimmt beide Rückfalllösungen von IPv4 auf einmal, und es beißt den Tunnel in beide Richtungen. Fahr ihn über ICMPv6, und jede Verbindung auf dem Pfad muss 1280 tragen, ein Pfad, der darunter fällt, nimmt also den Transport herunter, und es gibt kein Abschaben unter dem Boden, wie du es auf IPv4 kannst. Trag IPv6 innerhalb des Tunnels, und du triffst dieselbe Wand von der anderen Seite: das innere Interface kann auch nicht unter 1280, während der äußere ICMPv6-Umschlag, 40 Byte Header und 8 ICMPv6, das Budget schon ausgibt, der Pfad muss also 1280 plus den Overhead erübrigen. Verfehl es an einer der beiden Stellen, und das Paket wird verworfen, nicht auf Größe geschnitten: der Tunnel kommt hoch, kleine Dinge funktionieren, alles voller Größe hängt. IPv6 ist hier also die sicherere Familie, nicht die riskantere. Der Angriff, der auf IPv4 über fast alles läuft, ist auf IPv6 zerbrechlich und scheitert geschlossen. Sicherer ist nicht dasselbe wie sicher, wohlgemerkt: der Echo-Kanal ist auch auf IPv6 offen, die Regel verwirft ihn also weiter auf beiden Familien — der Angreifer kann sich bloß nicht auf IPv6 lehnen, wie er sich auf IPv4 lehnt.

Die Erholung davon ist der ICMP-Fehler, den zu behalten du sorgfältig warst. Packet Too Big ist, was einen Sender die funktionierende Größe finden lässt, und es ist ein Fehler, kein Echo, die Regel, für die dieser Beitrag argumentiert, lässt es also in Ruhe. Verwirf den Tunnel und behalte die Diagnosen — das ist der ganze Entwurf, und die MTU ist eine weitere Stelle, an der er sich bezahlt macht.

Die Regel, auf Echo verengt

Der letzte Beitrag gab das volle Transit-Regelwerk: erlaube die Fehler unter einer Ratenbegrenzung, verwirf das Echo. Ich drucke es nicht neu. Die Änderung, für die dieser Beitrag argumentiert, ist ein Paar Zeilen, und es ist das Paar, das die Arbeit macht:

# permit the ICMP errors — these are load-bearing, keep them
ip protocol icmp icmp type { destination-unreachable, time-exceeded, parameter-problem } \
    limit rate 100/second accept
ip6 nexthdr ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, \
    time-exceeded, parameter-problem } limit rate 100/second accept

# drop the tunnel — echo, both directions, both families
ip  protocol icmp     icmp   type { echo-request, echo-reply } drop
ip6 nexthdr ipv6-icmp icmpv6 type { echo-request, echo-reply } drop

Die Regel ist aber nicht die von Linux. Es ist dieselbe Absicht auf jeder Firewall, die den Namen wert ist: erlaube die Fehler, deckle ihre Rate, verwirf Echo in beide Richtungen auf beiden Familien. Hier ist sie also in den Dialekten, die du eher in der Hand hältst.

BSD pf (pfSense, OPNsense, OpenBSD, FreeBSD):

# permit the errors; block echo both directions, both families
pass  in proto icmp  icmp-type  { unreach, timex, paramprob }
pass  in proto icmp6 icmp6-type { unreach, toobig, timex, paramprob, \
                                  routersol, routeradv, neighbrsol, neighbradv }
block in proto icmp  icmp-type  { echoreq, echorep }
block in proto icmp6 icmp6-type { echoreq, echorep }

Behalte die Neighbour-Discovery-Typen auf der icmp6-Zeile; das sind die, die das Segment herunternehmen, wenn du sie verlierst.

Cisco IOS (erweiterte ACLs, an der Kante angewandt):

ip access-list extended ICMP-EDGE
 permit icmp any any unreachable
 permit icmp any any time-exceeded
 permit icmp any any parameter-problem
 deny   icmp any any echo
 deny   icmp any any echo-reply
!
ipv6 access-list ICMP6-EDGE
 permit icmp any any packet-too-big
 permit icmp any any unreachable
 permit icmp any any time-exceeded
 permit icmp any any parameter-problem
 permit icmp any any nd-ns
 permit icmp any any nd-na
 deny   icmp any any echo-request
 deny   icmp any any echo-reply

Auf IPv4 ist der Echo-Typ echo; auf IPv6 ist es echo-request. Die Ratenbegrenzung wohnt in CoPP, nicht in der ACL.

Juniper Junos (Firewall-Filter; der inet6-Filter spiegelt das und behält Neighbour Discovery):

firewall family inet filter icmp-edge {
    term errors {
        from { protocol icmp; icmp-type [ unreachable time-exceeded parameter-problem ]; }
        then { policer icmp-cap; accept; }
    }
    term drop-echo {
        from { protocol icmp; icmp-type [ echo-request echo-reply ]; }
        then discard;
    }
}

MikroTik RouterOS — verwirf die zwei Echo-Typen, dann akzeptiere den Rest von ICMP, was die Fehler behält und, auf v6, Neighbour Discovery:

/ip firewall filter
add chain=forward protocol=icmp icmp-options=8:0 action=drop comment="echo request"
add chain=forward protocol=icmp icmp-options=0:0 action=drop comment="echo reply"
add chain=forward protocol=icmp action=accept comment="keep the errors (add limit= to cap)"
/ipv6 firewall filter
add chain=forward protocol=icmpv6 icmp-options=128:0 action=drop comment="echo request"
add chain=forward protocol=icmpv6 icmp-options=129:0 action=drop comment="echo reply"
add chain=forward protocol=icmpv6 action=accept comment="keep errors and ND"

Andere Syntax, eine Regel. Erlaube die Nachrichten, die die Wahrheit tragen, verwirf die, die deine Byte trägt.

Zwei Warnungen, die dich beide beißen werden, wenn du überfliegst.

Verwirf Echo an der Grenze, nicht auf dem Draht zwischen einem Host und seinem eigenen Router. Auf IPv6 ist Neighbour Discovery ICMP — Typen 133 bis 137, nd-router-solicit bis nd-redirect — und es ist, wie das Segment die Aufgabe erledigt, die ARP in IPv4 erledigt. Trag ein Echo-Verwerfen auf eine Link-Local- oder Host-Kette, ohne die zu behalten, und das Segment hört innerhalb von Minuten auf zu funktionieren, und es wird nicht wie ein Firewall-Fehler aussehen. Filtere Echo, wo der Verkehr dein Netz verlässt, und lass die internen Ketten in Ruhe.

Verwirf es in beide Richtungen, und es hört so oder so auf, nützlich zu sein. Nur die Anfrage zu blockieren stoppt, dass deine Hosts gepingt werden, lässt aber noch eine Antwort hinaus, und ein Tunnel kann mit ein wenig mehr Aufwand allein auf Antworten gebaut werden. Verwirf Anfrage und Antwort, auf IPv4 und IPv6, und der Kanal ist in beide Richtungen geschlossen.

Was Ping zu verwerfen dich tatsächlich kostet

Sei ehrlich über den Verlust, denn eine Kontrolle, die du überverkauft hast, ist eine Kontrolle, die jemand leise rückgängig macht, sobald es unbequem ist.

Du verlierst ping über die Grenze. Das ist die Kosten, voll ausgesprochen. Es ist eine echte Kosten. ping ist der Reflex, es ist in jedermanns Fingern, und am Tag, nachdem du das ausgeliefert hast, wird jemand sagen, das Internet sei unten, weil sein Ping zum Gateway abläuft. Es ist nicht unten. Du hast dem Gateway gesagt, eine Anfrage nicht mehr zu beantworten, die immer nur eine Bequemlichkeit war.

Erreichbarkeitstests brauchen kein Echo. Eine TCP-Verbindung zu einem Port, von dem du weißt, dass er offen ist, sagt dir, dass der Host oben ist und der Pfad funktioniert, und sagt dir mehr als ein Ping, denn es beweist, dass ein Dienst geantwortet hat, nicht bloß ein Kernel:

# "is it up and reachable?" without sending a single echo
nc -zv <host> 443           # did the TCP handshake complete?
traceroute -T -p 443 <host> # walk the path on TCP, read the errors back

Beide reiten auf den ICMP-Fehlern, die du behalten hast, und dem TCP, das ein echter Dienst schon spricht. Keins sendet ein Echo. Der ehrliche Handel ist also der: du gibst das am wenigsten aussagende Werkzeug im Kasten auf, das, das „ist ein Kernel bereit zu antworten“ beantwortet und nichts weiter, und dafür schließt du den einzigen Teil von ICMP, den ein Angreifer in eine Route aus deinem Netz verwandeln kann. Die Diagnosen, nach denen du an einem schlechten Tag tatsächlich greifst, sind alle auf der anderen Seite der Linie, unberührt.

Ein Fisher-Price-Betriebssystem (Windows) ist kein Ausweg hieraus

Betreibst du einen Laden mit einem Fisher-Price-Betriebssystem (Windows), liest du das vielleicht als jemand anderes Problem. Ist es nicht. Die Haftung liegt im Protokoll, nicht im Betriebssystem. Der Standard verpflichtet jeden Host, der einen Ping beantwortet, deine Byte zurückzugeben, und er hält nicht inne, um zuerst zu fragen, was der Host fährt.

Eine Kiste auf diesem Betriebssystem macht einen völlig guten Tunnelendpunkt. Hans liefert einen Windows-Client, eine Maschine innen kann also das Client-Ende sein und ihren Verkehr in Echo hinausgeben wie jede andere. Was sie nicht leicht sein kann, ist das Server-Ende, denn das will ein tun-Gerät und ein rohes ICMP-Socket offen gehalten, und auf diesem Betriebssystem können nur Mitglieder der Administratoren-Gruppe Sockets vom Typ SOCK_RAW anlegen12 — Microsofts eigene Worte. Der Server sitzt also auf einem echten Betriebssystem, wie meiner, und die Kiste hinter der Firewall, die sich leise hinauspingt, ist die, von der dir gesagt wurde, sie sei sicher, weil sie auf der Innenseite war.

Das ist der Punkt für jeden, der es verteidigt. Die Grenzregel kümmert sich nicht, was das Innere fährt. Verwirf Echo, wo der Verkehr das Netz verlässt, und jeder Host dahinter ist abgedeckt — die, die du verwaltest, und die, von der dir versichert wurde, sie verstecke sich hinter NAT. Ich fahre das Ding nicht, und ich werde nicht so tun, als achte der Tunnel es. Die Behebung ist dieselbe Regel, an derselben Stelle, in was der Endpunkt auch gebootet ist.

Ist die Kiste selbst das, was du härtest (die Kiste, nicht das Netz), wird PowerShell sie zumindest davon abhalten, einen Ping auf eigene Rechnung zu beantworten oder abzugeben:

foreach ($t in 8,0)     { New-NetFirewallRule -DisplayName "Drop ICMPv4 Echo $t" -Protocol ICMPv4 -IcmpType $t -Direction Inbound -Action Block }
foreach ($t in 128,129) { New-NetFirewallRule -DisplayName "Drop ICMPv6 Echo $t" -Protocol ICMPv6 -IcmpType $t -Direction Inbound -Action Block }

Füg -Direction Outbound-Zwillinge hinzu, um sie davon abzuhalten, als Tunnel-Client hinauszugreifen, statt als Ziel dazusitzen, und lass die ICMPv6-Fehler- und Neighbour-Discovery-Typen in Ruhe, aus dem Grund, aus dem sie überall sonst auf dieser Seite in Ruhe gelassen werden. Aber halte es nicht für die Kontrolle. Eine Host-Firewall ist keine Transit-Firewall. Sie härtet die Kiste und ändert nichts an der Grenze, und die Grenze ist, wo das tatsächlich geschlossen wird.

Sprich das heute mit dem an, der deine Grenze betreibt

Du hast das wahrscheinlich bis hierher gelesen und dabei an ein Netz gedacht, das nicht deins zu ändern ist. Die meisten Leute tun das. Das Nützliche ist also nicht, nach der Firewall zu greifen, es ist, die Frage dem zu stellen, der sie hält — deinem eigenen Netzteam, deinem MSP, oder dem Hersteller, dessen Kiste an der Kante sitzt —, und sie heute zu stellen, denn das ist seit 1996 eine offene Tür, und eine weitere Woche davon ist eine Entscheidung.

Frag klar, und frag mit einem leeren Blick rechnend, denn ich würde die ganze Wirtschaftsleistung des Vereinigten Königreichs darauf wetten, dass niemand dort Echo einen zweiten Gedanken geschenkt hat. Es ist das eine Paket, das jeder erlaubt und niemand besitzt, und eine Regel, die niemand besitzt, ist genau die, die seit Jahren falsch dasitzt, ohne dass jemand es bemerkt.

Frag sie vier Dinge, in diesen Worten, damit kein Raum bleibt, zuzunicken und nichts zu ändern:

  • Verwerfen wir ICMP Echo-Anfrage und Echo-Antwort an der Grenze, auf IPv4 und IPv6 beide? Nicht „erlauben wir ICMP“ — die bestimmte Nachricht, beide Richtungen, beide Familien. Ist die Antwort nur eine Familie, ist es keine Antwort, denn der Tunnel zieht bloß auf die andere.
  • Erlauben wir weiter die ICMP-Fehler? Destination Unreachable, Time Exceeded, Parameter Problem, und Packet Too Big auf IPv6, unter einer Ratenbegrenzung statt einer Sperre. Können sie es nicht sagen, stehen die Chancen gleich, dass sie entweder alles offen gelassen oder alles blockiert haben, und beides ist falsch.
  • Haben wir IPv6 Neighbour Discovery auf den internen Ketten behalten? Typen 133 bis 137. Das ist das, was „wir haben ICMP gehärtet“ in ein Segment verwandelt, das eine Woche später leise stirbt, und es ist der Fehler, den eine hastige Änderung macht.
  • Zeig mir die Regel. Keine Grundsatzaussage, die tatsächliche Zeile auf der tatsächlichen Kiste, und das Datum, an dem sie draufkam. Eine Kontrolle, auf die niemand zeigen kann, ist eine Kontrolle, die nicht da ist.

Wird die Kiste vom Hersteller schon so geschlossen angekommen sein? Wird sie nicht. Die Voreinstellung fast überall ist, Echo durchzulassen und es als Paketzahl zu erfassen, was der ganze Grund ist, warum der Tunnel überhaupt funktioniert. Es nutzt keinen Fehler aus, es nutzt die Konfiguration, die als Standard ausgeliefert wird. Niemand schließt eine Tür, von der ihm nie gesagt wurde, dass sie offen war.

Der Grund, auf der Wortwahl zu bestehen, ist, dass die faule Behebung zu diesem Beitrag „gut, wir blockieren ICMP“ ist, und diese Behebung ist schlimmer als das Loch. Sie zerstört Path MTU Discovery und Traceroute, sie wird dich hängenden Übertragungen nachjagen lassen, ohne etwas in den Protokollen, und auf IPv6 nimmt sie Teile des Internets ganz vom Tisch. Greift die Person, die du fragst, danach, halte sie auf. Die Anweisung ist mit Absicht eng: verwirf Echo, behalte die Fehler, behalte Neighbour Discovery. Damit sollte jeder, der Firewalls beruflich betreibt, diese Änderung an einem Nachmittag machen und dir sagen können, dass sie erledigt ist.

Und sind sie ein MSP, den du bezahlst, das zu betreiben: ein Lieferant, der dir deine eigene Echo-und-Fehler-Haltung nicht aus dem Kopf sagen kann, oder der „wir blockieren ICMP“ antwortet, als wäre das die sichere Wahl, hat dir gerade etwas über den Rest des Bestands gesagt. Das nächste Mal, wenn ein Fehler zwischen zwei Netzen sitzt und beide sagen, sie seien sauber, erinnere dich, welches von ihnen nicht beschreiben konnte, was seine eigene Firewall mit einem Ping macht.

Die Diagnose, die nie eine Diagnose war

Ping hatte einen guten Lauf. Mike Muuss hat es 1983 geschrieben, um zu prüfen, ob ein Host antwortet, hat es nach dem Sonar benannt, und es machte diese eine Aufgabe so gut, dass es das Erste wurde, nach dem jeder greift, und das Letzte, das irgendwer hinterfragt. Vierzig Jahre später ist es Muskelgedächtnis, und Muskelgedächtnis ist genau, wie eine Haftung überlebt. Niemand prüft das, was er schon immer getan hat, neu.

Aber sieh an, was es tatsächlich ist, der Gewohnheit entkleidet. Eine Nachricht, die keine Tatsache über das Netz trägt, die jeder Host mit deinen eigenen Byte zurückzugeben verpflichtet ist, die über ein Protokoll läuft, das die Kontrollen nicht prüfen und die Protokolle nicht lesen. Alles, was es harmlos wirken lässt — es ist nur eine Diagnose, es ist nur ein Keepalive, jeder erlaubt es —, ist dasselbe, das es zum saubersten Weg von einem gefilterten Netz macht, den es gibt. Die Branche blockiert die ICMP-Fehler, die die Wahrheit tragen und das Netz zerstören, wenn sie weg sind, und winkt Echo durch, das trägt, was immer du hineinlädst. Sie hat das Protokoll genau verkehrt herum.

Die Behebung ist nicht klug. Behalte die Nachrichten, die dir die Wahrheit sagen, und verwirf die, die bloß deine Byte zurückgibt. Sie kostet dich einen Befehl, dem für eine Diagnose zu vertrauen du ohnehin kein Recht hattest, und sie schließt eine Tür, die seit 1996 offen steht, dokumentiert in einem Hacker-Magazin, seit zwei Jahrzehnten verpackt und weit gelassen, weil sie zu schließen hieße, jemand könnte nicht pingen. Wisse, was ein Ding kostet, nicht, was es ausgezeichnet ist. Ping ist mit nichts ausgezeichnet. Es kostet dich den einen Kanal, den du nicht sehen kannst.


  1. Loki, Phrack 49 — ein Befehlskanal, getragen in ICMP-Echo-Nutzlasten, 1996. ↩︎

  2. Ptunnel — trägt eine TCP-Sitzung in ICMP Echo. ↩︎

  3. RFC 792 — ICMP: „the data received in the echo message must be returned in the echo reply message“. ↩︎

  4. RFC 4443 — ICMPv6: Echo-Daten „MUST be returned entirely and unmodified in the ICMPv6 Echo Reply message“. ↩︎

  5. RFC 4890 — die ICMPv6-Nachrichten, die eine Firewall nicht verwerfen darf. ↩︎

  6. RFC 1812 — Router-Anforderungen: Time Exceeded ist ein MUSS, benannt nach Traceroute. ↩︎

  7. Hans — IP-über-ICMP-Echo-Tunnel, von Friedrich Schöller. ↩︎

  8. icmptunnel — tunnelt IP-Verkehr durch ICMP-Echo- und -Antwort-Pakete, von Dhaval Kapil. ↩︎

  9. Linux-Kernel — IP sysctl — icmp_echo_ignore_all: „the kernel will ignore all ICMP ECHO requests sent to it“. ↩︎

  10. Linux-Commit e6f86b0f — „ipv6: Add icmp_echo_ignore_all support for ICMPv6“ — die IPv6-Entsprechung, von Virgile Jarry. ↩︎

  11. RFC 8200 §5 — IPv6 verlangt, dass jede Verbindung eine MTU von mindestens 1280 Oktetten hat. ↩︎

  12. Microsoft — TCP/IP raw sockets — „only members of the Administrators group can create sockets of type SOCK_RAW“. ↩︎