Skip to main content

Chapitre 4.2 - SIEM, SOAR & Ingénierie de Détection

Module 4 : Ingénierie Défensive & Durcissement Prérequis : Chapitre 4.1 (Architecture des Pare-feux, Segmentation & Zéro Confiance)


Table des Matières

  1. Architecture SIEM - Ingestion, Normalisation & Corrélation
  2. Sources de Journaux & Collecte - Quoi Ingérer et Pourquoi
  3. Ingénierie de Détection - Sigma, YARA & Développement de Règles
  4. Elastic Stack (ELK) - Déploiement, Pipelines & Détection
  5. Splunk - SPL, Recherches de Corrélation & Tableaux de Bord
  6. Analytique Comportementale - UBA, Baselines & Détection d'Anomalies
  7. SOAR - Automatisation des Playbooks & Orchestration
  8. Triage des Alertes & Gestion des Faux Positifs
  9. Cartographie de la Couverture de Détection - ATT&CK & Analyse des Lacunes
  10. Cartographie MITRE ATT&CK

1. Architecture SIEM - Ingestion, Normalisation & Corrélation

Un système de Gestion des Informations et des Événements de Sécurité (SIEM) centralise la collecte des journaux, normalise les formats de données disparates, stocke les événements pour la conservation et l'investigation, et exécute des règles de corrélation pour générer des alertes exploitables. La qualité d'un SIEM est entièrement déterminée par ce qui y entre et par la qualité de la logique de détection qui lui est appliquée - un SIEM mal configuré ou mal ajusté génère du bruit, pas du renseignement.

Pipeline SIEM Principal

Sources de Journaux → Collecte → Transport → Analyse/Normalisation → Indexation → Corrélation → Alertes

Chaque étape introduit des points de défaillance potentiels :

ÉtapeDéfaillance CouranteConséquence
CollecteAgent non installé, rotation des journaux avant la collecteTélémétrie manquante - angles morts
TransportPerte UDP Syslog, mauvaise configuration TLSPerte de données en volume ; texte en clair en transit
AnalyseMauvais analyseur pour le format de journal, changement de version du fournisseurChamps non extraits - les règles de corrélation échouent
NormalisationNoms de champs incohérents (src_ip vs sourceAddress)Corrélation multi-sources impossible
IndexationStockage plein, niveau chaud épuiséÉvénements récents non consultables
CorrélationRègles trop larges, aucune baselineFatigue des alertes ; vraies alertes noyées

Planification du Volume de Données

# Estimer le volume quotidien de journaux avant le déploiement
# Débits moyens de journaux par type de source :
# Journal d'Événements Windows : ~500-2000 événements/hôte/jour (endpoint)
# Active Directory DC : ~50 000-200 000 événements/jour
# Pare-feu : ~100 Mo-5 Go/jour selon le trafic
# Proxy web : ~1-10 Go/jour
# Serveur DNS : ~500 Mo-2 Go/jour
# EDR (CrowdStrike) : ~1-5 Go/hôte/jour (télémétrie brute)

# Calculer le total :
python3 << 'EOF'
sources = {
"Windows Endpoints (500 hosts)": 500 * 1500 * 200, # events * avg_bytes
"AD Domain Controllers (4)": 4 * 100000 * 200,
"Firewalls (3)": 3 * 1e9, # 1GB/day each
"Web Proxy": 5e9,
"DNS": 1e9,
}
total_bytes = sum(sources.values())
print(f"Estimated daily ingest: {total_bytes/1e9:.1f} GB/day")
print(f"Monthly storage (90-day retention): {total_bytes*90/1e12:.1f} TB")
EOF


2. Sources de Journaux & Collecte - Quoi Ingérer et Pourquoi

Sources de Journaux Critiques par Priorité

Niveau 1 - Indispensable (détection fortement dégradée sans ces sources) :

# Journal des Événements de Sécurité Windows - authentification, utilisation des privilèges, création de processus
# Identifiants d'événements clés :
# 4624 - Connexion réussie (type 3=réseau, type 10=interactif distant)
# 4625 - Connexion échouée (détection de force brute)
# 4648 - Connexion avec des identifiants explicites (indicateur de Pass-the-Hash)
# 4663 - Accès aux objets (accès fichier/registre)
# 4688 - Création de processus (nécessite une politique d'audit + journalisation de la ligne de commande)
# 4698 - Tâche planifiée créée (persistance)
# 4720 - Compte utilisateur créé
# 4728/4732 - Membre ajouté à un groupe de sécurité/local (escalade de privilèges)
# 4769 - Ticket de service Kerberos (Kerberoasting)
# 4771 - Pré-authentification Kerberos échouée (tentative AS-REP roasting)
# 7045 - Nouveau service installé (psexec / persistance de malware)

# Activer la journalisation détaillée de la création de processus (Windows)
# Via GPO : Config Ordinateur -> Politique d'Audit -> Auditer la Création de Processus -> Succès
# ET activer la ligne de commande dans les événements de création de processus :
reg add "HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\System\\\\Audit" \\
/v ProcessCreationIncludeCmdLine_Enabled /t REG_DWORD /d 1 /f

# Linux auditd - audit des appels système et des accès aux fichiers
apt install auditd
cat > /etc/audit/rules.d/hardening.rules << 'EOF'
# Journaliser tous les événements d'authentification
-w /var/log/auth.log -p wa -k auth_log
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes

# Journaliser l'escalade de privilèges
-a always,exit -F arch=b64 -S setuid -S setgid -k privilege_escalation
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands

