j'ai écrit un sniffer de paquets pour apprendre les protocoles, puis menti sur ce qu'il détecte
cybersecurity · Oct 4, 2026 · 5 min read
j'ai construit sniffcli parce que je voulais comprendre ce qui traverse réellement mon réseau. j'utilisais tcpdump depuis des années et restais complètement incapable d'expliquer ce que signifiait une seule ligne de sa sortie.
le plan était simple : écrire la chose moi-même, lire les paquets, apprendre les protocoles.
le plan a marché. mais pas dans la direction que j'attendais.
## d'abord la partie honnête
le readme dit que l'outil fait de la « détection de force brute ssh ». il ne le fait pas. voici la fonctionnalité entière :
```go
if dstPort == 22 {
sshCounters[info.Source]++
if sshCounters[info.Source] > 3 {
info.IsAlert = true
info.Info = "POTENTIAL SSH BRUTE FORCE"
}
}
```
c'est tout. il compte les paquets vers le port 22, par adresse source, et crie après le quatrième.
cela ne peut pas détecter une attaque par force brute. **il ne peut pas voir de mot de passe, parce que ssh chiffre toute la session.** l'échange d'authentification a lieu à l'intérieur d'un flux tcp chiffré, et un sniffer passif posé sur le câble n'a aucun moyen d'y entrer. même si j'activais `tcpdump -A` je verrais du chiffré binaire.
ce qu'il détecte réellement, c'est « quelque chose sur mon réseau a parlé au port 22 plus de trois fois ». ce n'est pas de la détection de force brute. c'est un compteur de connexions avec un message inquiétant collé dessus.
## pourquoi j'ai quand même gardé la fonctionnalité
parce qu'elle n'est pas inutile, elle est juste mal nommée. plus de trois connexions au port 22 dans une courte fenêtre *est* un vrai signal — je l'ai déclenché moi-même avec des `ssh` qui réessaient après une faute de frappe. et le même mécanisme attrape un scanner qui parcourt le réseau.
ce que j'aurais dû faire, c'est l'étiqueter pour ce qu'il est. « connexions ssh répétées » est honnête. « détection de force brute » promet quelque chose que le code ne peut pas livrer, et c'est moi qui ai écrit cette promesse.
la leçon générale, que j'ai mise absurdement longtemps à apprendre : **une fonctionnalité nommée d'après une menace sous-entend une capacité que vous n'avez peut-être pas construite.** si votre outil prétend détecter des attaques, le test est de savoir s'il voit ce que les attaquants envoient réellement. le mien n'en voit aucune trace.
## ce que j'ai appris sur les protocoles
plus que je ne l'espérais, et rien de tout ça dans la documentation.
c'est le cloisonnement qui a enfin cliqué. une seule trame n'est pas une chose, c'est une pile, et gopacket vous donne chaque couche séparément :
```go
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 n'est pas « le paquet ». c'est une couche qui peut exister sans ip (certaines encapsulations), et c'est pourquoi le vrai code teste tcp et udp dans leur propre branche après les couches d'adresse, au lieu de supposer un ordre fixe.
**« qui a »** est ce qu'est une diffusion arp : une machine qui demande à tout le réseau « qui a cette ip, donnez-moi votre mac ». c'est la première chose que vous voyez si vous commencez à sniff sur un réseau câblé, et ça inonde en permanence parce qu'arp n'a pas de mémoire.
**le multicast et le trafic de découverte** font l'essentiel du bruit. `224.0.0.0/24` pour ipv4, `ff02::` pour ipv6, plus mdns sur 5353, ssdp sur 1900, dhcp sur 67/68. mes vingt premières minutes de sniffing étaient presque entièrement ça. le drapeau `-c` existe uniquement pour le masquer.
**une connexion est bidirectionnelle, un paquet ne l'est pas.** l'outil lit `tcp.SrcPort` et `tcp.DstPort` sur chaque trame, donc la même conversation apparaît deux fois avec les ports inversés. il n'y a pas de réassemblage intégré, et je n'en écris pas. c'est une vraie limitation et je préfère la nommer que la prétendre.
## la partie où je me suis trompé deux fois
`isLocalIP` décide si un paquet est une « sortie inhabituelle ». il considère le loopback, le link-local, le multicast et les trois plages privées comme locaux.
le bug : **tout ce qui est dans `192.168.0.0/16` compte comme local, y compris les autres machines de mon propre réseau.** donc un appareil qui exfilte discrètement vers le nas d'à côté produit zéro alerte. ma règle dit « ne quitte pas la maison », ce qui n'est pas la même chose que « ne quitte pas ma machine ».
la bonne correction serait de comparer à l'adresse réelle de l'interface, pas à une plage de réseaux possibles. je ne l'ai pas corrigé. il est écrit ici à la place, et c'est la seule raison pour laquelle ce paragraphe existe.
## la partie dont je suis le plus content
une ui concurrente qui ne saccade pas.
le sniffer tourne dans sa propre goroutine et pousse les paquets dans le programme tea :
```go
p := tea.NewProgram(m, tea.WithAltScreen())
go startSniffing(iface, watchlist, p)
if _, err := p.Run(); err != nil {
// ...
}
```
la boucle ne touche jamais l'ui directement, elle envoie seulement des messages. c'est toute la raison pour laquelle l'interface reste réactive pendant un pic de trafic — une règle que je connaissais déjà du côté frontend de mon propre site, et que je n'ai vraiment appréciée que lorsque l'ui est devenue un terminal au lieu du dom.
c'est aussi pourquoi `HighPerformanceRendering` est explicitement mis à `false` : c'est un journal en lecture seule que l'utilisateur fait défiler, donc laisser bubbletea sauter des frames coûte moins cher que de les garder fluides.
et la petite chose qui a rendu l'usage agréable : une table d'alias d'appareils, pour que `192.168.1.185` s'affiche comme « machine principale » et qu'une mac surveillée apparaisse en orange. les outils de sécurité ont la réputation d'être désagréables, et la moitié du travail pour rendre l'un d'eux agréable, c'est de laisser l'utilisateur voir sa propre maison en mots humains.
## ce que je dirais à mon moi d'avant
ouvrez le fichier que vous avez écrit et comptez les lignes qui font vraiment ce que le nom annonce.
si la réponse est trois lignes et qu'elles comptent quelque chose de voisin de la menace, vous avez écrit un compteur de connexions. renommez-le avant que quelqu'un lui fasse confiance — y compris vous, six mois plus tard, quand vous vous demanderez pourquoi les alertes semblent fausses.