Ingénierie de détection

Détection réseau avec Suricata et Snort

Comprendre les signatures IDS : en-tête, content, flow, sticky buffers et seuils, puis tester ses règles Suricata sur une capture pcap.

1 Introduction

Un système de détection d'intrusion réseau (NIDS) observe une copie du trafic qui traverse votre réseau et lève une alerte quand un paquet ou une conversation correspond à une signature. Placé en coupure, le même moteur devient un IPS capable de bloquer le trafic. Deux moteurs open source dominent le domaine et partagent un langage de règles quasi identique :

Snort

Créé par Martin Roesch en 1998, aujourd'hui maintenu par Cisco Talos. Sa syntaxe de règles est devenue le standard de fait. Snort 3, réécrit de zéro, est la version actuelle.

Suricata

Développé depuis 2009 par la fondation à but non lucratif OISF. Multithread, doté d'analyseurs de protocoles riches (HTTP, DNS, TLS, SMB…) et de journaux JSON détaillés (EVE). Il lit la plupart des règles Snort.

Les règles réseau complètent les autres couches de détection : YARA examine les fichiers, Sigma les logs, Suricata et Snort le trafic. C'est là que les IOC réseau (une adresse IP, un domaine, un motif d'URL, une empreinte TLS) deviennent des alertes, et ils voient toutes les machines du segment, y compris les imprimantes, caméras et serveurs sur lesquels on ne peut pas installer d'agent.

1 ligne

une règle tient sur une ligne : un en-tête et des options entre parenthèses

sid + rev

identifiant unique et révision de chaque règle, obligatoires

1 000 000+

la plage de SID réservée à vos règles locales

2 Anatomie d'une règle

Une règle comporte deux parties. L'en-tête indique quel trafic est concerné :

alert  http  $HOME_NET any  ->  $EXTERNAL_NET any  ( options... )
  |      |        |      |   |        |       |
action proto  src IP  src  dir    dst IP   dst port
                     port
Champ Valeurs
Actionalert (journaliser), pass (arrêter l'inspection) et, en mode IPS, drop (bloquer silencieusement) ou reject (bloquer et renvoyer un reset). Par défaut, pass l'emporte sur drop, qui l'emporte sur alert.
Protocoletcp, udp, icmp, ip, ou un protocole applicatif (http, dns, tls, smb, ssh…) reconnu quel que soit le port.
AdressesUne IP, un CIDR, une liste [10.0.0.0/8,!10.1.1.1], any, ou une variable comme $HOME_NET / $EXTERNAL_NET définie dans suricata.yaml.
PortsUn numéro, une plage 1024:, une liste [80,443], any ou une variable ($HTTP_PORTS).
Direction-> de la source vers la destination, <> dans les deux sens.

Les options, entre parenthèses, disent quoi chercher et comment le signaler. Chaque option se termine par un point-virgule. Voici une règle complète, puis le rôle de chaque mot-clé :

local.rules

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"MLAB DEMO Suspicious User-Agent mlab-demo-bot"; flow:established,to_server; http.user_agent; content:"mlab-demo-bot"; nocase; classtype:trojan-activity; sid:1000002; rev:1;)

Options de signalement

msg:"...";Le texte de l'alerte. Par convention, il commence par un préfixe de source (ET MALWARE, MLAB DEMO).
sid:1000002;Identifiant de signature, unique parmi toutes les règles chargées. Utilisez la plage 1 000 000 à 1 999 999 pour vos règles locales ; Emerging Threats utilise la plage des 2 000 000.
rev:1;Révision, à incrémenter à chaque modification de la règle.
classtype:trojan-activity;Catégorie issue de classification.config ; elle fixe la priorité par défaut (1 = la plus haute).
reference:url,...;Lien vers une documentation, une CVE (reference:cve,2021-44228;) ou un rapport.
metadata:clé valeur, ...;Paires clé/valeur libres recopiées dans les alertes EVE : date de création, technique ATT&CK, niveau de confiance…

Options de détection

