⚠️ Disclaimer

La méthode que je présente ici correspond à ma propre démarche d’apprentissage. Elle peut contenir des approximations ou des erreurs, car j’apprends et je progresse chaque jour un peu plus. Ne prenez donc pas ce que je fais comme une référence absolue, mais plutôt comme un retour d’expérience personnel.

Wazuh SIEM, suite : MITRE ATT&CK, détection de vulnérabilités et blocage automatique via pfSense

Le premier article sur ce projet s’arrêtait sur un SIEM fonctionnel : des décodeurs custom pour pfSense et Synology, 18 règles qui matchaient, un dashboard qui commençait à avoir l’air d’un vrai dashboard. Mais le tout restait passif. Une IP essaie de bruteforcer le NAS cent fois, Wazuh lève une alerte level 12, et… rien ne se passe. L’alerte est là, elle attend qu’un humain la regarde.

Ce post couvre ce qui s’est passé ensuite : le mapping MITRE ATT&CK sur les règles custom, la détection de vulnérabilités (qui ne fonctionnait pas pour des raisons pas évidentes), et l’Active Response, c’est-à-dire le blocage automatique d’IPs hostiles directement sur pfSense.

Les configs sont sur GitHub.

Vue d'ensemble : MITRE ATT&CK, Vulnerability Detection et Active Response sur pfSense

Vue d’ensemble du projet : mapping MITRE ATT&CK, fix Vulnerability Detection, et chaîne Active Response complète vers pfSense.


Cartographier les règles sur MITRE ATT&CK

MITRE ATT&CK est une base de données publique de tactiques et techniques d’attaque. Chaque technique a un identifiant : T1110 pour le Brute Force, T1046 pour le Network Service Scanning, et ainsi de suite. Mapper ses règles SIEM sur ce référentiel leur donne un contexte dans un langage commun à toute la communauté cyber.

Concrètement dans Wazuh, ça se passe dans local_rules.xml avec des balises <mitre> :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<rule id="100023" level="10">
  <if_matched_sid>100022</if_matched_sid>
  <same_source_ip />
  <timeframe>120</timeframe>
  <frequency>5</frequency>
  <description>pfSense Suricata brute force SSH détecté (5+ échecs en 2 min)</description>
  <mitre>
    <id>T1110.001</id>
  </mitre>
  <group>authentication_failures,brute_force</group>
</rule>

La plupart des attributions sont assez directes. T1110 (Brute Force) pour les tentatives d’authentification ratées sur le NAS, T1087 (Account Discovery) pour les tentatives sur des comptes différents, T1071 (Application Layer Protocol) pour les alertes Suricata sur des communications C2.

Quelques-unes sont moins évidentes. La règle 100032, qui détecte un conflit de bail DHCP (deux machines déclarant la même IP), est tagguée T1557 (Adversary-in-the-Middle). Ça peut sembler exagéré pour un conflit DHCP banal. Mais un conflit DHCP délibéré est précisément une des techniques utilisées pour intercepter le trafic réseau : une machine malveillante répond avant le serveur DHCP légitime et distribue une configuration qu’elle contrôle. L’exercice de mapping oblige à se poser ce genre de question, ce qui n’est pas inutile.

Résultat : 12 règles sur 19 tagguées, 5 tactiques couvertes (Initial Access, Credential Access, Discovery, Lateral Movement, Command and Control). Les 7 sans tag sont les règles parents et les règles de base qui n’ont pas de sémantique d’attaque propre.

Mapping MITRE ATT&CK dans le dashboard Wazuh

Les règles custom avec leurs tags MITRE ATT&CK dans le dashboard Wazuh.


Vulnerability Detection : le problème avec deux pipelines

Wazuh embarque un module de détection de vulnérabilités. Il analyse les packages installés sur les agents et les compare à des bases CVE. Sur le papier, on active l’agent sur une machine, on attend quelques minutes, les vulnérabilités apparaissent dans le dashboard.

En pratique, l’onglet “Vulnerabilities” restait vide sur ma VM Wazuh, alors que l’agent Proxmox était bien déclaré et remontait des données. J’ai cherché dans les mauvais endroits pendant un moment.

