tcpcat.
Projet communautaire open source
N. — NycolazSec
Note technique : 1337
github.com/NycolazSec/tcpcat
Références : Nmap, RFC 793, eBPF/AF_XDP
Octobre 2026
Catégorie : informationnel — évaluation réseau autorisée
Document communautaire — non commercial

tcpcat : scanner réseau eBPF/AF_XDP, inventaire et analyse d'exposition

Note technique communautaire · v1.5

CI: Go test -race Go 1.26 AGPL-3.0 / commercial Linux · macOS · Windows Projet communautaire
OpenSSF Best Practices badge XERJ Corpus Hub contributor
  [eth0]====-._     _,-'""'-._        _____ ____ ____   ____    _  _____ 
         (,-.'._,'(       |\'-/|      |_   _/ ___|  _ \ / ___|  / \|_   _|
             '-.-' \ )-'( , o o)        | || |   | |_) | |     / _ \ | |  
                   '-    \'_'"'-        | || |___|  __/| |___ / ___ \| |  
    <--[SYN]--(sniffing)--[ACK]-->      |_| \____|_|    \____/_/   \_\_|  
                                        Modular Security & Network Engine
                                         [ eBPF / AF_XDP ] by NycolazSec
Colophon

Statut de ce mémo

Ce document présente tcpcat, un scanner réseau écrit en Go. Cette note communautaire n'est pas une RFC IETF et ne remplace pas Nmap. Le projet est en double licence : AGPL-3.0 ou licence commerciale (OEM).

Résumé

tcpcat énumère les ports ouverts d'un hôte. En mode --ebpf, il forge ses sondes en espace utilisateur et lit les réponses via des sockets AF_XDP, sans passer par la pile TCP/IP du noyau. Surtout, il ne s'arrête pas à « le port est ouvert » : inventory et explain disent quel processus écoute derrière chaque port, et comment fermer ceux qui ne devraient pas l'être.

Démarrage rapide

Local

Installer

go install github.com/\
  NycolazSec/tcpcat/\
  cmd/tcpcat@latest

Scanner un hôte

tcpcat -sT -p 1-1000 \
  192.0.2.10

Exporter un rapport

tcpcat -sV \
  -j report.json 192.0.2.10

1. Introduction et spécifications

§1

tcpcat construit lui-même chaque paquet de sonde en espace utilisateur. En mode --ebpf, il les envoie et lit les réponses via des sockets AF_XDP plutôt que par les sockets BSD classiques, sans passer par la pile TCP/IP du noyau.

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port         |       Destination Port       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgment Number                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum           |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 1 — En-tête TCP (RFC 793 §3.1). C'est l'en-tête que tcpcat forge et lit pour chaque sonde.

2. Capacités système

§2

2.1 Scans et reconnaissance

TCP (SYN, connect, ACK, Window, FIN, Xmas), UDP et ICMP. Découverte d'hôtes, détection de version avec -sV, empreinte d'OS, rapports JSON et SARIF.

2.2 Moteur eBPF / AF_XDP (Linux)

Un hook XDP redirige vers des sockets AF_XDP les réponses aux sondes de tcpcat ; tout le reste du trafic suit la pile normale. Sur un gros scan, cela réduit le coût CPU par paquet. Le programme XDP est assemblé en Go, sans C ni clang.

2.3 Dissecteurs WebAssembly

Un dissecteur est un petit module WebAssembly, en bac à sable, qui parle au port sondé via une fonction hôte unique. Écrit en Go (TinyGo) ou en Rust, il identifie un service et sa version. Voir le SDK.

2.4 Test de visibilité (--evasion)

--evasion applique des contrôles de variation (fragmentation, jitter temporel, leurres) pour vérifier si des défenses autorisées journalisent et alertent sur un scan. À n'utiliser que sur un périmètre approuvé.

3. Exposition : qui écoute, et comment fermer

§3

Lancé sur la machine, inventory lit /proc et note quel processus, unité systemd ou conteneur tient chaque socket en écoute. Copié sur un autre poste, explain sonde la machine depuis l'extérieur et recoupe les deux vues : pour chaque port, qui est derrière et quoi faire. Pour un port exposé non prévu, il nomme le responsable et le correctif.

# sur l'hôte
$ tcpcat inventory -o inventory.json
# sur ton poste
$ tcpcat explain inventory.json --expect 22

  HOST     web-01  ·  203.0.113.5
  INSIDE   3 listening socket(s)

  TO FIX (1)  reachable from outside, not declared with --expect
    6379/tcp   EXPOSED    redis-server · redis.service                 HIGH
                          reachable from outside · listens on 0.0.0.0
                          → Set `bind 127.0.0.1 ::1` and `protected-mode yes`
                            in redis.conf.

  PUBLIC AS EXPECTED (1)
    22/tcp     EXPOSED    sshd · ssh.service

  LOCAL ONLY (1)  5432

  ✗ 1 port(s) reachable that should not be.

explain --remediate écrit en plus la règle nftables, le drop-in systemd ou la NetworkPolicy qui ferme l'exposition. tcpcat n'applique rien lui-même : il produit du texte à relire.

4. Benchmark comparatif

§4

Scan SYN des 65 535 ports sur 2 hôtes, même interface et même plafond de débit (25 000 paquets/s) pour les trois outils, 3 exécutions chacun, mesuré avec hyperfine. Chaque exécution est vérifiée : trouve-t-elle bien les 80 ports ouverts ?

Outil Moyenne ± σ CPU Ports trouvés
nmap5.38 s ± 0.032.7 s80/80
tcpcat --ebpf5.81 s ± 0.726.3 s80/80
tcpcat -sS6.33 s ± 0.1042.3 s80/80
naabu18.94 s ± 0.3951.5 s80/80
Commande de mesure exacte
# depuis une copie du dépôt, Docker suffit
$ ./benchmarks/run.sh

# par outil, l'équivalent de :
$ tcpcat -p 1-65535 -sS --ebpf -T 5 --rate 25000 -Pn host1 host2
$ nmap   -p 1-65535 -sS -n -Pn -T4 --max-rate 25000 host1 host2
$ naabu  -p 1-65535 -rate 25000 -host host1,host2 -silent

--rate est le débit total, tous hôtes et tous ports confondus (global, pas par hôte). Pour N sondes à R paquets/s, un scan ne peut pas finir en moins de N/R secondes : ici 131 070 sondes à 25 000/s, soit un plancher d'environ 5,2 s, cohérent avec les temps ci-dessus.

Chiffres d'un labo Docker (interface veth) sur un portable, reproductibles avec la commande ci-dessus. À débit plafonné, tcpcat et nmap sont au coude-à-coude ; l'intérêt de tcpcat est surtout l'étape explain. Une mesure sur vraie carte réseau reste à faire. Méthode complète : benchmarks/README.md.

5. Référence en ligne de commande

§5
Exemple de sortie de tcpcat : scan SYN avec détection d'OS, 3 ports ouverts sur 1 000 tcpcat — zsh $ sudo tcpcat -sS -O -p 1-1000 192.0.2.10 [*] Running Host Discovery... ├─ [UP] 192.0.2.10 [*] Ports loaded: 1000 port(s) targeted ──────────────────────────────────────────────────────────────────────────────── [i] 192.0.2.10 ─ OS: Linux 3.x-6.x (90% confidence) [+] 192.0.2.10:22 ─ OPEN (time=0.41ms | reason=SYN-ACK Received) [+] 192.0.2.10:80 ─ OPEN (time=0.38ms | reason=SYN-ACK Received) [+] 192.0.2.10:443 ─ OPEN (time=0.44ms | reason=SYN-ACK Received) [~] 192.0.2.10 ─ Not shown: 997 closed port(s) ──────────────────────────────────────────────────────────────────────────────── [✓] Scan completed in 2.103s. Found 3 open port(s) across 1 active target(s). $ ▌
Sortie abrégée (bannière omise). Adresse de documentation RFC 5737, durées indicatives.
Cartographie rapide (1000 ports les plus fréquents)
sudo tcpcat -sS --top-ports 1000 --rate 100000 target.lan
Mode eBPF sur un sous-réseau complet
sudo tcpcat --ebpf -p 1-1000 10.0.0.0/16 -w 128 -T 5
Versions applicatives, rapport JSON et SARIF
sudo tcpcat -sV -p 22,80,443,3306 192.168.0.0/24 -j report.json --sarif report.sarif

À garder en tête : eBPF/AF_XDP exige Linux et une carte réseau compatible. Une bannière ou une version détectée n'est pas une preuve de vulnérabilité. Le chemin réseau et les privilèges locaux peuvent modifier les résultats.

6. Considérations de sécurité

§6
Usage
autorisé
uniquement

tcpcat est réservé aux audits de sécurité défensifs, aux tests d'intrusion autorisés et à l'administration d'infrastructures sous mandat explicite. Scanner un réseau sans autorisation est passible de sanctions pénales (art. 323-1 et suivants du Code pénal, CFAA, RGPD). Les auteurs déclinent toute responsabilité en cas d'utilisation illicite.

Le projet est non commercial et ne fournit aucun service de scan hébergé. Voir la notice complète. Références : Nmap, RFC 793, eBPF, AF_XDP.

Annexe A. Folklore réseau (informatif, non normatif)

Ann. A

« Il n'y a pas d'endroit comme le 127.0.0.1. » (dans la tradition des RFC humoristiques, cf. RFC 2549, RFC 1149).

RFC 793, le barman TCP

Un paquet entre dans un bar : SYN, « Je voudrais une bière. » SYN-ACK, « Tu veux une bière ? » ACK, « Oui, une bière. » Le barman répond RST (port 80 fermé).

Extension du modèle OSI

Layer 8 : erreur critique identifiée entre la chaise et le clavier. « Avez-vous essayé d'éteindre et de rallumer la table de routage BGP ? »

ICMP Echo Request

64 bytes, icmp_seq=1, ttl=116 : la seule relation stable et sans engagement de ta vie.

RFC 768, le datagramme

« Je te raconterais bien une blague sur l'UDP, mais je ne suis pas sûr que tu la reçoives. »