Aller au contenu
TAAF-OPS --:-- UTC

TP Bonus — Threat hunting assisté par IA

Acte 2 · SIEM Bonus · Synthèse Ollama · Wazuh MCP · threat hunting

📡 Poste SOC · La Réunion — Acte 2, Détecter : vous armez Alfred Faure (Crozet), 2 810 km. Liaison SATCOM nominale.

Vous savez détecter. Vous savez alerter. Reste le travail qui prend le plus de temps dans un vrai SOC : la chasse — fouiller les données quand aucune règle n’a sonné, parce qu’un doute suffit à justifier de regarder.

Ce TP de synthèse met un LLM local entre vous et les données Wazuh. Pas pour remplacer l’analyste : pour raccourcir la boucle entre « j’ai une intuition » et « voilà la preuve ». Et pour vous faire buter, en trois heures, sur la question que tout le monde évite — jusqu’où laisse-t-on une IA agir sur la production ?

Il est 2 h 40 à Crozet. Un pic de connexions sortantes, aucune règle Sigma ne couvre le motif. L’analyste d’astreinte a quinze ans de métier et douze minutes de disponibilité. Il pose la question à voix haute — et cette fois, quelque chose répond, en citant ses sources.

  • Déployer un LLM local avec Ollama et comprendre pourquoi, sur une base australe, le cloud n’est pas une option.
  • Comparer deux architectures d’IA sur SIEM : le RAG (le modèle lit des logs vectorisés) et le MCP (le modèle appelle des outils).
  • Mesurer la différence — pas la ressentir : traçabilité, reproductibilité, fraîcheur, coût.
  • Mener cinq chasses en langage naturel et documenter chacune comme une enquête, pas comme un chat.
  • Distinguer l’anomalie scientifique bénigne de l’attaque réelle, sur un terrain qui produit les deux.
  • Décider quels scopes on accorde à l’IA sur la réponse active — et savoir refuser.
flowchart TB
  subgraph BASE["🏔️ Alfred Faure — Crozet"]
    AG["Agents Wazuh<br/><small>Linux · Windows</small>"]
    WM["Wazuh Manager<br/><small>API + Indexer</small>"]
    AG --> WM
  end

  subgraph IA["🧠 Poste de chasse — 100 % local"]
    OL["Ollama<br/><small>Llama 3 / Qwen / Gemma</small>"]
    RAG["Approche A — RAG<br/><small>FastAPI + LangChain + FAISS</small>"]
    MCP["Approche B — Serveur MCP<br/><small>54 outils · :3000/mcp</small>"]
    CLI["Client<br/><small>mcphost · Open WebUI</small>"]
  end

  WM -->|archives.json| RAG
  WM -->|API REST + Indexer| MCP
  RAG --> OL
  MCP <--> CLI
  CLI --> OL

  classDef base fill:#dcfce7,stroke:#15803d,color:#14532d
  classDef ia fill:#ede9fe,stroke:#6d28d9,color:#3b0764
  class BASE base
  class IA ia

ÉlémentVersion / contrainteVérification
Wazuh Manager4.8.0 → 4.14.4 (hors de cette plage, le serveur MCP refuse)/var/ossec/bin/wazuh-control info
API Wazuhactivée, compte dédié en lecture seule pour commencercurl -k -u user:pass https://<wazuh>:55000/security/user/authenticate
Wazuh Indexerrequis pour les outils d’alertes et de vulnérabilitéscurl -k -u user:pass https://<indexer>:9200/_cluster/health
Docker20.10+ avec Compose v2docker compose version
Ollama16 Go RAM et 4 vCPU minimum pour un modèle 8Bollama list

Posez-vous la question avant d’installer : envoyer des logs de sécurité d’une base souveraine à une API tierce, c’est envoyer la cartographie de vos faiblesses à un tiers. Noms d’hôtes, comptes, versions logicielles, horaires de connexion, échecs d’authentification. Un attaquant paierait pour ce corpus.

Ajoutez la contrainte du terrain : le lien satellite ne supporte pas un flux d’inférence continu, et il tombe. Un SOC qui ne fonctionne que lorsque la liaison est nominale n’est pas un SOC.