# Journaliser les connexions réseau (sortantes - détecter les C2)
-a always,exit -F arch=b64 -S connect -k outbound_connections

# Journaliser les modifications cron (persistance)
-w /etc/cron.d -p wa -k cron_changes
-w /var/spool/cron -p wa -k cron_changes

# Journaliser les ajouts de clés SSH (persistance)
-w /root/.ssh -p wa -k ssh_keys
-w /home -p wa -k home_ssh_keys
EOF
auditctl -R /etc/audit/rules.d/hardening.rules
service auditd restart

Niveau 2 - Haute valeur :

# Journalisation des Blocs de Script PowerShell - voir exactement ce que PowerShell a exécuté
# Même les commandes obfusquées/encodées sont journalisées après décodage
# GPO : Config Ordinateur -> Modèles Administratifs -> PowerShell ->
# Activer la Journalisation des Blocs de Script PowerShell -> Activé

# ID d'événement 4104 = Bloc de script PowerShell exécuté
# ID d'événement 400/403 = Démarrage/arrêt du moteur PowerShell
# ID d'événement 4103 = Journalisation des modules

# Journalisation des requêtes DNS - inestimable pour la détection C2/DGA/exfiltration
# Serveur DNS Windows : activer la journalisation analytique/debug
dnscmd /config /logLevel 0x8100F331
dnscmd /config /logFilePath C:\\\\Windows\\\\System32\\\\dns\\\\dns.log

# Linux BIND :
# /etc/bind/named.conf.options :
# logging {
# channel query_log {
# file "/var/log/named/query.log" versions 3 size 20m;
# severity dynamic;
# print-time yes;
# };
# category queries { query_log; };
# };

# Journaux DHCP - correspondance IP vers MAC/nom d'hôte (essentiel pour l'attribution)
# Lorsque vous voyez une IP suspecte dans les journaux du pare-feu, le DHCP vous indique quel appareil c'était
# DHCP Windows : journaux dans C:\\\\Windows\\\\System32\\\\dhcp\\
# ISC DHCP : /var/log/dhcpd.log ou journalctl -u isc-dhcp-server

# Journaux d'accès au serveur web - toutes les requêtes HTTP avec codes de statut
# Apache/nginx : format de journal combiné au minimum
# Ajouter : journalisation X-Forwarded-For, temps de réponse, version TLS, chiffrement

Collecte de Journaux avec Elastic Agent

# Installer Elastic Agent (remplace Filebeat/Winlogbeat/Metricbeat)
# Télécharger depuis : https://www.elastic.co/downloads/elastic-agent

# Installation Linux
curl -L -O https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.x.x-linux-x86_64.tar.gz
tar xzf elastic-agent-8.x.x-linux-x86_64.tar.gz
cd elastic-agent-8.x.x-linux-x86_64

# Inscrire l'agent auprès du serveur Fleet
./elastic-agent install \\
--url=https://fleet.internal.corp:8220 \\
--enrollment-token=YOUR_ENROLLMENT_TOKEN \\
--insecure

# Windows : déployer via GPO ou SCCM
# Programme d'installation MSI : elastic-agent-8.x.x-windows-x86_64.msi
# Ligne de commande :
msiexec /i elastic-agent-8.x.x-windows-x86_64.msi \\
FLEET_URL="https://fleet.internal.corp:8220" \\
ENROLLMENT_TOKEN="YOUR_TOKEN" /qn

# Vérifier l'état de l'agent
elastic-agent status
elastic-agent inspect # Afficher la configuration actuelle

# Collecte Syslog (pour les équipements réseau qui ne peuvent pas exécuter d'agents)
# rsyslog sur le serveur collecteur :
cat > /etc/rsyslog.d/remote.conf << 'EOF'
# Recevoir syslog via TLS (port 6514)
module(load="imtls")
input(type="imtls"
port="6514"
tls.cacert="/etc/ssl/ca.crt"
tls.mycert="/etc/ssl/collector.crt"
tls.myprivkey="/etc/ssl/collector.key")

# Modèle : écrire dans des fichiers par hôte
$template RemoteLogs,"/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log"
*.* ?RemoteLogs
EOF
systemctl restart rsyslog

3. Ingénierie de Détection - Sigma, YARA & Développement de Règles

Principes de l'Ingénierie de Détection

L'ingénierie de détection est la discipline qui consiste à traduire le renseignement sur les menaces et le comportement des adversaires en logique de détection opérationnelle. Le résultat est une règle - une expression logique précise qui se déclenche lorsque des conditions spécifiques sont remplies dans la télémétrie.

Dimensions de qualité de la détection :

DimensionMauvaise RègleBonne Règle
SpécificitéSe déclenche sur toute exécution PowerShellSe déclenche sur les commandes encodées avec net.webclient
SensibilitéNe se déclenche que sur un hachage connuSe déclenche sur le motif de comportement (quel que soit le hachage)
RésilienceContournée en renommant cmd.exeBasée sur les appels système - résistante au renommage
MaintenabilitéListe d'IOC codée en durBasée sur la logique avec un flux externe de threat intel
CouvertureSource de journaux uniqueCorrélation multi-sources

Sigma - Règles de Détection Indépendantes du Fournisseur

Sigma est un format de signature générique pour la détection basée sur les journaux. Une seule règle Sigma se compile en Splunk SPL, Elastic KQL, Microsoft Sentinel KQL, QRadar AQL et autres - écrire une fois, déployer partout.