Le point clé que j’avais mal compris : Wazuh utilise deux pipelines de données distincts vers OpenSearch.

  • Filebeat gère les alertes temps réel vers l’index wazuh-alerts-*. C’est ce qui peuple le dashboard principal.
  • indexer-connector est un module séparé qui alimente des index d’état, dont wazuh-states-vulnerabilities. C’est lui qui peuple l’onglet Vulnerabilities.

Ces deux pipelines ont chacun leur configuration et leur authentification indépendantes. Le fait que Filebeat fonctionne ne dit rien sur l’état d’indexer-connector.

Premier problème : dans ossec.conf, le bloc <indexer> contenait https://0.0.0.0:9200 comme adresse. Quand on configure un serveur pour écouter sur 0.0.0.0, ça veut dire “toutes les interfaces”. Mais quand un client se connecte à 0.0.0.0, le comportement dépend de l’OS et de la résolution. OpenSearch écoutait bien sur 192.168.30.100:9200, le module essayait de s’y connecter via 0.0.0.0, et ça échouait silencieusement. Pas d’erreur explicite dans les logs, juste… rien.

Second problème : indexer-connector nécessite que les credentials OpenSearch soient stockés dans le wazuh-keystore. Ce keystore est indépendant de celui de Filebeat. Je n’y avais jamais touché.

1
2
3
/var/ossec/bin/wazuh-keystore -f indexer -k username -v admin
# le mot de passe via pipe pour ne pas qu'il apparaisse dans history
echo "monmotdepasse" | /var/ossec/bin/wazuh-keystore -f indexer -k password

Après correction de l’IP et injection des credentials : 14 index wazuh-states-* peuplés, dont wazuh-states-vulnerabilities avec 169 CVE détectées sur le noeud Proxmox. 17 Critical, 57 High, principalement libssl3 et openssl. Un apt upgrade plus tard, une partie était résolue.

Onglet Vulnerabilities dans Wazuh avec les CVE détectées

169 CVE détectées sur le nœud Proxmox une fois indexer-connector correctement configuré.


Active Response : pfSense CE sans sudo ni API

C’est la partie qui a pris le plus de temps, et celle que je trouve la plus intéressante.

L’idée de l’Active Response dans Wazuh est simple : quand une règle dépasse un certain level, au lieu de juste logguer l’alerte, le Manager exécute un script. Ce script peut bloquer une IP, couper une connexion, envoyer une notification. La brique SOAR de base, donc.

Pour bloquer une IP sur pfSense, le mécanisme le plus propre serait une API REST. pfSense propose ça… dans pfSense Plus, la version commerciale. Sur pfSense CE, pas d’API. La seule option est SSH + pfctl, l’outil de gestion du firewall packet filter FreeBSD.

Première contrainte : pfSense CE n’a ni sudo ni doas. La commande pfctl exige les droits root. On ne peut pas créer un utilisateur dédié et lui donner accès à pfctl proprement. Il faut se connecter directement en root.

Pour compenser l’accès root, la sécurité repose entièrement sur les options de la clé SSH dans /root/.ssh/authorized_keys :

command="/usr/local/bin/wazuh_pfctl.sh",from="192.168.30.100",restrict ssh-ed25519 AAAA...

Trois restrictions s’empilent : from= limite la connexion à l’IP du Manager Wazuh (aucune autre machine ne peut utiliser cette clé), command= remplace le shell par un unique wrapper script (pas de shell interactif possible), restrict bloque le port forwarding, le PTY, et tout le reste. Le wrapper filtre ensuite les commandes pfctl autorisées.

Est-ce que c’est parfait ? Je ne suis pas convaincu que ce soit la solution la plus élégante. Mais avec les contraintes de pfSense CE, c’est pragmatique et les trois couches de protection se complètent.

Deuxième contrainte, inattendue : le shell par défaut de pfSense/FreeBSD est tcsh. Et tcsh interprète le caractère ! même dans les quotes simples, y compris dans les arguments de commandes. Résultat : impossible d’utiliser echo, printf, ni vi pour créer le wrapper script sur pfSense. Tout ce qui contenait #!/bin/sh déclenchait une erreur “Event not found”.

La solution : ee, le “Easy Editor” de FreeBSD. Un éditeur de texte natif qui contourne complètement le problème puisqu’il n’utilise pas le shell pour saisir le texte.

