TP 2 — Alerting & Notification Discord
Auteur : Thibaut Fontaine Institution : Université de la Réunion Date : 2026
Table des matières
Section intitulée « Table des matières »- Objectif & contexte
- Prérequis
- Étape 1 — Webhook Discord + Contact Point Grafana
- Étape 2 — Compiler Sigma → Alerte Grafana
- Étape 3 — Provisionner l’alerte par YAML
- Étape 4 — Notification policies par sévérité
- Étape 5 — Déclencher l’attaque (R3)
- Étape 6 — Tuner les faux positifs (R4)
- Souveraineté — Discord en prod ?
- Livrables & validation
- Annexe enseignant — corrigé
1. Objectif & contexte
Section intitulée « 1. Objectif & contexte »📡 Poste SOC · La Réunion — Acte 2, Détecter : vous armez Alfred Faure (Crozet), ~2 900 km. Liaison SATCOM nominale.
Une détection ne sert à rien si personne ne la voit. On ferme la boucle : règle Sigma → requête LogQL → alerte Grafana → notification Discord — sur la même stack Grafana, sans composant externe.
🔴 Au fil de l’enquête
Section intitulée « 🔴 Au fil de l’enquête »La règle existe (TP 1) mais elle dort. Le jour J, aucune alerte n’a jamais tonné — personne n’avait fermé la boucle. Ici vous la fermez : vous déclenchez l’attaque depuis votre poste, et vous regardez la notification tomber dans Discord. C’est la seconde qui aurait réveillé l’astreinte ce jour-là.
Objectifs pédagogiques
Section intitulée « Objectifs pédagogiques »- Comprendre la chaîne règle ⇒ alert rule ⇒ notification policy ⇒ contact point.
- Provisionner une alerte par YAML (alerting-as-code) — pas par clics dans l’UI.
- Configurer des notification policies par sévérité (Discord channel routing).
- Déclencher l’attaque correspondante (Atomic Red Team / flightsim) et voir la notification tomber.
- Tuner les faux positifs avec du bruit de fond (GHOSTS).
Architecture du TP2
Section intitulée « Architecture du TP2 »graph LR subgraph "Détection-as-Code (TP1)" RULE[/règle.yml
Sigma/] end subgraph "Compilation" SIGMA[sigma convert
-f grafana_alerting] end subgraph "Grafana Alerting" PROV[/provisioning/
alert-rules.yml/] AR[Alert Rule] NP[Notification Policy
severity routing] CP[Contact Point
Discord webhook] end subgraph "Sortie" DISCORD([Discord
#soc-critical
#soc-info]) end RULE --> SIGMA SIGMA --> PROV PROV --> AR AR -->|severity label| NP NP --> CP CP --> DISCORD classDef code fill:#99ff99,stroke:#333,stroke-width:2px classDef alert fill:#ffccff,stroke:#333,stroke-width:2px classDef out fill:#ffcc99,stroke:#333,stroke-width:2px class RULE,SIGMA,PROV code class AR,NP,CP alert class DISCORD out
2. Prérequis
Section intitulée « 2. Prérequis »TP1 validé
Section intitulée « TP1 validé »Vous arrivez avec un repo taaf-detections contenant au moins :
rules/initial_access/vpn-auth-external-ip.ymlrules/collection/falco-classified-vault-read.ymlrules/exfiltration/postgres-genetic-samples-no-approval.ymlscripts/compile.sh
Discord — créer un webhook
Section intitulée « Discord — créer un webhook »- Discord → votre serveur de TP → Paramètres du salon → Intégrations → Créer un webhook.
- Nommez-le
TAAF-SOC-bot, choisissez le salon#soc-critical(créez-le si besoin). - Copiez l’URL du webhook :
https://discord.com/api/webhooks/<id>/<token>.
Vérifiez :
curl -X POST -H "Content-Type: application/json" \ -d '{"content":"Hello from TAAF SOC 👋"}' \ "$DISCORD_WEBHOOK_CRITICAL"→ Un message doit apparaître dans #soc-critical.
Créez aussi #soc-info (avec son propre webhook → $DISCORD_WEBHOOK_INFO).
Outils d’attaque
Section intitulée « Outils d’attaque »- Atomic Red Team (déjà installé en Acte 0, sinon voir annexe).
- flightsim : binaire Go pour générer du trafic suspect (DNS exfil, beaconing). Sur la VM
soc:
which flightsim || (curl -sL https://github.com/alphasoc/flightsim/releases/download/v2.4.0/flightsim_2.4.0_Linux_x86_64.tar.gz \ | tar xz -C /tmp && sudo mv /tmp/flightsim /usr/local/bin/)flightsim version # → 2.4.03. Étape 1 — Webhook Discord + Contact Point Grafana
Section intitulée « 3. Étape 1 — Webhook Discord + Contact Point Grafana »3.1 — Créer le contact point dans Grafana
Section intitulée « 3.1 — Créer le contact point dans Grafana »Ouvrez Grafana (http://IP_VM2:3000) → Alerting → Contact points → + Add contact point.
| Champ | Valeur |
|---|---|
| Name | discord-soc-critical |
| Integration | Discord |
| Webhook URL | <votre webhook critical> |
| Use Discord username | TAAF-SOC-bot |
| Avatar URL | (laissez vide ou mettez un logo TAAF) |
| Message Content | {{ template "discord.taaf.message" . }} |
| Use template for message | ✅ (on créera le template en 3.3) |
Test : bouton Test → vous devez voir un message arriver dans #soc-critical.
Recommencez pour discord-soc-info (webhook info).
3.2 — Provisionner par YAML (recommandé pour la traçabilité)
Section intitulée « 3.2 — Provisionner par YAML (recommandé pour la traçabilité) »Plutôt que de cliquer, déposez dans le repo monitoring le fichier grafana/provisioning/alerting/contact-points.yml :
apiVersion: 1contactPoints: - orgId: 1 name: discord-soc-critical receivers: - uid: discord_critical type: discord settings: url: ${DISCORD_WEBHOOK_CRITICAL} use_discord_username: true username: TAAF-SOC-bot message: | {{ template "discord.taaf.message" . }} - orgId: 1 name: discord-soc-info receivers: - uid: discord_info type: discord settings: url: ${DISCORD_WEBHOOK_INFO} username: TAAF-SOC-bot message: | {{ template "discord.taaf.message" . }}Et dans .env du compose Grafana :
DISCORD_WEBHOOK_CRITICAL=https://discord.com/api/webhooks/.../...DISCORD_WEBHOOK_INFO=https://discord.com/api/webhooks/.../...docker compose restart grafana3.3 — Template Discord lisible
Section intitulée « 3.3 — Template Discord lisible »Dans Grafana → Alerting → Contact points → Notification templates → + Add template :
{{ define "discord.taaf.message" }}🚨 **{{ .CommonLabels.alertname }}** — {{ .CommonLabels.severity | toUpper }}
{{ range .Alerts }}**État :** {{ .Status }}**Règle :** `{{ .Labels.rulename }}` (ATT&CK : {{ .Labels.attack_id }})**Source :** {{ .Labels.job }}**Détails :** {{ .Annotations.summary }}**Lien :** [Explore]({{ .GeneratorURL }}){{ end }}{{ end }}4. Étape 2 — Compiler Sigma → Alerte Grafana
Section intitulée « 4. Étape 2 — Compiler Sigma → Alerte Grafana »Reprenez le repo taaf-detections du TP1.
4.1 — Conversion avec le format grafana_alerting
Section intitulée « 4.1 — Conversion avec le format grafana_alerting »cd ~/taaf-detectionssource ~/sigma-venv/bin/activate
sigma convert \ -t loki \ -f grafana_alerting \ -p loki_grafana_logfmt \ rules/initial_access/vpn-auth-external-ip.yml \ > build/alerts/vpn-auth-external-ip.ymlSortie attendue (extrait) :
apiVersion: 1groups: - orgId: 1 name: taaf-detections folder: SOC-TAAF interval: 1m rules: - uid: vpn-auth-external-ip title: VPN Authentication from External IP (TAAF) condition: A data: - refId: A datasourceUid: loki model: expr: | {job="auth"} | logfmt | service=`vpn` | auth_status=`success` | src_ip!~`^(10\.42\.|192\.168\.).*` | src_ip!~`^(198\.51\.100\.10|198\.51\.100\.11)$` noDataState: OK execErrState: Error for: 1m labels: severity: high attack_id: T1078 attack_id_secondary: T1133 annotations: summary: "Auth VPN externe pour utilisateur TAAF" description: "Auth VPN réussie depuis IP non-interne, non-partenaire."4.2 — Étendre scripts/compile.sh
Section intitulée « 4.2 — Étendre scripts/compile.sh »#!/usr/bin/env bashset -euo pipefailOUT_LOGQL="build/logql"OUT_ALERTS="build/alerts"mkdir -p "$OUT_LOGQL" "$OUT_ALERTS"
find rules -name "*.yml" -type f | while read -r rule; do name=$(basename "$rule" .yml) echo "[+] $rule" sigma convert -t loki -p loki_grafana_logfmt "$rule" > "$OUT_LOGQL/$name.logql" sigma convert -t loki -f grafana_alerting -p loki_grafana_logfmt "$rule" > "$OUT_ALERTS/$name.yml"doneecho "[✓] $(ls "$OUT_LOGQL" | wc -l) LogQL · $(ls "$OUT_ALERTS" | wc -l) alertes Grafana"./scripts/compile.sh5. Étape 3 — Provisionner l’alerte par YAML
Section intitulée « 5. Étape 3 — Provisionner l’alerte par YAML »5.1 — Déposer les alertes dans le provisioning Grafana
Section intitulée « 5.1 — Déposer les alertes dans le provisioning Grafana »Copiez build/alerts/*.yml dans le volume monté par Grafana :
# Depuis le repo monitoringmkdir -p grafana/provisioning/alerting/rulescp ~/taaf-detections/build/alerts/*.yml grafana/provisioning/alerting/rules/
# Redémarrer Grafana pour chargerdocker compose restart grafanadocker compose logs -f grafana | grep -i alertVérification : Grafana → Alerting → Alert rules → dossier SOC-TAAF → 3 règles visibles.
5.2 — Tester sans attendre l’attaque
Section intitulée « 5.2 — Tester sans attendre l’attaque »Dans l’UI de l’alerte → Preview → exécute la query et montre les résultats actuels. Si vos logs du jour J sont peuplés, la query doit retourner ≥ 1 ligne → l’alerte passera en Pending puis Firing après for: 1m.
6. Étape 4 — Notification policies par sévérité
Section intitulée « 6. Étape 4 — Notification policies par sévérité »6.1 — Créer la policy
Section intitulée « 6.1 — Créer la policy »Grafana → Alerting → Notification policies → Default policy → + New nested policy :
| Champ | Valeur |
|---|---|
| Matching labels | severity = critical |
| Contact point | discord-soc-critical |
| Group by | alertname, attack_id |
| Group wait | 15s |
| Group interval | 1m |
| Repeat interval | 4h |
Recommencez :
| severity = high | discord-soc-critical |
| severity = medium | discord-soc-info |
| severity = low | discord-soc-info |
6.2 — Provisionner par YAML
Section intitulée « 6.2 — Provisionner par YAML »grafana/provisioning/alerting/notification-policies.yml :
apiVersion: 1policies: - orgId: 1 receiver: discord-soc-info # default group_by: [alertname] group_wait: 30s group_interval: 5m repeat_interval: 12h routes: - receiver: discord-soc-critical matchers: - severity =~ "critical|high" group_by: [alertname, attack_id] group_wait: 15s group_interval: 1m repeat_interval: 4h - receiver: discord-soc-info matchers: - severity =~ "medium|low"docker compose restart grafana6.3 — La leçon « fatigue d’alerte »
Section intitulée « 6.3 — La leçon « fatigue d’alerte » »| Mauvaise pratique | Symptôme |
|---|---|
Tout en severity: critical | L’astreinte se déconnecte au bout d’1 semaine |
Pas de group_by | 50 alertes pour 1 incident — analyste noyé |
repeat_interval: 1m | Spam Discord, on désactive le webhook |
| Pas de silences pour la maintenance | Faux positifs pendant les déploiements |
→ Documentez votre politique d’alerting dans le README.md du repo taaf-detections.
7. Étape 5 — Déclencher l’attaque (R3)
Section intitulée « 7. Étape 5 — Déclencher l’attaque (R3) »7.1 — Atomic Red Team — T1078 (Auth anormale)
Section intitulée « 7.1 — Atomic Red Team — T1078 (Auth anormale) »Depuis la VM monitoring (où l’AD/VPN est branché — ou simulez via injection de log) :
# Simuler une auth VPN depuis 203.0.113.42 (correspond à jour J 09:12Z)logger -t auth -p auth.info \ 'service=vpn user=e.bernard src_ip=203.0.113.42 auth_status=success ts='$(date -u +%FT%TZ)→ Le log part vers Alloy → Loki. Attendez ~ 1 min (intervalle d’évaluation de l’alerte).
→ La notification doit tomber dans #soc-critical.
📸 Capture #1 : Grafana → Alert state Firing.
📸 Capture #2 : message Discord avec template rendu.
7.2 — flightsim — Beaconing C2
Section intitulée « 7.2 — flightsim — Beaconing C2 »Simule un beaconing périodique vers une IP “C2” :
sudo flightsim run c2 --dry-run # voir ce qui va être générésudo flightsim run c2 --no-bypass --max=30→ Zeek capture le trafic, génère des conn.log → Loki.
→ Si vous avez écrit une règle Sigma T1071.001 - C2 beaconing, l’alerte tombe.
7.3 — Atomic Red Team — T1213 (Falco read sensitive)
Section intitulée « 7.3 — Atomic Red Team — T1213 (Falco read sensitive) »Sur la VM monitoring :
docker exec -u root nextcloud-alfred-faure \ cat /var/www/html/data/admin.cloud.af/files/CLASSIFIED-VAULT-X/KER-2024-002.fasta \ > /dev/null→ Falco déclenche Unauthorized Read of CLASSIFIED Vault (user root n’est pas dans vault_authorized_users mappé à NextCloud).
→ Alerte Grafana Firing → Discord #soc-critical.
📸 Capture #3 : message Discord avec severity=critical, mention du fichier.
8. Étape 6 — Tuner les faux positifs (R4)
Section intitulée « 8. Étape 6 — Tuner les faux positifs (R4) »8.1 — Générer du bruit de fond bénin (GHOSTS)
Section intitulée « 8.1 — Générer du bruit de fond bénin (GHOSTS) »GHOSTS simule de l’activité utilisateur “normale” (browsing, mails, ouverture de docs). Sur la VM monitoring :
docker run -d --name ghosts-noise \ --network monitoring_default \ -e GHOSTS_USERS=5 \ -e GHOSTS_RATE=normal \ ghcr.io/cmu-sei/ghosts:latestLaissez tourner 30 min.
8.2 — Observer les faux positifs
Section intitulée « 8.2 — Observer les faux positifs »Vérifiez #soc-info : combien d’alertes medium/low sont tombées sans qu’il y ait eu d’attaque ?
8.3 — Tuner
Section intitulée « 8.3 — Tuner »Ajoutez des filter_* dans les règles Sigma. Exemple — exclure les requêtes PostgreSQL d’adminer qui consultent légitimement genetic_samples pour les admins :
detection: selection: query|contains: 'genetic_samples' approved_by: null filter_admin_review: user: 'admin' application: 'adminer' condition: selection and not filter_admin_reviewRecompilez (./scripts/compile.sh), redéployez (docker compose restart grafana), relancez GHOSTS — comparez le nombre de FP avant/après.
8.4 — Tableau de tuning (livrable)
Section intitulée « 8.4 — Tableau de tuning (livrable) »| Règle | FP avant tuning | Cause FP | Filtre ajouté | FP après tuning |
|---|---|---|---|---|
postgres-genetic-samples-no-approval | 8 / heure | Adminer admin | filter_admin_review | 0 |
vpn-auth-external-ip | 0 | — | — | 0 |
falco-classified-vault-read | 2 / heure | Backup nightly | vault_authorized_users += backup-cron | 0 |
9. Souveraineté — Discord en prod ?
Section intitulée « 9. Souveraineté — Discord en prod ? »À mentionner dans votre rapport : un paragraphe de ~10 lignes sur le pourquoi du choix Discord (pédagogique) et le plan de migration vers un canal souverain pour la prod.
10. Livrables & validation
Section intitulée « 10. Livrables & validation »Livrables (à pousser sur GitLab)
Section intitulée « Livrables (à pousser sur GitLab) »- Repo
taaf-detectionsenrichi :build/alerts/*.yml(3 alertes Grafana compilées)scripts/compile.shétendu (LogQL + alerts)README.mdavec la politique d’alerting (sévérités, groupings, repeat intervals)
- Repo
monitoringenrichi :grafana/provisioning/alerting/contact-points.ymlgrafana/provisioning/alerting/notification-policies.ymlgrafana/provisioning/alerting/rules/*.yml(3 fichiers copiés du build)grafana/templates/discord.taaf.tmpl(notification template)
- 3 captures :
- Grafana Alert state
Firing - Message Discord
#soc-critical(template rendu) - Tableau Notification policies
- Grafana Alert state
- Tableau de tuning FP (markdown ou PDF) — § 8.4
Grille de validation
Section intitulée « Grille de validation »| Critère | Pondération |
|---|---|
| Webhook Discord configuré et testé (test message reçu) | 10 % |
| Contact points provisionnés par YAML (pas par clics) | 15 % |
| Compilation Sigma → grafana_alerting fonctionnelle (3 alertes) | 20 % |
| Notification policies par sévérité (≥ 2 routes) | 15 % |
| Au moins une attaque déclenchée fait tomber une notification Discord | 20 % |
| Tableau de tuning FP rempli avec valeurs réelles | 10 % |
| Paragraphe souveraineté + plan de migration | 10 % |
Format de rendu : PDF nom-prenom-promo.pdf sur Moodle.
11. Annexe enseignant — corrigé
Section intitulée « 11. Annexe enseignant — corrigé »Installation Atomic Red Team
Section intitulée « Installation Atomic Red Team »# Sur la VM monitoringsudo apt install -y powershellpwsh -c "Install-Module -Name invoke-atomicredteam -Force -Scope CurrentUser; \ Install-AtomicRedTeam -getAtomics -Force"Notification policy YAML — version corrigée
Section intitulée « Notification policy YAML — version corrigée »(Voir § 6.2 — vérifier qu’elle est strictement identique chez l’étudiant.)
Évaluation des erreurs courantes
Section intitulée « Évaluation des erreurs courantes »- Webhook hardcodé dans
contact-points.yml(sans${DISCORD_WEBHOOK_CRITICAL}) → push d’un secret sur Git. -15 %. - Pas de
group_by→ la promo a découvert l’effet en TP : 30+ notifications pour 1 attaque. -10 %. for:à 0s → l’alerte tombe puis se résout aussitôt (oscillation). Doit être ≥ 1 min. -5 %.- Sévérité non cohérente avec la règle Sigma (
level: criticaldans Sigma maisseverity: mediumdans le label Grafana) → -5 %.
Pont vers le TP3
Section intitulée « Pont vers le TP3 »À la fin du TP2, l’étudiant a 3 alertes provisionnées qui tombent quand l’attaque est rejouée. Au TP3, on va mesurer la couverture ATT&CK de ces règles (S2AN → Navigator) et construire une corrélation multi-étapes (phishing → puis → escalade → puis → exfil) pour reconstruire la kill chain, pas juste voir des alertes éparses.