# sigma/rules/windows/process_creation/proc_creation_win_powershell_download_cradle.yml
title: PowerShell Download Cradle Execution
id: 3b6ab547-8ec2-4991-b9d2-2b06702a753e
status: stable
description: Detects PowerShell download cradle patterns used to download and execute payloads
references:
- https://attack.mitre.org/techniques/T1059/001/
author: Detection Engineering Team
date: 2024/01/15
tags:
- attack.execution
- attack.t1059.001
- attack.defense_evasion
- attack.t1027
logsource:
category: process_creation # Catégorie de la source de journaux
product: windows
detection:
selection_powershell:
Image|endswith:
- '\\\\powershell.exe'
- '\\\\pwsh.exe'
selection_download:
CommandLine|contains:
- 'DownloadString' # Download cradle classique
- 'DownloadFile'
- 'WebClient'
- 'IEX' # Invoke-Expression
- 'Invoke-Expression'
- 'IWR' # Invoke-WebRequest
- 'Invoke-WebRequest'
- 'Net.WebClient'
- 'curl ' # Espace après curl dans le contexte PowerShell
- 'wget '
selection_encoded:
CommandLine|contains:
- '-EncodedCommand'
- '-enc '
- '-ec '
condition: selection_powershell and (selection_download or selection_encoded)
falsepositives:
- Legitimate administrative scripts using WebClient
- Software update scripts
level: high
# Convertir une règle Sigma vers différents formats de requête SIEM
pip install sigma-cli
sigma plugin install elasticsearch # Installer le backend Elasticsearch
sigma plugin install splunk # Installer le backend Splunk
sigma plugin install sentinel # Installer le backend Microsoft Sentinel

# Convertir en Elastic KQL
sigma convert -t lucene \\
sigma/rules/windows/process_creation/proc_creation_win_powershell_download_cradle.yml

# Convertir en Splunk SPL
sigma convert -t splunk \\
sigma/rules/windows/process_creation/proc_creation_win_powershell_download_cradle.yml

# Convertir en Microsoft Sentinel KQL
sigma convert -t azuremonitor \\
-p sentinel \\
sigma/rules/windows/process_creation/proc_creation_win_powershell_download_cradle.yml

# Convertir en lot tout un répertoire de règles
sigma convert -t lucene \\
--pipeline ecs_windows \\\\ # Appliquer le mappage de champs ECS
sigma/rules/windows/ \\
-o /tmp/elastic_rules/

# Valider une règle Sigma
sigma check \\
sigma/rules/windows/process_creation/proc_creation_win_powershell_download_cradle.yml

Plus de Règles Sigma - Motifs d'Attaques Clés

# Détecter DCSync (T1003.006) - réplication d'annuaire par un non-DC
title: DCSync Attack - Non-DC Replication Request
id: 56e0d8b8-3886-44d4-b1b5-5ff4f3e9ac56
logsource:
product: windows
service: security
detection:
selection:
EventID: 4662
ObjectType|contains: 'domainDNS'
AccessMask|contains: '0x100' # DS-Replication-Get-Changes
filter_dc:
SubjectUserName|endswith: '$' # Les comptes machine (DC) se terminent par $
condition: selection and not filter_dc # Compte non-machine = suspect
level: critical
tags:
- attack.credential_access
- attack.t1003.006
# Détecter Pass-the-Hash (T1550.002) - type de connexion 3 sans Kerberos
title: Pass-the-Hash via NTLM Logon
id: a9e3c12d-8e4f-5c3b-9f2a-1d4e7b8c9a0f
logsource:
product: windows
service: security
detection:
selection:
EventID: 4624
LogonType: 3 # Connexion réseau
AuthenticationPackageName: NTLM # NTLM utilisé (pas Kerberos)
WorkstationName: '-' # Poste de travail vide = indicateur PtH
filter_legitimate:
SubjectUserName: 'ANONYMOUS LOGON' # Filtrer les anonymes
IpAddress: '127.0.0.1' # Filtrer le loopback
condition: selection and not filter_legitimate
level: medium
tags:
- attack.lateral_movement
- attack.t1550.002
# Détecter le Kerberoasting - requêtes TGS chiffrées RC4
title: Kerberoasting - RC4 Encrypted TGS Request
id: 32d1f04a-9f7b-4c2e-8d3f-5a1b9c7e2d4f
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
TicketEncryptionType: '0x17' # RC4-HMAC (faible, utilisé pour le craquage)
TicketOptions: '0x40810000' # Transférable, Renouvelable
filter_computers:
ServiceName|endswith: '$' # Comptes d'ordinateurs (normal)
filter_krbtgt:
ServiceName: 'krbtgt'
condition: selection and not filter_computers and not filter_krbtgt
level: high
tags:
- attack.credential_access
- attack.t1558.003

YARA - Détection Basée sur la Mémoire & les Fichiers

Les règles YARA correspondent à des motifs d'octets et des chaînes dans des fichiers, des processus ou la mémoire - utilisées par les plateformes EDR, AV et sandbox :

// yara/rules/cobalt_strike_beacon.yar
rule CobaltStrike_Beacon_Default_Config
{
meta:
description = "Detects Cobalt Strike Beacon with default configuration"
author = "Detection Team"
date = "2024-01-15"
mitre = "T1059.003, T1071.001"
reference = "https://www.cobaltstrike.com"

strings:
// Chaînes de métadonnées par défaut du beacon CS
$cs1 = "ReflectiveLoader" ascii wide
$cs2 = "%s as %s\\\\%s: %d" ascii
$cs3 = "Started service %s on %s" ascii

// Identifiant du masque de sommeil
$sleep = { 48 83 EC 28 B9 00 00 00 08 FF 15 }

// Caractéristiques d'en-tête PE courantes de Cobalt Strike
$mz = { 4D 5A } // En-tête MZ
$pe = { 50 45 00 00 } // En-tête PE

condition:
$mz at 0 and $pe and
2 of ($cs*) and $sleep
}

