Détecter un scan et une reconnaissance réseau
11 min · Intermédiaire · 7 août 2026
Le même geste, vu des deux côtés
Le chapitre sur la reconnaissance active du cours sur la méthodologie de test d’intrusion décrivait un scan de ports du point de vue de l’attaquant : tester systématiquement une plage de ports pour voir lesquels répondent. Ce chapitre observe la même activité du point de vue du défenseur, dans les logs qu’elle laisse derrière elle.
Le pattern à rechercher
Un utilisateur normal sollicite un nombre restreint de ports connus, de façon répétée. Un scan, à l’inverse, touche de nombreux ports distincts, depuis une même source, en peu de temps.
from collections import defaultdict
evenements = [
{"t": 0, "source": "203.0.113.44", "port": 21},
{"t": 1, "source": "203.0.113.44", "port": 22},
{"t": 1, "source": "203.0.113.44", "port": 23},
{"t": 2, "source": "203.0.113.44", "port": 80},
{"t": 2, "source": "203.0.113.44", "port": 443},
{"t": 2, "source": "203.0.113.44", "port": 3306},
{"t": 2, "source": "203.0.113.44", "port": 3389},
{"t": 15, "source": "10.0.20.15", "port": 443},
{"t": 16, "source": "10.0.20.15", "port": 443},
]
ports_par_source = defaultdict(set)
for evenement in evenements:
ports_par_source[evenement["source"]].add(evenement["port"])
SEUIL_PORTS_DISTINCTS = 5
for source, ports in ports_par_source.items():
if len(ports) >= SEUIL_PORTS_DISTINCTS:
print(f"ALERTE scan de ports : {source} a touche {len(ports)} ports distincts : {sorted(ports)}")
else:
print(f"normal : {source} -> {len(ports)} port(s) distinct(s) : {sorted(ports)}") 10.0.20.15 sollicite plusieurs fois le même port (443), un usage web parfaitement ordinaire. 203.0.113.44 touche sept ports différents en trois secondes, un pattern qu’aucun usage normal ne produit.
Info
Cette logique reprend directement celle du cours sur la défense et la détection : compter des occurrences distinctes plutôt que des occurrences brutes, sur une fenêtre resserrée, révèle un pattern qu’une lecture ligne par ligne des logs manquerait facilement.