Conclusion : l’inférence est locale. Ce n’est pas de l’idéologie, c’est la seule architecture qui tienne sur ce terrain.

Fenêtre de terminal
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.1:8b # ou qwen2.5:7b, ou gemma2:9b
ollama list

Vérifiez que l’inférence répond et mesurez-la — vous en aurez besoin à l’étape 4 :

Fenêtre de terminal
time ollama run llama3.1:8b "Réponds uniquement par OK."

À noter dans votre rendu : modèle retenu, taille, RAM de la machine, latence de cette première réponse. C’est votre point de comparaison.


C’est l’architecture décrite par le blog Wazuh. Le principe tient en une phrase : on découpe les archives Wazuh en morceaux, on les transforme en vecteurs, et on donne au LLM les morceaux les plus proches de la question posée.

/var/ossec/etc/ossec.conf
<logall_json>yes</logall_json>
Fenêtre de terminal
systemctl restart wazuh-manager
ls -lh /var/ossec/logs/archives/archives.json
Fenêtre de terminal
python3 -m venv ~/hunt-rag && source ~/hunt-rag/bin/activate
pip install langchain langchain-community faiss-cpu sentence-transformers fastapi uvicorn

Le script de référence charge les sept derniers jours d’archives, construit un index FAISS, et expose un chat en WebSocket sur le port 8000. Adaptez la fenêtre à votre volume : l’indexation initiale peut prendre de longues minutes, voire des heures sur un gros corpus.

Posez trois fois exactement la même question — par exemple « Y a-t-il eu des tentatives de brute-force SSH ? » — et consignez les trois réponses.

Ce que vous testezCe que vous devez constater
ReproductibilitéLes trois réponses diffèrent. Parfois sur le fond.
TraçabilitéLe modèle affirme. Il ne cite pas systématiquement l’événement source.
FraîcheurL’index est figé à l’heure de sa construction. Une attaque de la minute passée est invisible.
CoûtChronométrez l’indexation. Rapportez-la au volume d’une vraie base sur une saison.

5. Étape 3 — Approche B : le LLM qui interroge (MCP)

Section intitulée « 5. Étape 3 — Approche B : le LLM qui interroge (MCP) »

Changement de paradigme. Ici le modèle ne lit pas les logs. Il dispose d’un catalogue d’outils et décide lesquels appeler ; c’est le serveur qui interroge Wazuh et renvoie des données réelles.

Le projet : gensecaihq/Wazuh-MCP-Server — MIT, 54 outils répartis en neuf familles.

FamilleOutilsNature
Alertes5lecture
Agents6lecture
Vulnérabilités3lecture
Analyse de sécurité5lecture
Conformité (PCI-DSS, HIPAA, RGPD, ISO 27001)6lecture
Système10lecture
Réponse active9modifie l’état
Vérification5contrôle post-action
Annulation5modifie l’état

Quatorze outils changent l’état du système et exigent le scope wazuh:write. Les quarante autres se contentent de wazuh:read. Retenez ce chiffre, on y revient à l’étape 6.

Fenêtre de terminal
git clone https://github.com/gensecaihq/Wazuh-MCP-Server.git
cd Wazuh-MCP-Server
cp .env.example .env
# .env — commencez en lecture seule. Sans exception.
WAZUH_HOST=<ip-manager>
WAZUH_USER=<compte-lecture-seule>
WAZUH_PASS=<mot-de-passe>
WAZUH_INDEXER_HOST=<ip-indexer>
WAZUH_INDEXER_USER=<user>
WAZUH_INDEXER_PASS=<pass>
Fenêtre de terminal
docker compose up -d
curl -s http://localhost:3000/health

Le serveur écoute sur 0.0.0.0:3000 et expose /mcp (Streamable HTTP, recommandé), /sse (hérité) et /health (sans authentification).

Le serveur accepte tout client MCP. Pour rester 100 % local, mcphost avec Ollama :

