Journaliser et durcir PowerShell sur un parc Windows
Section intitulée « Journaliser et durcir PowerShell sur un parc Windows »PowerShell est à la fois l’outil d’administration de référence sous Windows et l’un des interpréteurs les plus utilisés après compromission (technique T1059.001 de MITRE ATT&CK). L’interdire n’a aucun sens ; le rendre bavard et le brider là où il n’a rien à faire en a beaucoup. Cette page couvre les cinq leviers natifs, ce que chacun voit réellement, l’ordre dans lequel les activer et ce qu’il reste à surveiller.
Imagine une salle d’examen où l’on autorise les calculatrices, parce que tout le monde en a besoin. Le surveillant ne les confisque pas : il note ce que chacun tape, vérifie qu’aucune n’a été trafiquée et range au placard le vieux modèle sans mémoire. Cette page explique comment faire pareil avec l’outil le plus puissant d’un ordinateur Windows.
→ Lire l’explication complète de la marmotte
Ce que voient Script Block Logging, Module Logging et la transcription
Section intitulée « Ce que voient Script Block Logging, Module Logging et la transcription »Les trois mécanismes de journalisation ne capturent pas la même chose, et les confondre conduit à activer le plus bruyant en croyant activer le plus parlant.
Script Block Logging (événement 4104, journal Microsoft-Windows-PowerShell/Operational) enregistre le texte de chaque bloc de script au moment où le moteur le compile. C’est le point décisif : une chaîne obfusquée passée à Invoke-Expression devient un nouveau bloc, compilé, donc journalisé en clair. Plusieurs couches d’encodage et de concaténation produisent autant d’événements, dont le dernier contient le code effectivement exécuté. Un script long est découpé en plusieurs événements reliés par ScriptBlockId et numérotés MessageNumber/MessageTotal ; un SIEM (Security Information and Event Management) qui ne recolle pas ces fragments indexe des morceaux de script sans queue ni tête. Depuis PowerShell 5, même sans activation, le moteur journalise en niveau Avertissement les blocs contenant des mots-clés jugés suspects. Ce filet automatique repose sur une liste figée et publique : il attrape les scripts paresseux, pas les autres. Les événements 4105/4106 (début et fin d’invocation) se trouvent dans le même réglage, option « Log script block invocation start/stop events » : les laisser désactivés, ils multiplient le volume sans rien apporter de lisible.
Module Logging (événement 4103) journalise l’exécution du pipeline : cmdlet appelée, paramètres liés, valeurs passées. Il voit ce que Script Block Logging ne montre pas directement, par exemple la valeur d’une variable au moment de l’appel. Il est configuré par liste de modules ; la valeur * couvre tout et produit un volume considérable. Le bon usage est ciblé : modules d’administration sensibles (Active Directory, gestion des comptes, accès distant), pas l’intégralité du parc.
La transcription écrit dans un fichier texte ce qui entre et sort d’une session, en-têtes d’invocation compris (EnableInvocationHeader). C’est le seul mécanisme qui capture la sortie des commandes, donc ce que l’opérateur a vu : la liste des comptes énumérés, le contenu du fichier lu. Les fichiers atterrissent dans OutputDirectory, par défaut dans le profil de l’utilisateur, c’est-à-dire là où un intrus les lira avant les analystes. Le répertoire doit être un partage UNC (Universal Naming Convention) en écriture seule : les comptes du domaine peuvent y créer des fichiers, personne sauf le compte de collecte ne peut les relire, et une ACL (Access Control List) refuse la lecture et la modification à CREATOR OWNER. Un partage de transcription lisible par tous est une fuite organisée, horodatée et indexée par nom de machine.
La position à retenir : Script Block Logging partout, transcription partout avec un partage verrouillé, Module Logging ciblé. Le premier donne le code, la troisième donne le contexte et le résultat, le second sert l’enquête sur les modules qui comptent. Ces journaux n’ont de valeur que centralisés : collectés via WEF (Windows Event Forwarding) ou un agent, comme le détaille le guide journalisation Sysmon sur postes Windows. Un 4104 qui ne quitte jamais la machine est un 4104 que l’intrus efface à loisir.
Le schéma suivant montre où chaque mécanisme intervient dans le cycle d’exécution d’une commande ; à lire en particulier : la journalisation des blocs et AMSI travaillent sur le même texte, au même moment, avant l’exécution.
AMSI : l’analyse en mémoire et ses angles morts
Section intitulée « AMSI : l’analyse en mémoire et ses angles morts »AMSI (Antimalware Scan Interface) est une interface par laquelle un interpréteur soumet du contenu à l’antivirus inscrit sur la machine avant de l’exécuter. PowerShell 5 et suivants l’appellent sur chaque bloc compilé, ce qui en fait la seule couche native capable de bloquer un script désobfusqué au vol. VBScript, JScript, les macros VBA (Visual Basic for Applications) d’Office et le chargement d’assemblages .NET en mémoire passent par la même interface.
Ce qu’AMSI ne garantit pas tient en une phrase : il n’a d’efficacité que celle du moteur d’analyse branché derrière. Un antivirus tiers qui ne fournit pas de fournisseur AMSI laisse l’interface en place et ne reçoit rien. Le contrôle doit porter sur la présence effective d’un fournisseur inscrit, pas sur la case « antivirus installé » de l’inventaire.
Les limites d’AMSI, connues et documentées, relèvent de trois familles. D’abord l’altération de l’état du moteur dans le processus courant, qui lui fait croire que l’analyse est indisponible. Ensuite la neutralisation de la bibliothèque d’analyse chargée en mémoire, pour qu’elle réponde toujours « rien à signaler ». Enfin l’obfuscation pure, qui ne désactive rien mais mise sur le fait qu’AMSI juge un texte sur signature : un code suffisamment découpé et renommé ne déclenche aucune correspondance. Le dénominateur commun est que ces contournements vivent dans le processus PowerShell lui-même et avec ses privilèges. D’où la contre-mesure structurante : ces manipulations touchent des types et des fonctions dont le nom est stable, donc leur préparation est elle-même du texte qui transite par le 4104 avant même que l’altération prenne effet. AMSI tombe ; Script Block Logging, lui, a déjà enregistré la tentative. C’est l’argument décisif pour ne jamais compter sur AMSI seul : il est fait pour bloquer l’opportuniste, pas pour résister à qui le vise explicitement.
Constrained Language Mode et le retrait de PowerShell v2
Section intitulée « Constrained Language Mode et le retrait de PowerShell v2 »Le mode de langage contraint, ou CLM (Constrained Language Mode), restreint ce qu’un script peut faire sans l’empêcher de s’exécuter : plus d’appel direct à des types .NET arbitraires, plus de Add-Type, plus d’objets COM (Component Object Model), plus de P/Invoke (Platform Invoke, l’appel d’API système natives depuis du code managé). Les cmdlets signées et approuvées restent disponibles. L’immense majorité des techniques de chargement en mémoire dépendant de l’accès .NET brut, CLM leur coupe l’herbe sous le pied.
Le piège est qu’une variable d’environnement, __PSLockdownPolicy, pilote ce mode. Qui peut écrire ses propres variables d’environnement peut se replacer en mode plein. CLM n’a donc de valeur réelle que piloté par une politique de contrôle d’application : WDAC (Windows Defender Application Control) ou, à défaut, AppLocker. Quand une telle politique est en vigueur, PowerShell bascule automatiquement en CLM pour tout code non approuvé, et cet état n’est plus une variable que l’on retourne : il découle de la signature du code. CLM posé sans WDAC derrière est une barrière peinte sur le sol. L’ordre logique est donc inverse de l’ordre intuitif : la politique d’application d’abord, le mode contraint en conséquence. La mise en place d’AppLocker ou WDAC relève de la même démarche que le durcissement par minimisation de surface : retirer les capacités inutiles avant de surveiller celles qui restent.
PowerShell v2 mérite un sort à part. Cette version n’appelle pas AMSI, ne connaît pas Script Block Logging complet et ignore CLM. Elle reste installable comme fonctionnalité optionnelle sur des hôtes modernes, et un powershell -version 2 suffit à retomber dans un interpréteur aveugle si le moteur v2 et son prérequis .NET Framework 3.5 sont présents. Downgrade trivial, journalisation contournée : retirer la fonctionnalité MicrosoftWindowsPowerShellV2 et MicrosoftWindowsPowerShellV2Root est non négociable. C’est la première chose à faire, avant même d’activer quoi que ce soit, parce qu’activer la journalisation d’un moteur qu’on peut court-circuiter par une option de ligne de commande relève de la décoration.
Ordre d’activation et exemple de configuration
Section intitulée « Ordre d’activation et exemple de configuration »L’ordre qui tient en production va du plus bloquant au plus bavard, pour qu’un contournement d’une couche laisse toujours la trace d’une autre :
- Retirer PowerShell v2 (ferme la porte de sortie arrière).
- Déployer la politique WDAC ou AppLocker (impose CLM de fait).
- Vérifier qu’un fournisseur AMSI est inscrit et actif.
- Activer Script Block Logging sur tout le parc.
- Activer la transcription vers un partage en écriture seule.
- Activer Module Logging sur les modules sensibles seulement.
- Centraliser et recoller les événements côté collecte.
Les réglages de journalisation se poussent par stratégie de groupe (clés sous HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell). Extrait reproductible pour vérifier l’état réel d’un hôte, étape que trop de déploiements sautent :
# Retrait du moteur v2 (porte de downgrade), sur Windows clientDisable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart# Sur Windows Server, la fonctionnalité porte un autre nom :# Uninstall-WindowsFeature -Name PowerShell-V2
# État de Script Block Logging (1 = actif)(Get-ItemProperty 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging').EnableScriptBlockLogging
# Mode de langage courant : doit afficher ConstrainedLanguage sous WDAC/AppLocker$ExecutionContext.SessionState.LanguageMode
# Fournisseurs AMSI inscrits sur la machine (aucune clé = l'interface analyse dans le vide)Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\AMSI\Providers' | Select-Object -ExpandProperty PSChildName
# Service antimalware effectivement en marche (Microsoft Defender)(Get-MpComputerStatus).AMServiceEnabledLa troisième ligne est le juge de paix : si elle renvoie FullLanguage sur un poste bureautique, la politique d’application n’est pas appliquée et le reste du durcissement repose sur de la confiance.
Ce qui reste exploitable côté détection
Section intitulée « Ce qui reste exploitable côté détection »Même parfaitement configuré, cet empilement laisse des prises, et c’est tant mieux : la détection vit de là. Désactiver AMSI, retourner CLM ou purger le journal Operational sont des actions qui produisent elles-mêmes des événements ou précèdent un 4104 lisible. Quelques signaux à câbler dans le SIEM, à rapprocher d’une démarche de threat hunting :
- 4104 contenant les noms de types servant à toucher AMSI ou le champ d’état du moteur : la préparation est journalisée avant que l’altération agisse.
- Événement 1102 (purge du journal de sécurité) et effacement du journal PowerShell/Operational : l’effacement est un aveu.
- Processus
powershell.exeavec-version 2ou chargement deSystem.Management.Automationv2 : tentative de downgrade. - Chute brutale du volume de 4104 sur un hôte habituellement bavard : la journalisation a été coupée ou le trafic ne remonte plus.
- 4103 montrant des cmdlets d’énumération d’annuaire en rafale, à corréler avec l’audit des comptes orphelins.
Le compromis central de tout le sujet : on gagne une visibilité quasi totale sur le code exécuté et un plancher qui bloque l’opportuniste, on paie en volume de journaux et en travail de corrélation. Activer Script Block Logging sans dimensionner la collecte, c’est produire des téraoctets que personne ne lira, ce qui revient à ne rien journaliser avec, en prime, la facture de stockage.
Faut-il activer Module Logging avec * pour tout voir ?
Section intitulée « Faut-il activer Module Logging avec * pour tout voir ? »Non. La valeur * journalise chaque appel de pipeline sur tout le parc et noie les signaux utiles sous le trafic d’administration légitime. Script Block Logging donne déjà le code ; Module Logging sert à capturer les valeurs de paramètres sur des modules nommés et sensibles. Un périmètre ciblé coûte moins cher à collecter et reste lisible.
CLM protège-t-il un poste si l’utilisateur est administrateur local ?
Section intitulée « CLM protège-t-il un poste si l’utilisateur est administrateur local ? »Pas s’il repose uniquement sur la variable __PSLockdownPolicy, qu’un administrateur local réécrit. CLM ne tient que dérivé d’une politique WDAC ou AppLocker signée, où le mode contraint découle de l’absence de signature approuvée du code et non d’un réglage modifiable depuis la session.
🐝 Ce qui arrive si on s’en passe
Section intitulée « 🐝 Ce qui arrive si on s’en passe »Sans ce durcissement, l’exécution de code sous Windows redevient opaque. Un script obfuscé s’exécute sans qu’aucun journal ne conserve son contenu réel : l’analyse post-incident ne dispose d’aucune trace du code ni de ses sorties, et ne peut pas établir ce qui a été lu, énuméré ou exfiltré. PowerShell v2 laissé en place offre un interpréteur aveugle accessible par une simple option de ligne de commande, annulant toute journalisation posée sur le moteur moderne. Sans politique d’application, le mode contraint ne s’impose jamais, et le chargement d’outils en mémoire reste libre. Sans partage de transcription verrouillé, les fichiers produits restent lisibles par le compte qui les génère, c’est-à-dire potentiellement par l’intrus avant les analystes. Il reste un interpréteur puissant, utilisé, et muet.
- Securing PowerShell in the Enterprise, Australian Cyber Security Centre (ACSC).
- MITRE ATT&CK, technique T1059.001 (Command and Scripting Interpreter : PowerShell), MITRE.
- about_Logging_Windows, documentation de référence PowerShell, Microsoft Learn.
- Antimalware Scan Interface (AMSI), Microsoft Learn.
- Windows Defender Application Control (App Control for Business), Microsoft Learn.
🦫 Explication de la marmotte 🍫
Section intitulée « 🦫 Explication de la marmotte 🍫 »PowerShell est le programme avec lequel on pilote un ordinateur Windows en tapant des commandes. Comme il est très puissant, ceux qui attaquent s’en servent aussi. Plutôt que de l’interdire, on l’oblige à tout noter et on le bride là où il n’a rien à faire.
Première chose : enlever la vieille version numéro 2, parce qu’elle sait tout faire sans rien noter. Tant qu’elle existe, on peut la rappeler d’un mot et contourner la surveillance. Ensuite, on met en place une règle qui n’autorise que les programmes approuvés. Cette règle force automatiquement un mode réduit où le code non reconnu ne peut plus appeler les fonctions profondes du système.
On active alors l’enregistrement : chaque commande est écrite dans un journal au moment où l’ordinateur la comprend, même si elle était camouflée, car pour l’exécuter il doit d’abord la décoder. Une autre fonction, l’analyse à la volée, montre ce code à l’antivirus avant de le lancer et peut le bloquer. Enfin, on enregistre dans un fichier ce qui a été tapé et ce qui s’est affiché, en rangeant ce fichier là où l’attaquant ne peut pas le relire.
Si une de ces couches est désactivée, l’action de la désactiver laisse elle-même une trace. Rien n’arrête tout seul quelqu’un de déterminé, mais chaque couche qui tombe en révèle une autre. Tout cela n’a de sens que si les journaux partent ailleurs : laissés sur la machine, ils s’effacent.
À lire aussi
- Configurer une détection open source avec WazuhGuide pratique pour centraliser les journaux, installer Wazuh et détecter les premières tentatives d’accès suspectes sans SIEM commercial.
- TA0002 Exécution : faire tourner son code chez quelqu'un d'autreLa tactique ATT&CK TA0002 : exécuter du code contrôlé par l'attaquant sur un système. Vingt-deux techniques, des interpréteurs aux injections de prompt ajoutées en v20, et ce qui se détecte vraiment.
- Comment formuler une hypothèse de threat huntingConstruire, exécuter et industrialiser une hypothèse de threat hunting à partir du renseignement sur la menace et des données déjà collectées.
- EDR : fonctionnement, angles morts et contournementsCe que fait un EDR (Endpoint Detection and Response) sur les terminaux, ce qu'il ne voit pas, comment les attaquants le neutralisent et comment l'exploiter sans en faire un risque de plus.
Pages voisines
La citent
- Prouver qu'un programme a tourné : AmCache, ShimCache et leurs pièges
- TA0002 Exécution : faire tourner son code chez quelqu'un d'autre
Journaliser et durcir PowerShell sur un parc Windows
Elle cite