rule Mimikatz_Memory_Pattern
{
meta:
description = "Detects Mimikatz patterns in memory or files"
author = "Detection Team"
tags = "credential_access, T1003"

strings:
$m1 = "sekurlsa::logonpasswords" ascii wide nocase
$m2 = "lsadump::sam" ascii wide nocase
$m3 = "kerberos::golden" ascii wide nocase
$m4 = "privilege::debug" ascii wide nocase
$m5 = { 6D 69 6D 69 6B 61 74 7A } // "mimikatz" en hexadécimal
$m6 = "Benjamin DELPY" ascii wide // Chaîne de l'auteur dans le binaire

condition:
2 of them
}
# Exécuter YARA contre des fichiers
yara -r /path/to/rules/ /suspicious/file.exe # -r : récursif
yara cobalt_strike_beacon.yar /tmp/memory_dump.bin

# Scanner les processus en cours d'exécution
yara cobalt_strike_beacon.yar /proc/*/mem 2>/dev/null

# Scanner avec toutes les règles d'un répertoire de manière récursive
yara -r /etc/yara/rules/ /quarantine/ \\
--print-meta \\\\ # Afficher les métadonnées des règles
--print-strings \\\\ # Afficher les chaînes correspondantes
--fail-on-warnings # Mode strict

# Intégrer avec Zeek - scanner les transferts de fichiers en temps réel
# files.log de Zeek fournit les fichiers extraits -> transmettre au scanner YARA
zeek -r capture.pcap -C local
# Puis scanner les fichiers extraits :
find /tmp/zeek_extracted/ -type f -exec \\
yara -r /etc/yara/rules/ {} \\\\; | \\
grep -v "^$"

4. Elastic Stack (ELK) - Déploiement, Pipelines & Détection

Pipeline d'Ingestion - Extraction de Champs & Normalisation

# Créer un pipeline d'ingestion Elasticsearch pour les événements de sécurité Windows
curl -X PUT "https://elasticsearch:9200/_ingest/pipeline/windows-security" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"description": "Pipeline de traitement du Journal des Événements de Sécurité Windows",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"%{TIMESTAMP_ISO8601:timestamp} %{DATA:hostname} %{DATA:process}\\\\[%{NUMBER:pid}\\\\]: %{GREEDYDATA:log_message}"
],
"ignore_failure": true
}
},
{
"geoip": {
"field": "source.ip",
"target_field": "source.geo",
"ignore_missing": true
}
},
{
"set": {
"field": "event.dataset",
"value": "windows.security"
}
},
{
"script": {
"lang": "painless",
"source": """
// Normaliser le type de connexion en lisible par l'humain
Map logonTypes = [
"2": "Interactive",
"3": "Network",
"4": "Batch",
"5": "Service",
"7": "Unlock",
"8": "NetworkCleartext",
"9": "NewCredentials",
"10": "RemoteInteractive",
"11": "CachedInteractive"
];
if (ctx.winlog?.event_data?.LogonType != null) {
String lt = ctx.winlog.event_data.LogonType;
ctx.logon_type_name = logonTypes.getOrDefault(lt, "Unknown(" + lt + ")");
}
"""
}
}
]
}'

Règles de Détection Kibana - KQL & EQL

# KQL (Kibana Query Language) - correspondance champ:valeur
# Rechercher des indicateurs de Pass-the-Hash dans Kibana Discover :
winlog.event_id:4624 AND winlog.event_data.LogonType:3 AND winlog.event_data.AuthenticationPackageName:NTLM

# Rechercher la création de tâches planifiées
winlog.event_id:4698 AND NOT user.name:SYSTEM

# Détecter un grand nombre de connexions échouées depuis la même IP (force brute)
winlog.event_id:4625 | stats count by source.ip | where count > 20

# EQL (Event Query Language) - détection de séquences sur plusieurs événements
# Détecter : PowerShell générant cmd.exe générant net.exe (chaîne de mouvement latéral classique)
curl -X GET "https://elasticsearch:9200/logs-*/_eql/search" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"query": """
sequence with maxspan=2m
[process where process.name == "powershell.exe"]
[process where process.name == "cmd.exe" and
process.parent.name == "powershell.exe"]
[process where process.name in ("net.exe", "net1.exe") and
process.parent.name == "cmd.exe"]
""",
"size": 100
}'

# EQL : Détecter la persistance via une clé de registre run
curl -X GET "https://elasticsearch:9200/logs-*/_eql/search" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"query": """
sequence by host.name with maxspan=30s
[registry where registry.path like~ "*\\\\\\\\Run\\\\\\\\*" and
registry.data.strings like~ ("*.exe*", "*.dll*", "*.ps1*")]
[process where process.name in ("cmd.exe","powershell.exe","wscript.exe")]
""",
"size": 50
}'

Gestion du Cycle de Vie des Index Elasticsearch

# Configurer la politique ILM pour les niveaux de rétention des journaux
curl -X PUT "https://elasticsearch:9200/_ilm/policy/security-logs-policy" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_size": "50GB", // Faire pivoter l index après 50 Go
"max_age": "1d" // Ou après 1 jour
},
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 },
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "30d",
"actions": {
"freeze": {},
"set_priority": { "priority": 0 }
}
},
"delete": {
"min_age": "365d",
"actions": { "delete": {} } // Supprimer après 1 an
}
}
}
}'

5. Splunk - SPL, Recherches de Corrélation & Tableaux de Bord

Le Langage de Traitement de Recherche (SPL) de Splunk est un langage de requête basé sur des pipelines où chaque commande transforme l'ensemble de résultats de la commande précédente.

Motifs SPL Principaux pour la Détection de Sécurité

| Détecter les tentatives de connexion par force brute
index=windows EventCode=4625
| bucket _time span=5m
| stats count by _time, src_ip, user
| where count > 10
| sort - count
| table _time, src_ip, user, count

| Détecter le mouvement latéral - même compte se connectant à plusieurs hôtes rapidement
index=windows EventCode=4624 LogonType=3
| bucket _time span=10m
| stats dc(host) as unique_hosts, values(host) as hosts by _time, user
| where unique_hosts > 5
| sort - unique_hosts

| Détecter le download cradle PowerShell (création de processus avec ligne de commande)
index=windows EventCode=4688
| where like(CommandLine, "%DownloadString%")
OR like(CommandLine, "%IEX%")
OR like(CommandLine, "%-EncodedCommand%")
OR like(CommandLine, "%WebClient%")
| eval risk_score=case(
like(CommandLine, "%-EncodedCommand%"), 90,
like(CommandLine, "%IEX%") AND like(CommandLine, "%WebClient%"), 95,
true(), 70)
| table _time, host, user, CommandLine, risk_score
| sort - risk_score

| Détection du Kerberoasting - requêtes TGS RC4
index=windows EventCode=4769
| where TicketEncryptionType="0x17"
| bucket _time span=1m
| stats count, values(ServiceName) as services by _time, src_ip
| where count > 3
| eval alert="Possible Kerberoasting"
| table _time, src_ip, count, services, alert

| Détection du tunneling DNS - noms de domaine inhabituellement longs
index=dns
| eval qname_length=len(query)
| where qname_length > 52 | Les sous-domaines de plus de 52 caractères sont suspects
| stats count by src_ip, query
| where count > 20
| sort - count

| Détection du beaconing - connexions sortantes régulières
index=network dest_port=443
| bucket _time span=1h
| stats count by _time, src_ip, dest_ip
| stats stdev(count) as jitter, avg(count) as avg_conns by src_ip, dest_ip
| where avg_conns > 3 AND jitter < 1.5 | Faible variance = périodique = beacon
| sort - avg_conns

| Trouver les comptes avec plusieurs connexions échouées suivies d'un succès (force brute réussie)
index=windows (EventCode=4625 OR EventCode=4624)
| eval event_type=if(EventCode==4625, "failure", "success")
| sort _time
| streamstats count(eval(event_type="failure")) as fail_count
count(eval(event_type="success")) as success_count
by user
| where success_count=1 AND fail_count > 5
| dedup user
| table user, fail_count, _time, host, src_ip

Événements Notables Splunk & Alertes Basées sur le Risque

| Alertes Basées sur le Risque - agréger les scores de risque par entité
| tstats summariesonly=true sum(All_Risk.calculated_risk_score) as risk_score
count(All_Risk.calculated_risk_score) as risk_event_count
values(All_Risk.annotations.mitre_attack.mitre_technique_id) as mitre_techniques
from datamodel=Risk
by All_Risk.risk_object, All_Risk.risk_object_type, _time span=24h
| `drop_dm_object_name("All_Risk")`
| where risk_score > 200
| sort - risk_score
| table risk_object, risk_object_type, risk_score, risk_event_count, mitre_techniques

| Chasse aux menaces - trouver tous les processus générés par les applications Office
index=windows EventCode=4688
| where ParentProcessName IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe")
| where ProcessName NOT IN ("splwow64.exe","AcroRd32.exe","WINWORD.exe")
| stats count by host, ParentProcessName, ProcessName, CommandLine
| sort - count

| Rechercher les binaires de subsistance (LOLBins) utilisés pour le mouvement latéral
index=windows EventCode=4688
| where ProcessName IN (
"certutil.exe","mshta.exe","regsvr32.exe","rundll32.exe",
"wscript.exe","cscript.exe","msiexec.exe","installutil.exe",
"odbcconf.exe","regasm.exe","regsvcs.exe","cmstp.exe")
| stats count by host, ProcessName, CommandLine, user
| sort - count

6. Analytique Comportementale - UBA, Baselines & Détection d'Anomalies

Construction des Baselines Comportementales

#!/usr/bin/env python3
# baseline_logon_hours.py - construire une baseline des heures de connexion par utilisateur
# Entrée : journaux d'événements de sécurité Windows 4624 au format JSON

import json
import pandas as pd
import numpy as np
from collections import defaultdict
from datetime import datetime

# Charger les événements de connexion (exportés depuis le SIEM en JSON)
events = []
with open('/tmp/logon_events.json') as f:
for line in f:
events.append(json.loads(line))

df = pd.DataFrame(events)
df['timestamp'] = pd.to_datetime(df['timestamp'])
df['hour'] = df['timestamp'].dt.hour
df['dayofweek'] = df['timestamp'].dt.dayofweek # 0=Lundi, 6=Dimanche

# Construire la baseline : pour chaque utilisateur, à quelles heures se connecte-t-il normalement ?
baseline = df.groupby(['user', 'hour', 'dayofweek']).size().reset_index(name='count')

def is_anomalous_logon(user, hour, dayofweek, baseline_df, threshold=0):
"""Retourne True si cette heure de connexion n'a jamais été vue pour cet utilisateur"""
match = baseline_df[
(baseline_df['user'] == user) &
(baseline_df['hour'] == hour) &
(baseline_df['dayofweek'] == dayofweek)
]
return len(match) == 0 or match['count'].iloc[0] <= threshold