Troisième contrainte : pfSense écoute le SSH sur un port non standard, pas le 22 par défaut. J’ai perdu du temps à déboguer des connexions qui échouaient avant de réaliser que mes commandes de test ne spécifiaient pas le bon port.

Quatrième chose à ne pas oublier : une règle firewall entre le VLAN LAB (où tourne Wazuh, 192.168.30.x) et le VLAN MGMT (où se trouve pfSense, 192.168.20.x). Par défaut, les VLANs sont isolés. Le Manager Wazuh ne pouvait pas atteindre pfSense en SSH avant que j’ajoute une règle Pass TCP spécifique dans pfSense pour cette source et cette destination.

Une fois tout ça en place, la validation est satisfaisante :

1
2
3
4
5
# Dans un terminal : surveiller le log Active Response
tail -f /var/ossec/logs/active-responses.log

# Dans un autre : déclencher une règle manuellement pour tester
# (les logs "BLOQUÉE" et "DÉBLOQUÉE" apparaissent en temps réel)

Puis vérifier côté pfSense que la table est bien peuplée :

1
2
3
ssh -i /var/ossec/active-response/bin/.ssh/wazuh_ar_key \
    -p <PORT_SSH> root@<IP_PFSENSE> \
    "pfctl -t wazuh_blocked -T show"

Le blocage s’applique immédiatement sur le WAN. La durée est fixée à 600 secondes dans ossec.conf, après quoi Wazuh relance le script avec l’action DELETE pour débloquer automatiquement.

Quatre règles déclenchent l’Active Response : le brute force Synology (100023), l’énumération d’utilisateurs Synology (100024), les alertes Suricata Priority 1 (100041), et les alertes “hôte compromis connu” Suricata (100043).


La suite

Ce qui reste sur ce projet (en fonction de si j’ai le temps) :

  • Décodeur nginx pfSense (accès WebUI admin) : contourner le conflit avec le décodeur built-in web-accesslog
  • Alerting actif (email ou Telegram) sur les règles level >= 10
  • Dashboard Grafana Wazuh avec les métriques qui comptent
  • FIM (File Integrity Monitoring) sur les répertoires sensibles des VMs LAB

Ce que j’en retiens

Le mapping MITRE m’a plus appris que je ne l’attendais. Ce n’est pas un exercice de case-à-cocher, c’est une façon de vérifier que tu comprends ce que ta règle détecte vraiment. La règle qui lève une alerte sur un conflit DHCP, je l’avais écrite pour avoir de la visibilité sur les conflits d’adresses dans mon lab. En cherchant la bonne technique MITRE, j’ai compris que ce comportement peut aussi être le signe d’une attaque active, ce qui change la façon dont on doit la traiter.

Pour Vulnerability Detection, la leçon c’est qu’il ne faut pas supposer qu’une installation fonctionne parce qu’une partie d’elle fonctionne. Deux pipelines indépendants avec deux keystores séparés, et le fait que l’un marche ne dit rien sur l’autre. C’est le genre de chose qui n’est pas évident à déduire de la documentation, mais qui devient réflexe après avoir passé du temps à déboguer.

L’Active Response est ce qui transforme le SIEM en quelque chose d’actif. Wazuh ne fait plus que regarder et logguer, il réagit. La chaîne complète, Manager Wazuh → SSH → pfSense → pfctl → règle WAN, a des points de fragilité que j’ai tous rencontrés un par un. tcsh et ses expansions surprenantes, les permissions SSH, les VLANs isolés, le port non standard. C’est exactement le genre de projet où la liste de prérequis dans la doc en ligne et la réalité de l’environnement divergent assez vite.

Dans un contexte SOC ou SOAR en entreprise, ce type d’automatisation de blocage est courant, mais mieux encadré : API disponible, séparation des droits plus fine, validation humaine avant blocage dans certains cas. Mon implémentation homelab en est une version simplifiée. Mais les mécanismes autour de la clé SSH restreinte sont proches de ce qu’on trouverait dans un vrai déploiement. Ce que je retiens surtout : comprendre pourquoi on empile trois couches de restriction sur une connexion root, pas juste copier la commande.


Les scripts et configs sont disponibles sur GitHub.