ich habe einen paket-sniffer geschrieben, um protokolle zu lernen, und dann gelogen, was er erkennt

cybersecurity · Oct 4, 2026 · 5 min read

ich habe sniffcli gebaut, weil ich verstehen wollte, was tatsächlich über mein netzwerk geht. ich hatte jahrelang tcpdump benutzt und war völlig unfähig, eine einzige zeile seiner ausgabe zu erklären.

der plan war einfach: schreibe das ding selbst, lies die pakete, lerne die protokolle.

der plan hat funktioniert. nur nicht in der richtung, die ich erwartet hatte.

zuerst der ehrliche teil

die readme sagt, das werkzeug mache „ssh brute-force detection". tut es nicht. hier ist die gesamte funktion:

if dstPort == 22 {
  sshCounters[info.Source]++
  if sshCounters[info.Source] > 3 {
    info.IsAlert = true
    info.Info = "POTENTIAL SSH BRUTE FORCE"
  }
}

das ist alles. es zählt pakete auf port 22, pro quelladresse, und brüllt ab dem vierten.

das kann keinen brute-force-angriff erkennen. es kann kein passwort sehen, weil ssh die gesamte sitzung verschlüsselt. der authentifizierungsaustausch passiert in einem verschlüsselten tcp-stream, und ein passiver sniffer auf der leitung hat keinen weg hinein. selbst wenn ich tcpdump -A einschalten würde, sähe ich binären chiffretext.

was es also tatsächlich erkennt, ist „etwas in meinem netzwerk hat mehr als dreimal mit port 22 geredet". das ist keine brute-force-erkennung. das ist ein verbindungszähler mit einer beängstigenden nachricht.

warum ich die funktion trotzdem behalten habe

weil sie nicht nutzlos ist, nur schlecht benannt. mehr als drei verbindungen auf port 22 in einem kurzen fenster ist ein echtes signal — ich habe es selbst ausgelöst, mit ssh-wiederholungen nach einem tippfehler. und derselbe mechanismus fängt einen scanner, der das netzwerk abklappert.

was ich hätte tun sollen, ist es als das zu bezeichnen, was es ist. „wiederholte ssh-verbindungen" ist ehrlich. „brute-force-erkennung" verspricht etwas, das der code nicht liefern kann, und diese Zusage habe ich geschrieben.

die allgemeine lehre, die ich unangenehm lange gebraucht habe: ein feature, das nach einer bedrohung benannt ist, impliziert eine fähigkeit, die du vielleicht nicht gebaut hast. wenn dein werkzeug behauptet, angriffe zu erkennen, lautet der test, ob es das sehen kann, was angreifer tatsächlich senden. meins kann davon nichts sehen.

was ich über die protokolle gelernt habe

mehr als erwartet, und nichts davon aus der dokumentation.

die schichtung ist der teil, der endlich klick gemacht hat. ein einzelner frame ist nicht ein ding, er ist ein stapel, und gopacket gibt dir jede schicht einzeln:

if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil {
  // ...
} else if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
  // ...
} else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
  // ...
}

tcp ist nicht „das paket". es ist eine schicht, die ohne ip existieren kann (manche verkapselungen), weshalb der echte code tcp und udp in eigenen zweigen nach den adressschichten prüft, statt eine feste reihenfolge anzunehmen.

„wer hat" ist das, was ein arp-broadcast ist: eine maschine, die das ganze netzwerk fragt „wer hat diese ip, nenn mir deine mac". es ist das erste, was du siehst, wenn du in einem kabelnetzwerk zu sniffen anfängst, und es flutet ständig, weil arp kein gedächtnis hat.

multicast und discovery-traffic ist der größte teil des rauschens. 224.0.0.0/24 für ipv4, ff02:: für ipv6, dazu mdns auf 5353, ssdp auf 1900, dhcp auf 67/68. meine ersten zwanzig minuten sniffing waren fast vollständig das. das flag -c existiert rein, um es zu verstecken.

eine verbindung ist bidirektional, ein paket nicht. das werkzeug liest tcp.SrcPort und tcp.DstPort in jedem einzelnen frame, also erscheint dasselbe gespräch zweimal mit vertauschten ports. es gibt keinen eingebauten reassembler, und ich schreibe keinen. das ist eine echte einschränkung, die ich lieber benenne als vorspiele.

der teil, den ich zweimal falsch hatte

isLocalIP entscheidet, ob ein paket „ungewöhnlicher ausgang" ist. es behandelt loopback, link-local, multicast und die drei privaten bereiche als lokal.

der fehler: alles innerhalb von 192.168.0.0/16 gilt als lokal, einschließlich anderer maschinen in meinem eigenen netzwerk. ein gerät, das still an das nas daneben exfiltriert, erzeugt also null warnungen. meine regel sagt „verlässt nicht das haus", und das ist nicht dasselbe wie „verlässt nicht meine maschine".

die ehrliche korrektur wäre, gegen die tatsächliche interface-adresse zu vergleichen, nicht gegen eine reihe möglicher netzwerke. ich habe es nicht korrigiert. es steht stattdessen hier, und das ist der einzige grund, warum dieser absatz existiert.

der teil, auf den ich am stolzesten bin

eine nebenläufige ui, die nicht ruckelt.

der sniffer läuft in seiner eigenen goroutine und schiebt pakete in das tea-programm:

p := tea.NewProgram(m, tea.WithAltScreen())
go startSniffing(iface, watchlist, p)
if _, err := p.Run(); err != nil {
  // ...
}

die schleife fasst die ui nie direkt an, sie sendet nur nachrichten. das ist der ganze grund, warum die interface während eines verkehrsausbruchs reaktionsfähig bleibt — eine regel, die ich aus der frontend-seite meiner eigenen seite schon kannte und erst richtig gewürdigt habe, als die ui ein terminal statt des dom war.

es ist auch der grund, warum HighPerformanceRendering ausdrücklich auf false steht: das ist ein schreibgeschütztes log, in dem der benutzer scrollt, also ist es billiger, bubbletea frames überspringen zu lassen, als sie glatt zu halten.

und die kleine sache, die es angenehm zu benutzen machte: eine alias-karte für geräte, damit 192.168.1.185 als „main rig" erscheint und eine überwachte mac in orange auftaucht. sicherheitswerkzeuge haben den ruf, unangenehm zu sein, und die hälfte davon, eines angenehm zu machen, ist, dem benutzer sein eigenes haus in menschenwörtern zu zeigen.

was ich meinem vergangenen ich sagen würde

öffne die datei, die du geschrieben hast, und zähle die zeilen, die tatsächlich tun, was der name behauptet.

wenn die antwort drei zeilen sind und sie etwas zählen, das der bedrohung benachbart ist, hast du einen verbindungszähler geschrieben. benenne es um, bevor jemand ihm vertraut — auch du, sechs monate später, wenn du dich fragst, warum sich die warnungen falsch anfühlen.