# Tester les événements récents contre la baseline
recent_events = df[df['timestamp'] > df['timestamp'].max() - pd.Timedelta('1d')]
for _, event in recent_events.iterrows():
if is_anomalous_logon(event['user'], event['hour'], event['dayofweek'], baseline):
print(f"[ANOMALIE] Utilisateur: {event['user']} | "
f"Heure: {event['hour']}:00 | "
f"Jour: {event['dayofweek']} | "
f"Source: {event.get('src_ip','inconnu')} | "
f"Hôte: {event.get('host','inconnu')}")

Elastic ML - Jobs de Détection d'Anomalies

# Créer un job ML pour une activité de processus inhabituelle
curl -X PUT "https://elasticsearch:9200/_ml/anomaly_detectors/unusual_process_activity" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"description": "Détecter des motifs d exécution de processus inhabituels par hôte",
"analysis_config": {
"bucket_span": "15m",
"detectors": [
{
"detector_description": "Processus rare par hôte",
"function": "rare",
"by_field_name": "process.name",
"partition_field_name": "host.name"
},
{
"detector_description": "Nombre de processus inhabituel",
"function": "high_count",
"by_field_name": "process.name",
"partition_field_name": "host.name"
}
],
"influencers": ["host.name", "user.name", "process.name"]
},
"data_description": {
"time_field": "@timestamp",
"time_format": "epoch_ms"
},
"analysis_limits": {
"model_memory_limit": "256mb"
}
}'