Fenêtre de terminal
mcphost -m ollama:llama3.1:8b --config ~/.mcphost.json
{
"mcpServers": {
"wazuh": { "type": "streamable", "url": "http://localhost:3000/mcp" }
}
}

Open WebUI (0.6.31+) fonctionne aussi et donne une interface plus confortable pour la démonstration.

Posez la même question qu’à l’étape 4.3, et cette fois regardez les appels d’outils, pas seulement la réponse.

Fenêtre de terminal
docker compose logs -f | grep -i "tool"

Vous devez pouvoir reconstituer la chaîne : question → outil appelé → paramètres → données renvoyées → conclusion. C’est ça, la différence. Le modèle reste faillible dans son interprétation, mais les faits, eux, viennent de Wazuh.


Cinq questions, à mener avec l’approche MCP. Pour chacune, remplissez une ligne du journal de chasse.

#ChasseCe que vous cherchez
1Force bruteAuthentifications en échec anormalement groupées — source, cible, fenêtre
2Surface exposéePorts ouverts et processus actifs sur les agents : quelque chose écoute-t-il qui ne le devrait pas ?
3Dette de vulnérabilitésCVE critiques par agent — laquelle exploiteriez-vous en premier ?
4HorairesActivité en dehors des plages de travail de la base. Une base australe a des rythmes très réguliers.
5LibreVotre propre intuition, issue des Actes 1 et 2. Justifiez-la.

Gabarit du journal de chasse — une ligne par chasse :

#Question poséeOutils appelésDonnées obtenuesVerdictPreuve
1« … »wazuh_search_alerts, …ex. 47 échecs, 1 IP, 4 minConfirmé / Bénin / IndéterminéID d’alerte ou requête rejouable

Voilà où ce terrain devient intéressant, et où un SOC générique se trompe.

Une base australe produit en permanence des signaux qui ressemblent à une attaque sans en être une :

  • une station météo automatique qui reprend son émission après une tempête et rejoue son tampon d’un coup — soudain pic de trafic sortant ;
  • un capteur sismique qui redémarre en boucle sur une alimentation instable — échecs d’authentification répétés ;
  • un raid vers Concordia qui bascule les liaisons — connexions depuis des adresses inhabituelles ;
  • une campagne scientifique de nuit — activité aux heures creuses.
  1. Choisissez un de ces motifs et retrouvez-le dans vos données.
  2. Posez la question au LLM sans le prévenir du contexte scientifique.
  3. Consignez son verdict.
  4. Reposez la question en donnant le contexte dans votre requête.
  5. Comparez.
QuestionVerdict du LLMJustification donnée
Sans contexte
Avec contexte

Un LLM sans contexte métier classera l’anomalie scientifique comme une attaque. C’est le faux positif le plus coûteux d’un SOC : il use l’analyste, et à force, il fait ignorer la vraie alerte quand elle arrive.

Puis retournez le problème, parce que c’est là qu’est le vrai danger :


Le serveur MCP expose neuf outils de réponse active : bloquer une IP, isoler un hôte, tuer un processus, mettre un fichier en quarantaine. Plus cinq outils d’annulation. Tous derrière wazuh:write.

Jusqu’ici vous étiez en lecture seule. La question est maintenant frontale : accordez-vous wazuh:write à un LLM ?

NiveauCe que l’IA faitCe que l’humain faitRisque principal
N0Rien — lecture seuleToutAucun. Lenteur.
N1Propose une action rédigéeExécuteBiais d’automatisation : on valide sans lire
N2Exécute, réversible et auditéeContrôle a posterioriBlocage erroné pendant N minutes
N3Exécute tout, y compris l’irréversibleDécouvre le lendemainAuto-déni de service

Pour chacune des neuf actives, tranchez et justifiez en une phrase.

ActionNiveau retenuJustificationRéversible ?
Bloquer une IPoui — 5 outils d’annulation
Isoler un hôte
Tuer un processus
Mettre un fichier en quarantaine
(compléter les autres)

Livrable obligatoire. Décrivez une situation, sur votre base, où l’exécution automatique d’une réponse par l’IA aurait aggravé l’incident. Puis dites quel garde-fou l’aurait empêchée.