flow:established,to_server;Uniquement les paquets d'une session TCP établie qui vont vers le serveur (ou to_client). Peu coûteux et indispensable : à mettre dans toute règle TCP.
content:"abc";Octets exacts à trouver. Les octets binaires s'écrivent entre barres verticales : content:"|4D 5A|";. Préfixé par !, il exige l'absence.
nocase; startswith; endswith;Modifient le content qui précède.
offset / depth / distance / withinContraintes de position : où commencer, jusqu'où chercher, et par rapport à la correspondance précédente.
http.uri; dns.query; tls.sni;…Sticky buffers : tous les content suivants s'appliquent à ce champ décodé (URI, user agent, nom DNS, nom de serveur TLS, fichier téléchargé…) plutôt qu'au paquet brut.
pcre:"/regex/flags";Expression régulière Perl, appliquée au buffer courant. Toujours accompagnée d'un content.
fast_pattern;Indique au moteur quel content utiliser pour son préfiltre rapide. Par défaut, il prend le plus long.
threshold:type limit, track by_src, count 1, seconds 3600;Limitation du nombre d'alertes : ici une au plus par source et par heure. Types : limit, threshold, both.
detection_filter:track by_src, count 20, seconds 60;N'alerte qu'une fois que la règle a correspondu plus de 20 fois en 60 secondes pour une même source (puis à chaque nouvelle correspondance).

3 Exemples guidés

Toutes les règles ci-dessous se chargent dans Suricata 8 et utilisent des indicateurs fictifs : evil-update.example utilise le TLD réservé .example, et 203.0.113.0/24 est une plage réservée à la documentation.

Étape 1 : l'en-tête et le strict minimum

Les requêtes ICMP echo (ping, itype:8) qui atteignent votre réseau. Inutile en production, idéal pour vérifier que votre installation produit bien des alertes.

alert icmp any any -> $HOME_NET any (msg:"MLAB DEMO ICMP echo request to home net"; itype:8; classtype:misc-activity; sid:1000001; rev:1;)

Étape 2 : transformer des IOC réseau en règles

Un rapport de menace cite un domaine de C2. On le détecte dans les requêtes DNS et dans le Server Name Indication TLS, qui reste visible même en HTTPS. endswith couvre aussi les sous-domaines comme cdn.evil-update.example.

alert dns $HOME_NET any -> any any (msg:"MLAB DEMO DNS query for evil-update.example"; dns.query; content:"evil-update.example"; nocase; endswith; classtype:bad-unknown; sid:1000003; rev:1;)

alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"MLAB DEMO TLS SNI for evil-update.example"; flow:established,to_server; tls.sni; content:"evil-update.example"; nocase; endswith; classtype:bad-unknown; sid:1000005; rev:1;)

Étape 3 : une règle réaliste de beacon C2

Notre malware fictif envoie POST /gate.php?id= suivi de 16 caractères hexadécimaux, sans en-tête Referer (un vrai navigateur en envoie presque toujours un quand on suit un lien). Une règle longue peut s'écrire sur plusieurs lignes grâce à un \ final :

alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"MLAB DEMO Beacon to fake gate.php panel"; \
    flow:established,to_server; \
    http.method; content:"POST"; \
    http.uri; content:"/gate.php?id="; startswith; fast_pattern; \
    pcre:"/^\/gate\.php\?id=[0-9a-f]{16}$/"; \
    http.header_names; content:!"Referer"; \
    threshold:type limit, track by_src, count 1, seconds 3600; \
    reference:url,academy.mlab.sh/page/suricata-snort-rules; \
    classtype:trojan-activity; \
    metadata:mitre_tactic_id TA0011, mitre_technique_id T1071.001, created_at 2026_10_21; \
    sid:1000004; rev:2;)

Les filtres peu coûteux d'abord

alert http et flow limitent la règle aux requêtes HTTP sortantes ; http.method la restreint aux POST.

Un fast pattern solide

/gate.php?id= en début d'URI est la chaîne la plus distinctive : c'est elle que le préfiltre doit chercher.

pcre pour la forme exacte

Ne s'exécute que si le content a correspondu, et vérifie que l'identifiant fait exactement 16 caractères hexadécimaux.

Test négatif et limitation

http.header_names; content:!"Referer"; exige l'absence de l'en-tête ; threshold évite 1 440 alertes par jour pour un beacon qui appelle chaque minute.

Étape 4 : compter les événements (force brute)

Un SYN isolé vers le port 22 est normal ; plus de 20 en une minute depuis la même source externe ressemble à une attaque par force brute SSH ou à un scan. flags:S,12; correspond à un SYN pur en ignorant les deux bits ECN.

alert tcp $EXTERNAL_NET any -> $HOME_NET 22 (msg:"MLAB DEMO Possible SSH brute force"; flow:to_server; flags:S,12; detection_filter:track by_src, count 20, seconds 60; classtype:attempted-admin; sid:1000007; rev:1;)