# Ouvrir et démarrer le job
curl -X POST "https://elasticsearch:9200/_ml/anomaly_detectors/unusual_process_activity/_open" \\
-u elastic:PASSWORD

# Créer un datafeed (connecte le job à l'index)
curl -X PUT "https://elasticsearch:9200/_ml/datafeeds/datafeed-unusual_process_activity" \\
-H "Content-Type: application/json" \\
-u elastic:PASSWORD \\
-d '{
"job_id": "unusual_process_activity",
"indices": ["logs-endpoint.events.process-*"],
"query": {
"bool": {
"filter": [{"range": {"@timestamp": {"gte": "now-24h"}}}]
}
}
}'

# Démarrer le datafeed
curl -X POST "https://elasticsearch:9200/_ml/datafeeds/datafeed-unusual_process_activity/_start" \\
-u elastic:PASSWORD

7. SOAR - Automatisation des Playbooks & Orchestration

Architecture SOAR

SOAR (Orchestration, Automatisation et Réponse de Sécurité) réduit le temps moyen de réponse (MTTR) en automatisant les étapes répétables d'investigation et de réponse. Un playbook bien conçu gère automatiquement les 80 premiers pourcents du triage des alertes, présentant aux analystes des cas enrichis et pré-investigués.

Principales plateformes SOAR : Splunk SOAR (anciennement Phantom), Palo Alto XSOAR, TheHive + Cortex, Shuffle (open-source).

Shuffle - Playbook SOAR Open-Source

# Workflow Shuffle (action Python) - réponse automatisée au phishing
# Déclenché lorsque : une alerte de sécurité email se déclenche pour un email suspect

import requests
import json

def analyze_phishing_email(email_data):
"""
Playbook automatisé de triage des emails de phishing
Étapes : extraire les IOC -> enrichir -> contenir -> notifier
"""
results = {}

# Étape 1 : Extraire les IOC de l'email
sender_ip = email_data.get('sender_ip')
sender_domain = email_data.get('sender_domain')
urls = email_data.get('urls', [])
attachments = email_data.get('attachments', [])

# Étape 2 : Enrichir la réputation IP (VirusTotal)
vt_headers = {"x-apikey": VT_API_KEY}
ip_report = requests.get(
f"https://www.virustotal.com/api/v3/ip_addresses/{sender_ip}",
headers=vt_headers).json()
malicious_votes = ip_report.get('data',{}).get('attributes',{}) \\
.get('last_analysis_stats',{}).get('malicious', 0)
results['ip_malicious_votes'] = malicious_votes

# Étape 3 : Vérifier les URLs contre le threat intel
for url in urls:
url_report = requests.post(
"https://www.virustotal.com/api/v3/urls",
headers=vt_headers,
data={"url": url}).json()
results.setdefault('url_results', []).append({
'url': url,
'analysis_id': url_report.get('data',{}).get('id')
})

# Étape 4 : Analyser les hachages des pièces jointes en sandbox
for attachment in attachments:
hash_report = requests.get(
f"https://www.virustotal.com/api/v3/files/{attachment['sha256']}",
headers=vt_headers).json()
malicious = hash_report.get('data',{}).get('attributes',{}) \\
.get('last_analysis_stats',{}).get('malicious', 0)
results.setdefault('attachment_results', []).append({
'filename': attachment['name'],
'sha256': attachment['sha256'],
'malicious_votes': malicious
})

# Étape 5 : Réponse automatisée basée sur le score de risque
risk_score = malicious_votes * 10 + len([u for u in results.get('url_results',[]) if u])

