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
- Architecture SIEM - Ingestion, Normalisation & Corrélation
- Sources de Journaux & Collecte - Quoi Ingérer et Pourquoi
- Ingénierie de Détection - Sigma, YARA & Développement de Règles
- Elastic Stack (ELK) - Déploiement, Pipelines & Détection
- Splunk - SPL, Recherches de Corrélation & Tableaux de Bord
- Analytique Comportementale - UBA, Baselines & Détection d'Anomalies
- SOAR - Automatisation des Playbooks & Orchestration
- Triage des Alertes & Gestion des Faux Positifs
- Cartographie de la Couverture de Détection - ATT&CK & Analyse des Lacunes
- 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 :
| Étape | Défaillance Courante | Conséquence |
|---|---|---|
| Collecte | Agent non installé, rotation des journaux avant la collecte | Télémétrie manquante - angles morts |
| Transport | Perte UDP Syslog, mauvaise configuration TLS | Perte de données en volume ; texte en clair en transit |
| Analyse | Mauvais analyseur pour le format de journal, changement de version du fournisseur | Champs non extraits - les règles de corrélation échouent |
| Normalisation | Noms de champs incohérents (src_ip vs sourceAddress) | Corrélation multi-sources impossible |
| Indexation | Stockage plein, niveau chaud épuisé | Événements récents non consultables |
| Corrélation | Règles trop larges, aucune baseline | Fatigue 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 :
| Dimension | Mauvaise Règle | Bonne Règle |
|---|---|---|
| Spécificité | Se déclenche sur toute exécution PowerShell | Se déclenche sur les commandes encodées avec net.webclient |
| Sensibilité | Ne se déclenche que sur un hachage connu | Se déclenche sur le motif de comportement (quel que soit le hachage) |
| Résilience | Contournée en renommant cmd.exe | Basée sur les appels système - résistante au renommage |
| Maintenabilité | Liste d'IOC codée en dur | Basée sur la logique avec un flux externe de threat intel |
| Couverture | Source de journaux unique | Corré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
| Technique | ID | Méthode de Détection | Source de Journaux |
|---|---|---|---|
| Commandes et Scripts : PowerShell | T1059.001 | Journalisation des blocs de script Événement 4104 | Journal PowerShell Windows |
| Vidage des Identifiants OS : DCSync | T1003.006 | Événement 4662, GUID de réplication | Journal de Sécurité Windows |
| Vol de Tickets Kerberos : Kerberoasting | T1558.003 | Événement 4769 chiffrement RC4 | Journal de Sécurité Windows |
| Mouvement Latéral : Pass-the-Hash | T1550.002 | Événement 4624 LogonType 3 NTLM | Journal de Sécurité Windows |
| Persistance par Tâche Planifiée | T1053.005 | Événement 4698/4702 | Journal de Sécurité Windows |
| Exfiltration : Tunneling DNS | T1048.003 | Longues requêtes de sous-domaines | Journaux de requêtes DNS |
| C2 : Beaconing | T1071.001 | Analyse du timing des flux | Zeek conn.log / pare-feu |
| Découverte : Scan de Ports | T1046 | Alertes de scan inter-zones | Pare-feu / IDS |
| Évasion de Défense : LOLBins | T1218 | Exécution de binaires non signés | Sysmon Événement 1, auditd |
| Création de Compte | T1136.001 | Événement 4720 | Journal de Sécurité Windows |
| Collecte : Extraction d'Emails | T1114 | Journaux de passerelle mail | Exchange/O365 audit |
| Phishing : Spearphishing | T1566.001 | Alertes de sécurité email + 4688 | Passerelle 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