Étape 5 : inspecter les fichiers téléchargés

Le buffer file.data contient le corps des réponses HTTP (décompressé et réassemblé). Cette règle se déclenche quand un exécutable Windows (en-tête MZ) est téléchargé en HTTP clair, une alerte de politique classique. Suricata peut aussi extraire ces fichiers et calculer leurs hash, prêts à partir en sandbox.

alert http any any -> any any (msg:"MLAB DEMO file download with EXE magic"; flow:established,to_client; file.data; content:"MZ"; startswith; classtype:policy-violation; sid:1000008; rev:1;)

4 Tests et outils

Valider, puis rejouer une capture

Ne poussez jamais une règle sur une sonde sans ces deux étapes. La sortie ci-dessous est réelle : la capture de démonstration contient la requête de beacon de l'étape 3.

# 1. Vérification de syntaxe : charge la config + vos règles, puis quitte
$ suricata -T -c /etc/suricata/suricata.yaml -S local.rules -v
...
Info: detect: 1 rule files processed. 8 rules successfully loaded, 0 rules failed, 0 rules skipped
Notice: suricata: Configuration provided was successfully loaded. Exiting.

# 2. Rejouer une capture à travers les règles (-k none : ignorer les checksums faux des pcap)
$ suricata -r demo.pcap -S local.rules -k none -l ./out
$ cat ./out/fast.log
10/21/2026-12:02:41.519555  [**] [1:1000002:1] MLAB DEMO Suspicious User-Agent mlab-demo-bot [**] [Classification: A Network Trojan was detected] [Priority: 1] {TCP} 192.168.1.50:49152 -> 203.0.113.10:80
10/21/2026-12:02:41.519555  [**] [1:1000004:2] MLAB DEMO Beacon to fake gate.php panel [**] [Classification: A Network Trojan was detected] [Priority: 1] {TCP} 192.168.1.50:49152 -> 203.0.113.10:80
10/21/2026-12:02:41.519555  [**] [1:1000006:1] MLAB DEMO legacy-style URI match [**] [Classification: A Network Trojan was detected] [Priority: 1] {TCP} 192.168.1.50:49152 -> 203.0.113.10:80

# 3. Les mêmes alertes, avec tout leur contexte, en JSON
$ jq 'select(.event_type=="alert") | .alert.signature' ./out/eve.json

Une seule erreur de syntaxe (ici, un ; oublié après le content) et Suricata ignore la règle. En mode test, il indique précisément la ligne fautive :

E: detect: error parsing signature "alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"MLAB DEMO broken"; http.uri; content:"/gate.php" sid:1000010; rev:1;)" from file local.rules at line 1

Pour fabriquer des captures de test, enregistrez votre propre trafic avec tcpdump -w test.pcap en reproduisant le comportement dans un labo, ou utilisez des collections publiques comme malware-traffic-analysis.net (à manipuler comme du malware actif).

Gérer les jeux de règles avec suricata-update

# Télécharger et fusionner Emerging Threats Open (la source par défaut)
$ sudo suricata-update

# Lister et activer d'autres sources gratuites
$ sudo suricata-update list-sources
$ sudo suricata-update enable-source tgreen/hunting
$ sudo suricata-update

# Désactiver une règle bruyante par son SID : l'ajouter à /etc/suricata/disable.conf, puis relancer
$ echo "1000001" | sudo tee -a /etc/suricata/disable.conf

Snort et Suricata : même famille, dialectes différents

Dans la syntaxe classique héritée de Snort 2, le buffer est un modificateur écrit après le content. Suricata l'accepte toujours (cette règle se charge et s'est déclenchée dans le test ci-dessus) :

alert tcp $HOME_NET any -> $EXTERNAL_NET $HTTP_PORTS (msg:"MLAB DEMO legacy-style URI match"; flow:established,to_server; content:"/gate.php"; http_uri; classtype:trojan-activity; sid:1000006; rev:1;)

Suricata privilégie désormais les sticky buffers à point, placés avant le content (http.uri; content:"...";). Snort 3 utilise lui aussi des sticky buffers, mais avec ses propres noms (http_uri;) et des modificateurs de content séparés par des virgules dans la même option :

alert http $HOME_NET any -> $EXTERNAL_NET any ( msg:"MLAB DEMO gate.php request"; flow:established,to_server; http_uri; content:"/gate.php?id=",nocase; classtype:trojan-activity; sid:1000009; rev:1; )