if risk_score > 50:
# Malveillant à haute confiance - confinement automatique
# Bloquer l'IP de l'expéditeur sur le pare-feu
block_ip_on_firewall(sender_ip)
# Supprimer l'email de toutes les boîtes aux lettres
delete_email_from_exchange(email_data['message_id'])
# Créer un ticket haute priorité
create_jira_ticket(severity="HIGH", data=results)
results['action'] = 'AUTO_CONTAINED'
elif risk_score > 20:
# Confiance moyenne - escalader vers l'analyste
create_jira_ticket(severity="MEDIUM", data=results)
results['action'] = 'ESCALATED_TO_ANALYST'
else:
# Faible risque - journaliser et fermer
results['action'] = 'CLOSED_LOW_RISK'

return results

TheHive + Cortex - Plateforme IR Open-Source

# API TheHive - créer un cas à partir d'une alerte SIEM
curl -X POST "https://thehive.internal.corp:9000/api/case" \\
-H "Authorization: Bearer YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"title": "Possible Kerberoasting - WORKSTATION-05",
"description": "Événement 4769 avec chiffrement RC4 détecté. Plusieurs comptes de service ciblés.",
"severity": 3,
"tags": ["kerberoasting", "credential_access", "T1558.003"],
"tasks": [
{"title": "Identifier le compte source", "status": "Waiting"},
{"title": "Vérifier si le hachage a été cracké", "status": "Waiting"},
{"title": "Examiner le mouvement latéral depuis l IP source", "status": "Waiting"},
{"title": "Réinitialiser les mots de passe des comptes de service ciblés", "status": "Waiting"}
]
}'

# Analyseur Cortex - enrichir automatiquement un observable (adresse IP)
curl -X POST "https://cortex.internal.corp:9001/api/analyzer/Shodan_Host_1/run" \\
-H "Authorization: Bearer CORTEX_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"data": "203.0.113.42",
"dataType": "ip",
"tlp": 2
}'

# Obtenir les résultats de l'analyseur
curl -X GET "https://cortex.internal.corp:9001/api/job/JOB_ID/report" \\
-H "Authorization: Bearer CORTEX_API_KEY"

8. Triage des Alertes & Gestion des Faux Positifs

Cadre de Qualité des Alertes

Chaque alerte doit être évaluée sur deux axes :

  • Taux de Vrais Positifs (TVP) : À quelle fréquence cette alerte se déclenche-t-elle sur de vraies attaques ?
  • Taux de Faux Positifs (TFP) : À quelle fréquence se déclenche-t-elle sur une activité bénigne ?

La fatigue des alertes - les analystes ignorant les alertes parce que trop d'entre elles sont des faux positifs - est l'une des principales causes du temps de présence des intrusions. Un SOC avec 10 détections à haute fidélité est opérationnellement supérieur à un SOC avec 500 détections bruyantes.

#!/usr/bin/env python3
# alert_quality_metrics.py - calculer la qualité des alertes à partir des dispositions des analystes

import json
from collections import defaultdict

# Charger les données de disposition des alertes (exportées depuis le SIEM/système de ticketing)
# Format : {rule_name, disposition (TP/FP/Benign_True_Positive), analyst, timestamp}
dispositions = json.load(open('/tmp/alert_dispositions.json'))

metrics = defaultdict(lambda: {'TP': 0, 'FP': 0, 'BTP': 0, 'total': 0})

for alert in dispositions:
rule = alert['rule_name']
disp = alert['disposition']
metrics[rule][disp] = metrics[rule].get(disp, 0) + 1
metrics[rule]['total'] += 1

print(f"{'Règle':<50} {'Total':>6} {'TP%':>6} {'FP%':>6} {'Signal':>8}")
print("-" * 80)

for rule, m in sorted(metrics.items(), key=lambda x: x[1]['total'], reverse=True):
total = m['total']
tp_pct = (m.get('TP',0) / total) * 100
fp_pct = (m.get('FP',0) / total) * 100
# Score signal : TP élevé + FP faible = signal élevé
signal = tp_pct - (fp_pct * 2)

flag = "BRUYANTE" if fp_pct > 50 else ("BONNE" if tp_pct > 70 else "")
print(f"{rule:<50} {total:>6} {tp_pct:>5.1f}% {fp_pct:>5.1f}% {signal:>7.1f} {flag}")

Playbook d'Ajustement

# Lorsqu'une règle a un taux de FP > 50%, l'ajuster :

# Étape 1 : Comprendre le motif des FP
# Interroger toutes les dispositions FP pour la règle bruyante et trouver les caractéristiques communes
# Splunk :
index=notable_events rule_name="PowerShell Download Cradle" disposition=false_positive
| stats count by user, CommandLine, host
| sort - count

# Étape 2 : Ajouter une exception (liste blanche) sans supprimer la règle
# Option A : Exclure des utilisateurs/hôtes spécifiques
# Dans la règle Sigma - ajouter un filtre :
# filter_known_good:
# user|contains:
# - 'svc-deployagent' # Service d'automatisation du déploiement
# - 'SCCM-Client'
# CommandLine|contains:
# - 'WindowsUpdate.ps1' # Script légitime connu

# Option B : Augmenter le seuil (alerter seulement après N occurrences)
# Option C : Ajouter un score de risque au lieu d'une alerte (alerter seulement quand le risque combiné > seuil)

# Étape 3 : Documenter la décision d'ajustement
# Chaque suppression doit être documentée avec :
# - Ce qui a été supprimé
# - Pourquoi (motif FP spécifique)
# - Date de révision (les suppressions expirent et sont réévaluées)
# - Propriétaire (qui a approuvé)