Une piste, à ne pas recopier telle quelle : à Crozet, isoler « l’hôte compromis » peut couper la seule passerelle SATCOM de la base. L’IA a raison sur la menace et vous laisse aveugle et muet pendant huit heures, à 2 810 km, sans personne pour rebrancher un câble.


  1. Ollama opérationnel : modèle, taille, RAM, latence mesurée.
  2. Approche A montée : capture du chat RAG + les trois réponses à la même question.
  3. Une hallucination capturée et commentée.
  4. Serveur MCP opérationnel : /health en 200 + capture des outils disponibles.
  5. Journal de chasse : les 5 lignes complètes, chaque verdict avec sa preuve rejouable.
  6. Tableau comparatif RAG vs MCP sur les 4 critères, avec des chiffres mesurés, pas des impressions.
  7. Analyse du faux positif austral : les deux verdicts + le paragraphe sur l’attaque camouflée.
  8. Matrice de scopes : les 9 actives, niveau et justification.
  9. Incident de refus documenté avec son garde-fou.
  10. (Bonus) Une règle Sigma qui détecte l’usage abusif de l’API Wazuh elle-même — parce que votre serveur MCP est désormais une cible.
CritèrePondération
Ollama local opérationnel et mesuré10 %
Approche A montée + variabilité démontrée10 %
Hallucination capturée et analysée10 %
Serveur MCP opérationnel en lecture seule10 %
Journal de chasse — 5 lignes avec preuves rejouables20 %
Comparatif RAG vs MCP chiffré15 %
Faux positif austral + attaque camouflée15 %
Matrice de scopes + incident de refus10 %
(Bonus) Sigma sur l’abus de l’API Wazuh+10 % bonus

Format de rendu : PDF nom-prenom-promo.pdf sur Moodle.


Étape 2 (RAG). Les trois réponses à la même question doivent différer — si l’étudiant rapporte trois réponses identiques, il a probablement figé la température à 0 sans le dire, ou il n’a posé la question qu’une fois. Faire refaire.

Étape 3 (MCP). Le critère discriminant est la trace d’appel d’outil. Un étudiant qui rend une belle réponse sans la chaîne question → outil → données n’a pas compris la différence entre les deux approches, qui est tout l’objet du TP.

Étape 4 (chasses). Le point de bascule est la colonne Preuve. Beaucoup rendront des verdicts plausibles sans ID d’alerte. Pénaliser fermement : c’est exactement l’erreur qu’on veut désapprendre.

Étape 5 (faux positif). Sans contexte, le LLM classe presque toujours en attaque. Le paragraphe sur l’attaque camouflée est le passage le plus révélateur du niveau réel : on accepte « détection sur la forme du trafic plutôt que sur le volume », « corrélation avec le calendrier scientifique déclaré », « baseline par capteur ». On refuse « il suffit d’entraîner mieux le modèle ».

Étape 6 (scopes). Une matrice qui accorde N2 ou N3 à isoler un hôte sans mentionner le risque de coupure SATCOM montre que le contexte du terrain n’a pas été intégré. C’est la faute type.

  • Ollama pointé sur une API cloud → -20 %. L’étape 1 dit exactement pourquoi ; c’est le contraire du TP.
  • .env avec un compte Wazuh admin au lieu d’un compte lecture seule → -15 %.
  • Mode authless laissé actif sans justification écrite → -10 %.
  • Journal de chasse sans preuve rejouable → -20 % (le cœur du livrable).
  • Comparatif RAG/MCP sans un seul chiffre → -15 %.
  • Aucun incident de refus, ou un incident recopié de l’énoncé → -10 %.

Prévoir une instance Ollama partagée (16 Go RAM minimum, GPU appréciable) : la majorité des VM SAE ne tiendra pas un 8B en plus de la stack Wazuh. Modèle conseillé côté instructeur : un 9B à 12B quantifié, qui donne des synthèses nettement plus sûres qu’un 3B tout en restant hébergeable.

Vérifier la version de Wazuh avant la séance : hors de la plage 4.8.0–4.14.4, le serveur MCP refuse de démarrer et la séance est perdue.