Testez les règles Snort 3 avec snort -c /usr/local/etc/snort/snort.lua -R local.rules -T. Beaucoup de mots-clés applicatifs (dns.query, tls.sni, file.data avec un point) n'existent que dans Suricata : validez toujours une règle sur le moteur qui l'exécutera.

Vérifiez la destination d'une alerte sur mlab.sh

Une alerte sur une IP ou un domaine inconnu ? Extrayez les indicateurs de l'alerte et vérifiez leur réputation avant d'escalader.

Extraire des IOC sur mlab.sh

5 Pièges courants

Un $HOME_NET erroné

Si HOME_NET dans suricata.yaml ne correspond pas à vos vraies plages d'adresses, toutes les règles $HOME_NET -> $EXTERNAL_NET deviennent aveugles sans prévenir. C'est la première chose à vérifier sur une nouvelle sonde.

Espérer voir à l'intérieur du HTTPS

Sans interception TLS, les règles http.uri ne voient que le HTTP en clair. Pour le trafic chiffré, travaillez avec ce qui reste visible : SNI, certificats (masqués en TLS 1.3), empreintes JA3/JA4 si elles sont activées, requêtes DNS et rythme des connexions.

Un content bien trop courant

content:"GET"; ou content:"admin"; seuls correspondent à une grande partie du trafic. Une règle a besoin d'un contenu distinctif, ancré dans le bon buffer.

SID en double et rev oublié

Deux règles avec le même SID : l'une est rejetée au chargement. Une règle modifiée sans incrémenter rev : votre SIEM ne distingue plus les anciennes alertes des nouvelles.

Des règles sur IP issues de flux périmés

Les IP d'attaquants sont souvent des adresses d'hébergement mutualisé ou de cloud qui changent de mains en quelques jours (voir réputation IP). Donnez aux règles basées sur des IOC une date d'expiration dans metadata et révisez-les.

Tester un pcap sans -k none

Les captures prises sur la machine émettrice ont souvent des checksums invalides (calculés par la carte réseau). Suricata ignore alors les paquets et votre règle semble ne pas fonctionner.

Conseils de performance

Une sonde inspecte chaque paquet en temps réel : une règle lente, ce sont des paquets perdus, et des paquets perdus, ce sont des attaques manquées. Le moteur effectue d'abord une recherche multi-motifs rapide sur un seul content par règle, et n'évalue le reste de la règle que sur les candidats.

Toujours au moins un content distinctif

Une règle qui n'a qu'un pcre ou que des flags doit être évaluée sur chaque paquet de son protocole.

Choisissez le protocole et le buffer les plus étroits

alert http + http.uri inspecte quelques octets par requête ; alert tcp any any -> any any + un content brut inspecte tout.

Choisissez le fast pattern en connaissance de cause

Si le content le plus long est une chaîne banale (un préfixe de User-Agent, Mozilla/5.0), placez fast_pattern sur le plus rare.

Mesurez

Une version de Suricata compilée avec le profilage des règles écrit le coût de chacune (rule_perf.log) ; surveillez le compteur capture.kernel_drops dans stats.log.

6 Aller plus loin

Les règles de la communauté

Emerging Threats Open

Des dizaines de milliers de règles gratuites maintenues par Proofpoint et la communauté, pour Suricata et Snort. La source par défaut de suricata-update, et le meilleur endroit pour apprendre en lisant.

Règles Snort (Cisco Talos)

Règles Community (gratuites), Registered (compte gratuit, publiées avec un délai) et Subscriber (payantes, en temps réel).

abuse.ch

SSLBL et ThreatFox publient des jeux de règles Suricata générés à partir de leurs flux de threat intel (certificats malveillants, indicateurs de C2).

Règles de chasse

Des sources comme tgreen/hunting dans suricata-update sont volontairement larges : parfaites pour des tableaux de bord de threat hunting, trop bruyantes pour réveiller un analyste.

Relier les points

Les journaux EVE JSON de Suricata (les alertes, mais aussi chaque requête DNS, requête HTTP et négociation TLS) alimentent parfaitement un SIEM, où des règles Sigma peuvent les corréler avec les événements des postes. Étiquetez vos règles avec les techniques MITRE ATT&CK dans metadata, nourrissez-les avec les indicateurs reçus via STIX/TAXII, et servez-vous du trafic de C2 observé en sandbox pour écrire des règles comportementales qui survivront à n'importe quel domaine suspect.

Modules connexes