fix: harden Defender threat risk audit

This commit is contained in:
PierreCsn committed 2026-10-27 13:38:46 +01:00
1 parent 0f39b54826
commit 7be3105d68
5 files changed
+836 -157

No files matched your search

+36 -20
View File
@@ -27,79 +27,95 @@ Règles par défaut, définies dans `DEFAULT_RULES` et surchargeables par les fo
| Detected / état non résolu | +2 |
| Failed remediation | +3 |
| Allowed | +3 |
| Répétition de la même menace dans la fenêtre | +2 |
| Temps réel désactivé en mode Normal | +2 |
| Répétition de la même menace dans la fenêtre | +2 à `correlation_level` |
| Menaces distinctes corrélées dans la fenêtre | +2 à `correlation_level` |
| Temps réel désactivé en mode Normal | +2 au risque courant; +2 à la corrélation si menace grave |
| Antivirus / service désactivé en mode Normal (chaque signal) | +2 |
| Surveillance comportementale ou IOAV désactivée | +1 chacun |
| Signatures anciennes de plus de 3 jours / âge inconnu | +1 |
Le résultat est borné à `[0,16]`. Ces poids et les corrélations sont des règles métier Comitari, inspirées de Wazuh; ils ne reproduisent pas son moteur. L'échelle WAPT dépend de `current_risk_level`, jamais du maximum historique : 0–6 `OK`, 7–9 `WARNING`, 10–16 `ERROR`. En mode `Normal`, antivirus, service ou temps réel désactivé force `ERROR` indépendamment du score. `Passive`, `SxS Passive Mode` et `EDR Block Mode` ne déclenchent pas à eux seuls de faux incident; les menaces restent classifiées.
Le résultat est borné à `[0,16]`. `event_level` utilise la gravité et l'état de remédiation de cette détection; ni la configuration actuelle du poste ni le nombre d'événements ne la réécrivent. `correlation_level` agrège les événements associés sur six heures. `current_risk_level` combine les menaces non résolues, les investigations requises, les corrélations et l'état de protection. Ces poids sont des règles métier Comitari inspirées de Wazuh; ils ne reproduisent pas son moteur. Le statut WAPT utilise le risque courant : 0–6 `OK`, 7–9 `WARNING`, 10–16 `ERROR`. Un échec explicite de remédiation force aussi `ERROR`, même si le niveau numérique est inférieur à 10. En mode `Normal`, antivirus, service ou temps réel désactivé force `ERROR` indépendamment du score. `Passive`, `SxS Passive Mode` et `EDR Block Mode` ne déclenchent pas à eux seuls de faux incident; les menaces restent classifiées.
`Quarantined` reçoit une réduction de score mais reste dans `active_threats`, car la quarantaine ne prouve pas l'absence de compromission. `Cleaned`, `Removed` et `Blocked` ne maintiennent pas seuls le risque courant; toutefois `IsActive`, `DidThreatExecute` ou un état d'exécution `Allowed`/`Executing` les maintient comme risque non résolu. Les événements récents restent aussi dans `detections` et l'historique WAPT. `DidThreatExecute` et `CurrentThreatExecutionStatusID` sont des signaux explicites, séparés du nom et de la sévérité de signature; aucun nom (y compris « ransomware ») n'est interprété comme preuve d'exécution ou de comportement.
`Quarantined` reste non résolu et distinct de `Cleaned` ou `Removed`. `DidThreatExecute` est un signal historique : il ne maintient pas seul une détection correctement supprimée dans `active_threats`; il positionne `execution_observed` et `requires_investigation`. Pour `CurrentThreatExecutionStatusID`, seuls `3 = Executing` signifie une exécution en cours; `2 = Allowed` signifie que l'exécution a été autorisée et exige une investigation, mais ne prouve pas qu'elle est en cours. `1 = Blocked` n'est pas une preuve de compromission. Le `IsActive` de `Get-MpThreat` est une valeur agrégée par `ThreatID`; il ne rouvre pas un enregistrement `DetectionID` explicitement résolu, mais le conflit est signalé par `requires_investigation`. Aucun nom de menace (y compris « ransomware ») n'est interprété comme preuve d'exécution.
## Fenêtres et corrélations
- Fenêtre glissante par défaut : six heures; une détection y entre si sa première détection **ou** son dernier changement de statut se trouve dans cette fenêtre.
- Les événements résolus sont conservés dans la collecte courante pendant 30 jours. Toute menace non résolue est conservée sans limite d'ancienneté, donc une menace active de plus de six heures ne disparaît pas.
- `max_event_level_6h` porte sur les événements récents, `max_active_level` sur toutes les menaces non résolues et `current_risk_level` sur le maximum du risque actif et de l'état de protection.
- `max_event_level_6h` porte sur les événements récents, `max_active_level` sur les menaces non résolues, `max_investigation_level` sur les incidents demandant une investigation, `correlation_level` sur les règles de corrélation et `current_risk_level` sur le risque actuel du poste.
- Corrélations sur six heures : répétition d'une même menace, menaces distinctes multiples, menace High/Severe avec temps réel désactivé, échecs de remédiation répétés et menace explicitement autorisée.
- `DetectionID` est la clé de déduplication documentée, combinée à `ThreatID`. En son absence, un composite stable de `ThreatID`, dates, statut et empreinte interne des ressources est utilisé; l'identité incomplète est signalée dans le résumé. Les événements distincts avec des DetectionID différents ne sont pas fusionnés parce qu'ils partagent un ThreatID.
- Il n'y a plus de limite arbitraire à 100 détections. La collecte transmet tous les événements de moins de 30 jours ainsi que les états non résolus plus anciens. Les données étant complètes ou la collecte échouant explicitement, aucun incident critique n'est silencieusement tronqué.
## Audit WAPT et Reporting
L'API utilisée est `WAPT.write_audit_data_if_changed(section, key, value, ptype=None, value_date=None, expiration_date=None, max_count=2, keep_days=None)`, documentée dans l'API Setuphelpers WAPT 2.6. Les valeurs sont des dictionnaires/listes JSON natifs, consultables dans Audit Data / Reporting. Le paquet écrit un agrégat `snapshot` version 2 et conserve aussi les clés historiques `summary`, `detections` et `active_threats`, plus `correlations`. La collecte n'ajoute pas d'horodatage courant qui rendrait chaque valeur différente. WAPT conserve jusqu'à 512 changements pendant 90 jours, selon la première limite atteinte.
Le paquet utilise `WAPT.write_audit_data_if_changed(section, key, value, ptype=None, value_date=None, expiration_date=None, max_count=2, keep_days=None)` et `WAPT.read_audit_data(section, key, default=None, ptype=None, include_expired_data=True)`, documentées pour WAPT 2.6. Le schéma v3 évite un grand snapshot dupliqué : la clé `summary` contient le dernier résumé léger (historique limité à 128 changements/90 jours), `correlations` contient le dernier agrégat de corrélations, et chaque incident a sa clé `event.<sha256>` avec jusqu'à 16 évolutions/90 jours. Les écritures identiques ne créent pas une nouvelle valeur WAPT. Les événements utilisent un format JSON v1 indépendant de Reporting; aucune requête HTTP ou intégration Vector n'est incluse. Les anciennes clés v2 cessent d'être mises à jour et expirent selon leur rétention d'origine.
Exemple synthétique de structure :
```json
{
"schema_version": 2,
"schema_version": 3,
"collection_complete": true,
"defender": {
"running_mode": "Normal",
"antivirus_enabled": true,
"realtime_enabled": true,
"behavior_monitor_enabled": true,
"ioav_enabled": true,
"signature_age": 0,
"signature_version": "1.000.0.0",
"signature_last_updated": "2026-10-27T09:00:00Z",
"engine_version": "1.1.0.0",
"service_enabled": true
"signature_age": 0
},
"summary": {
"max_event_level_6h": 0,
"max_active_level": 0,
"max_investigation_level": 0,
"correlation_level": 0,
"current_risk_level": 0,
"current_risk_category": "Aucun événement",
"audit_status": "OK",
"collection_complete": true,
"detection_count_6h": 0,
"active_count": 0,
"investigation_count": 0,
"remediation_failed": false,
"resolved_count_6h": 0,
"identity_incomplete_count": 0,
"unknown_severity_count": 0,
"protection_issues": [],
"correlation_count": 0
},
"detections": [],
"active_threats": [],
"correlations": []
}
}
```
Chaque clé `event.<sha256>` conserve un événement compact, par exemple :
```json
{
"schema_version": 1,
"event_id": "sha256-stable-par-DetectionID",
"source": "comi-audit-defender",
"event_type": "threat.resolved",
"event_level": 9,
"remediation_state": "removed",
"execution_observed": true,
"requires_investigation": true,
"active": false,
"timestamp": "2026-10-27T11:30:00Z"
}
```
## Données et limites de collecte
- `Get-MpThreatDetection` fournit le ThreatID, DetectionID, ThreatStatusID, dates de détection/changement/remédiation, code d'erreur, réussite d'action, état d'exécution et ressources. `Get-MpThreat` est joint en Python par `ThreatID` pour ThreatName, sévérité, IsActive et DidThreatExecute.
- `Get-MpThreatDetection` fournit le ThreatID, DetectionID, ThreatStatusID, dates de détection/changement/remédiation, code d'erreur, réussite d'action, état d'exécution et ressources. `Get-MpThreat` est joint en Python par `ThreatID` pour ThreatName, sévérité, IsActive et DidThreatExecute. Le booléen `ActionSuccess=False` seul n'est pas un échec : la cmdlet documente sa valeur par défaut à `false`; le paquet exige un statut d'échec explicite ou un code d'erreur non nul.
- Les chemins complets des ressources, noms d'utilisateurs et noms de processus ne sont ni sérialisés ni transmis. Seuls le nombre et les catégories `file`, `process`, `registry`, `service`, `url` ou `other` sont conservés.
- Defender peut ne pas exposer une propriété sur une version donnée. Les valeurs absentes sont traitées comme inconnues; une détection de sévérité inconnue reçoit le niveau de base `Unknown`. Une panne de la cmdlet entière force `ERROR`.
- Les valeurs de `ThreatStatusID` utilisées viennent de `MSFT_MpThreatDetection` : 0 Unknown, 1 Detected, 2 Cleaned, 3 Quarantined, 4 Removed, 5 Allowed, 6 Blocked, 102 QuarantineFailed, 103 RemoveFailed, 104 AllowFailed, 105 Abandoned, 107 BlockedFailed. Les différents enums Microsoft peuvent différer selon l'API; ce paquet cible les objets des cmdlets Defender.
- Defender ne livre pas une chronologie complète des actions humaines ni une preuve d'exécution malveillante. `DidThreatExecute` et `CurrentThreatExecutionStatusID` sont exposés lorsque disponibles, sans déduction comportementale sur le nom.
- Defender ne livre pas une chronologie complète des actions humaines ni une preuve d'exécution malveillante. `DidThreatExecute` est agrégé au niveau `ThreatID` et peut s'appliquer à plusieurs `DetectionID` joints au même ThreatID; les enregistrements restent distincts, mais cette attribution agrégée peut nécessiter une investigation. Sans `DetectionID`, l'identifiant d'événement utilise les dates/ressources disponibles et le résumé signale l'identité incomplète.
- La réponse PowerShell contient l'ensemble des événements récents et actifs pertinents; le timeout de 90 s provoque un état `ERROR`, sans limite de 100 qui masquerait une détection.
- La table WAPT garde les transitions par clé d'incident. Les rapports doivent sélectionner la dernière valeur de chaque `event.<sha256>`; `value_date` donne l'âge de la dernière observation enregistrée.
## Tests locaux
Les exemples SQL pour le Reporting WAPT sont dans [docs/WAPT-REPORTING.md](docs/WAPT-REPORTING.md).
Les tests utilisent uniquement des données synthétiques et des mocks Python; aucune commande Defender réelle ni WAPT n'est invoquée :
```shell