9. Cartographie de la Couverture de Détection - ATT&CK & Analyse des Lacunes

Évaluation de la Couverture ATT&CK

#!/usr/bin/env python3
# coverage_map.py - cartographier les détections existantes sur les techniques ATT&CK
# Utilise le format de couche ATT&CK Navigator de MITRE

import json

# Vos détections actuelles mappées aux identifiants de techniques ATT&CK
current_detections = {
"T1059.001": {"name": "PowerShell", "coverage": "high", "rules": 4},
"T1558.003": {"name": "Kerberoasting", "coverage": "high", "rules": 2},
"T1003.006": {"name": "DCSync", "coverage": "high", "rules": 2},
"T1071.001": {"name": "Web Protocols C2", "coverage": "medium", "rules": 3},
"T1046": {"name": "Port Scanning", "coverage": "low", "rules": 1},
"T1021.002": {"name": "SMB Lateral", "coverage": "medium", "rules": 2},
"T1548.002": {"name": "UAC Bypass", "coverage": "none", "rules": 0},
"T1055": {"name": "Process Injection", "coverage": "none", "rules": 0},
"T1027": {"name": "Obfuscation", "coverage": "low", "rules": 1},
"T1574": {"name": "DLL Hijacking", "coverage": "none", "rules": 0},
}

# Générer une couche ATT&CK Navigator
color_map = {"high": "#00aa00", "medium": "#ffaa00", "low": "#ff5500", "none": "#ff0000"}

layer = {
"name": "Couverture de Détection SOC",
"versions": {"attack": "14", "navigator": "4.9"},
"domain": "enterprise-attack",
"techniques": []
}

for technique_id, info in current_detections.items():
layer["techniques"].append({
"techniqueID": technique_id,
"color": color_map[info["coverage"]],
"comment": f"Règles: {info['rules']} | Couverture: {info['coverage']}",
"enabled": True,
"score": {"high": 100, "medium": 66, "low": 33, "none": 0}[info["coverage"]]
})

with open('/tmp/attack_coverage_layer.json', 'w') as f:
json.dump(layer, f, indent=2)

print("Résumé de la couverture :")
for level in ["high", "medium", "low", "none"]:
count = sum(1 for v in current_detections.values() if v["coverage"] == level)
print(f" {level:>8}: {count} techniques")

# Téléverser vers ATT&CK Navigator : https://mitre-attack.github.io/attack-navigator/
# Fichier -> Ouvrir une Couche Existante -> téléverser attack_coverage_layer.json
# Atomic Red Team - valider que les détections se déclenchent réellement
# Atomic Red Team fournit des cas de test pour chaque technique ATT&CK

# Installer
Install-Module -Name invoke-atomicredteam -Scope CurrentUser

# Exécuter un test de technique spécifique (T1558.003 = Kerberoasting)
Invoke-AtomicTest T1558.003 -TestNumbers 1 # Exécuter le test 1
# Vérifier votre SIEM - l'alerte s'est-elle déclenchée ?

# Exécuter et vérifier automatiquement la détection
Invoke-AtomicTest T1059.001 -TestNumbers 2 -CheckPrereqs
Invoke-AtomicTest T1059.001 -TestNumbers 2 # Exécuter
Invoke-AtomicTest T1059.001 -TestNumbers 2 -Cleanup # Nettoyer les artefacts

# Tester en lot les lacunes de couverture
$gap_techniques = @("T1548.002", "T1055", "T1574", "T1027")
foreach ($tech in $gap_techniques) {
Write-Host "Test de $tech..."
Invoke-AtomicTest $tech -CheckPrereqs -Confirm:$false
Invoke-AtomicTest $tech -Confirm:$false
Start-Sleep 30 # Attendre que le SIEM traite
Write-Host "Vérifier l'alerte SIEM pour $tech"
}

10. Cartographie MITRE ATT&CK

TechniqueIDMéthode de DétectionSource de Journaux
Commandes et Scripts : PowerShellT1059.001Journalisation des blocs de script Événement 4104Journal PowerShell Windows
Vidage des Identifiants OS : DCSyncT1003.006Événement 4662, GUID de réplicationJournal de Sécurité Windows
Vol de Tickets Kerberos : KerberoastingT1558.003Événement 4769 chiffrement RC4Journal de Sécurité Windows
Mouvement Latéral : Pass-the-HashT1550.002Événement 4624 LogonType 3 NTLMJournal de Sécurité Windows
Persistance par Tâche PlanifiéeT1053.005Événement 4698/4702Journal de Sécurité Windows
Exfiltration : Tunneling DNST1048.003Longues requêtes de sous-domainesJournaux de requêtes DNS
C2 : BeaconingT1071.001Analyse du timing des fluxZeek conn.log / pare-feu
Découverte : Scan de PortsT1046Alertes de scan inter-zonesPare-feu / IDS
Évasion de Défense : LOLBinsT1218Exécution de binaires non signésSysmon Événement 1, auditd
Création de CompteT1136.001Événement 4720Journal de Sécurité Windows
Collecte : Extraction d'EmailsT1114Journaux de passerelle mailExchange/O365 audit
Phishing : SpearphishingT1566.001Alertes de sécurité email + 4688Passerelle email + EDR

Fin du Chapitre 4.2 - SIEM, SOAR & Ingénierie de Détection

Suivant : Chapitre 4.3 - Réponse aux Incidents & Informatique Légale Numérique