Détecter le PowerShell malveillant avec les blocs de script
Transformez les journaux PowerShell en détections : blocs 4104 reconstitués, événements AMSI et moteur, obfuscation, PowerShell non géré, bruit d'administration filtré.
Le guide pilier sur la sécurité de PowerShell active la journalisation. Ce guide traite de ce qu'on en fait : où arrivent les preuves, comment reconstituer les longs scripts à partir de fragments, quels schémas distinguent un attaquant d'un administrateur, et comment régler les détections pour que vos analystes les lisent encore le mois prochain.
La télémétrie disponible
| Source | Journal | Événement | Ce qu'elle apporte |
|---|---|---|---|
| Journalisation des blocs de script | Microsoft-Windows-PowerShell/Operational | 4104 | Texte du script désobfusqué, ID du bloc de script, chemin |
| Journalisation des modules | Microsoft-Windows-PowerShell/Operational | 4103 | Exécution du pipeline avec les valeurs des paramètres |
| Cycle de vie du moteur | Windows PowerShell (journal classique) | 400, 403, 600 | Démarrage/arrêt du moteur, HostApplication, EngineVersion |
| Création de processus | Sécurité | 4688 | Ligne de commande et parent (avec l'audit de la ligne de commande activé) |
| Sysmon | Microsoft-Windows-Sysmon/Operational | 1, 7 | Arborescence des processus avec empreintes ; chargements de DLL |
| AMSI via Defender | Microsoft-Windows-Windows Defender/Operational | 1116, 1117 | Contenu de script malveillant détecté et traité |
PowerShell 7 écrit dans son propre journal PowerShellCore/Operational une fois son manifeste d'événements enregistré, et utilise ses propres paramètres de stratégie. Si PowerShell 7 est installé, collectez les deux journaux, sinon vous aurez un angle mort avec un nom d'exécutable différent (pwsh.exe).
Toutes ces sources ne sont utiles qu'en dehors de l'hôte. Transférez-les avec Windows Event Forwarding ou votre agent, et augmentez la taille du journal Operational pour qu'un serveur chargé ne le fasse pas tourner avant la collecte.
Étape 1 : réassembler les blocs de script fragmentés
Les gros scripts sont répartis sur plusieurs événements 4104. Chaque fragment porte le même ScriptBlock ID et un compteur « partie n sur m ». Les analystes qui lisent des événements isolés voient du code tronqué, et les règles de détection manquent les motifs à cheval sur une frontière. Reconstituez-les d'abord :
$events = Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational'; Id = 4104
} -MaxEvents 5000
$events | Group-Object { $_.Properties[3].Value } | ForEach-Object {
$parts = $_.Group | Sort-Object { [int]$_.Properties[0].Value }
[pscustomobject]@{
ScriptBlockId = $_.Name
Time = ($parts | Select-Object -First 1).TimeCreated
Path = $parts[0].Properties[4].Value
Text = ($parts | ForEach-Object { $_.Properties[2].Value }) -join ''
}
}Dans un SIEM, faites de même avec un regroupement par ID de bloc de script avant la correspondance. Un Path vide signifie que le code provient d'une invite interactive, de -Command, de -EncodedCommand ou de la mémoire plutôt que d'un fichier sur disque. Cela seul constitue un pivot utile.
Étape 2 : commencer par ce que PowerShell signale déjà
Même sans stratégie, PowerShell 5.1 journalise les blocs de script contenant des termes connus comme suspects sous forme d'événements 4104 de niveau avertissement. Avec la journalisation des blocs de script activée, ceux-ci restent marqués comme avertissements parmi les événements détaillés. Ils constituent une première alerte peu bruyante :
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational'; Id = 4104; Level = 3
} -MaxEvents 200 | Select-Object TimeCreated, MachineName, @{n='Snippet';e={ $_.Properties[2].Value.Substring(0, [Math]::Min(200, $_.Properties[2].Value.Length)) }}Revoyez chaque événement de niveau avertissement pendant quelques semaines. Vous apprendrez quels scripts d'administration les déclenchent et pourrez les exclure par chemin.
Étape 3 : chasser les schémas dont les attaquants ont besoin
L'outillage offensif doit faire un petit nombre de choses, et chacune laisse des mots dans le texte du script. Regroupez les indicateurs selon l'objectif de l'attaquant :
| Objectif | Indicateurs dans les 4104 / lignes de commande |
|---|---|
| Télécharger et exécuter | DownloadString, DownloadData, Net.WebClient, Invoke-WebRequest / iwr redirigé vers Invoke-Expression / iex, Start-BitsTransfer vers des chemins temporaires |
| Masquer la charge utile | FromBase64String, -EncodedCommand et ses préfixes (-e, -ec, -enc), IO.Compression.DeflateStream, GzipStream, -bxor |
| Obfusquer les chaînes | Usage intensif de l'accent grave (`i`e`x), tableaux [char] joints avec -join, inversion de chaînes, réordonnancement par opérateur de format ('{1}{0}' -f), casse aléatoire |
| Exécuter en mémoire | [Reflection.Assembly]::Load, Add-Type avec DllImport, VirtualAlloc, GetProcAddress, GetDelegateForFunctionPointer |
| Aveugler les défenses | AmsiUtils, amsiInitFailed, cachedGroupPolicySettings, Set-MpPreference avec des commutateurs -Disable*, Add-MpPreference -ExclusionPath |
| Rester caché | -WindowStyle Hidden / -w hidden, -NoProfile, -NonInteractive, -ExecutionPolicy Bypass combinés avec ce qui précède |
| Récolter des identifiants | Références à lsass, MiniDumpWriteDump, boucles Get-Credential, conversions de SecureString depuis des fichiers |
Deux choses rendent ces règles exploitables :
- Combiner les indicateurs.
-NoProfileseul est partout.-NoProfileplus une fenêtre masquée plus un téléchargeur (download cradle), non. - Normaliser d'abord. Passez le texte en minuscules et retirez les accents graves avant la correspondance, et faites correspondre les options de ligne de commande par préfixe.
powershell.exeaccepte/aussi bien que-, ainsi que les noms de paramètres partiels.
Le dépôt ouvert de règles Sigma contient des règles PowerShell bien maintenues que vous pouvez traduire pour votre SIEM plutôt que de tout écrire vous-même.
Étape 4 : surveiller le moteur, pas seulement les scripts
Certains des meilleurs signaux se trouvent dans le journal classique Windows PowerShell et dans la télémétrie des processus plutôt que dans le texte des scripts :
- Tentatives de rétrogradation. Un événement 400 avec
EngineVersion=2.0signifie que quelqu'un a lancé le moteur PowerShell 2.0, sans journalisation des blocs de script ni AMSI. Alertez sur chaque occurrence et supprimez le moteur comme décrit dans le guide pilier. - Hôtes inhabituels. Les événements 400 et 600 incluent
HostApplication. PowerShell exécuté dans autre chose quepowershell.exe,pwsh.exeou l'ISE est suspect. - PowerShell non géré. Des outils peuvent charger le moteur PowerShell dans n'importe quel processus .NET. L'événement Sysmon 7 pour
System.Management.Automation.dll(ou son image native) chargé par un processus inattendu le détecte. Utilisez votre configuration Sysmon pour inclure cette seule DLL plutôt que de journaliser tous les chargements d'images. - Parents suspects. 4688 ou Sysmon 1 où
powershell.exeest l'enfant dewinword.exe,excel.exe,outlook.exe,mshta.exe,wmiprvse.exe,w3wp.exeousqlservr.exe. Le PowerShell lancé par Office est aussi bloqué purement et simplement par les règles de réduction de la surface d'attaque.
Étape 5 : utiliser les détections AMSI comme alertes de haute fidélité
L'Antimalware Scan Interface transmet le contenu des scripts à Defender à l'exécution, après désobfuscation. Quand Defender bloque un script, l'utilisateur voit « This script contains malicious content and has been blocked by your antivirus software », et Defender journalise 1116 (détecté) et 1117 (action effectuée). La ressource de la détection fait référence à AMSI et au processus hôte plutôt qu'à un fichier sur disque.
Traitez toute détection AMSI sur un serveur comme un incident, pas comme une nuisance antivirus. Activez d'abord en mode audit la règle ASR Block execution of potentially obfuscated scripts pour voir ce qu'elle intercepterait.
Étape 6 : détecter les tentatives d'éteindre la lumière
Les attaquants qui connaissent la journalisation tentent de la désactiver :
- Modifications du registre sous
HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell(Sysmon 13, ou une SACL avec 4657). - Effacement des journaux : 1102 (Sécurité) et 104 (Système, pour tout autre journal). Voir la référence des ID d'événements de sécurité pour les autres signaux de falsification.
- Un hôte dont le volume de 4104 tombe à zéro alors qu'il remonte toujours d'autres événements. Construisez un contrôle « hôte silencieux » par journal, pas seulement par machine.
- Modifications de Defender : 5001 (protection en temps réel désactivée) et 5007 (configuration modifiée, y compris de nouvelles exclusions).
Étape 7 : trier une alerte
Quand une règle se déclenche, répondez à quatre questions avant de la déclarer bénigne :
- Qui et où ? Compte, hôte et session d'ouverture de session. Pivotez sur la session pour trouver l'événement 4624 : un administrateur interactif à la console, ce n'est pas la même histoire qu'une ouverture de session réseau venant d'un poste de travail.
- Qu'est-ce qui l'a lancé ? Remontez la chaîne des parents dans 4688 ou Sysmon 1. Une tâche planifiée, un service, un document Office et une session WinRM distante (
wsmprovhost.exe) désignent chacun des points d'entrée différents. - Qu'a-t-il fait ensuite ? Examinez le script reconstitué, puis les connexions réseau (Sysmon 3 et 22), les écritures de fichiers (Sysmon 11) et les processus enfants dans les minutes qui suivent.
- Est-ce connu ? Recherchez l'empreinte du bloc de script ou des chaînes distinctives sur tout le parc. Le même script sur des centaines de machines est généralement un agent ; le même script sur trois serveurs pendant la nuit est généralement une intrusion.
Consignez la réponse dans les notes de réglage de la règle, pour que l'analyste suivant ne reparte pas de zéro.
Ce qui casse
L'ingénierie de détection pour PowerShell met surtout à l'épreuve la patience de vos analystes :
- Les agents de gestion ressemblent à des logiciels malveillants. Configuration Manager, l'Intune Management Extension, les agents de supervision et les outils de réponse EDR exécutent couramment PowerShell avec
-ExecutionPolicy Bypass,-NoProfileet des commandes encodées. Excluez-les par chemin complet du parent et signataire, jamais par le seul nom de processus. - Volume. Une journalisation complète des blocs de script sur un serveur de gestion chargé produit beaucoup d'événements 4104, tout comme chaque exécution DSC ou de supervision. Dimensionnez le journal en conséquence et filtrez dans le SIEM, pas en désactivant la journalisation.
- Secrets dans les journaux. Le texte des scripts inclut tout ce que tapent les administrateurs, mots de passe compris. Restreignez l'accès en lecture aux journaux PowerShell dans votre SIEM, ou utilisez Protected Event Logging.
Vérifier
Testez chaque couche sur une machine de laboratoire avant de vous y fier en production :
# 1. Journalisation des blocs de script : un marqueur inoffensif et facile à retrouver
powershell.exe -NoProfile -Command "Write-Output 'sbl-canary-2026'"
# 2. AMSI : la chaîne de test documentée par Microsoft, détectée mais inoffensive
'AMSI Test Sample: 7e72c3ce-861b-4339-8740-0ac1484c1386'
# 3. Détection de la rétrogradation du moteur, là où le moteur v2 existe encore en laboratoire
powershell.exe -Version 2 -Command "exit"Confirmez ensuite que chaque événement a atteint le SIEM et déclenché la règle attendue : 4104 contenant la chaîne marqueur, 1116 de Defender, 400 avec EngineVersion=2.0. S'il en manque un, réparez la chaîne de collecte avant d'écrire d'autres règles. Le Constrained Language Mode réduit ce que peuvent faire les attaquants ; la détection vous indique qu'ils ont essayé. Plus d'informations sur les deux sur la page thématique Sécurité de PowerShell.
Liste de contrôle
- 4103, 4104, 400/403/600 et PowerShellCore/Operational transférés en quasi temps réel
- Blocs de script réassemblés par ScriptBlock ID avant la correspondance des règles
- Alerte sur les 4104 de niveau avertissement, réglée par chemin de script
- Règles à indicateurs combinés pour le téléchargement, l'obfuscation, l'exécution en mémoire et l'évasion des défenses
- Alerte sur l'événement 400 avec EngineVersion 2.0
- Règle Sysmon pour System.Management.Automation.dll chargée par des processus inattendus
- Alerte sur les parents Office, serveur web et WMI de PowerShell
- Détections AMSI (1116/1117) sur les serveurs traitées comme des incidents
- Détections d'hôte silencieux et de falsification des journaux en place
- Agents de gestion exclus uniquement par chemin complet et signataire
Questions fréquentes
La journalisation des blocs de script vient-elle à bout de l'obfuscation ?
En grande partie. PowerShell journalise chaque bloc de script au moment de sa compilation : les couches d'encodage, de compression et d'astuces sur les chaînes sont journalisées à nouveau une fois déballées en nouveaux blocs de script. La couche externe obfusquée est elle aussi journalisée, ce qui constitue déjà un signal fort. Les attaquants qui évitent complètement le moteur PowerShell, ou qui désactivent d'abord la journalisation, représentent la lacune que vous couvrez avec d'autres télémétries.
Faut-il alerter sur chaque -EncodedCommand ?
Pas aveuglément. Les agents de gestion, programmes d'installation et outils de supervision utilisent légitimement des commandes encodées : une alerte brute inonde la file. Alertez sur les commandes encodées dont le processus parent est inhabituel (Office, navigateurs, serveurs web, WMI), ou combinez l'encodage avec d'autres indicateurs comme des téléchargements, des fenêtres masquées ou de la réflexion. Établissez une référence des agents connus par chemin complet et excluez-les précisément.
Et si un attaquant désactive la journalisation des blocs de script ?
Il lui faut des droits d'administrateur et il laisse des traces : modifications du registre sous la clé de stratégie PowerShell, chute soudaine du volume de 4104 d'un hôte, journaux effacés (1102 ou 104), ou schémas de falsification en mémoire eux-mêmes journalisés avant de prendre effet. Transférez les événements centralement en quasi temps réel pour que les preuves quittent l'hôte avant de pouvoir être supprimées.
Détecter le PowerShell malveillant avec les blocs de script