Renforcer la sécurité de PowerShell — journalisation, AMSI, mode de langage, JEA
· Go Komura · PowerShell, Sécurité, Windows, Journalisation, Audit, Systèmes d'information, Gestion des privilèges, Amélioration opérationnelle
Face à une succession de rapports indiquant que « PowerShell a été détourné lors d’une intrusion », certaines organisations cherchent à interdire totalement l’usage de PowerShell en interne. Mais ce choix ne se justifie ni sur le plan de l’efficacité réelle, ni sur celui de l’impact opérationnel. PowerShell constitue l’infrastructure d’administration de Windows elle-même : l’arrêter, c’est arrêter l’automatisation de l’exploitation. Un attaquant, de son côté, peut accomplir la même chose par un autre moyen.
La politique réaliste n’est pas l’interdiction, mais la visibilité et la restriction. Heureusement, à partir de PowerShell 5.0, les fonctionnalités nécessaires à la défense sont disponibles : la journalisation des blocs de script, qui consigne même le code obfusqué sous sa forme développée ; AMSI (Antimalware Scan Interface, le mécanisme Windows qui fait examiner le contenu d’un script par un produit antimalware), qui transmet le contenu à un produit antivirus avant l’exécution ; le mode de langage, qui restreint la syntaxe elle-même utilisable à l’exécution ; et JEA, qui « ne délègue que les opérations nécessaires ». En combinant ces éléments, on peut créer un état auditable sans arrêter l’activité.
Cet article rassemble, dans l’ordre d’efficacité, les points à configurer pour continuer à utiliser PowerShell en toute sécurité dans un environnement Windows interne. La stratégie d’exécution et la signature sont traitées dans « Stratégie d’exécution et signature de script PowerShell » ; cet article va au-delà.
Environnement ciblé et prérequis
| Élément | Contenu |
|---|---|
| Système ciblé | Windows 10/11, Windows Server. AMSI suppose Windows 10 ou ultérieur1 |
| Version ciblée | Cet article couvre à la fois Windows PowerShell 5.1 et PowerShell 7. Les clés de stratégie et les emplacements de journalisation diffèrent entre 5.1 et 7 : sur un poste où les deux sont installés, configurez les deux (chapitres 3 et 4) |
| Privilèges nécessaires | La configuration des journaux aux chapitres 3 et 4, la désactivation de PowerShell 2.0, ainsi que l’enregistrement du point de terminaison JEA au chapitre 7 nécessitent tous des privilèges d’administrateur. En exploitation réelle, on distribue cela par stratégie de groupe plutôt que de configurer chaque poste à la main |
| Local / distant | Les chapitres 3 à 6 se déroulent entièrement sur le poste ou le serveur ciblé. Seul JEA, au chapitre 7, suppose que PowerShell Remoting (WinRM) soit activé (voir le début du chapitre 7) |
| Ce qui n’est pas couvert | Les détails de la stratégie d’exécution et de la signature sont traités dans un autre article (Stratégie d’exécution et signature de script PowerShell). Cet article n’en clarifie que le positionnement (chapitre 5) |
1. La conclusion, d’abord
- La priorité absolue est d’activer la journalisation des blocs de script. Le code exécuté est consigné dans le journal des événements, et même le code obfusqué reste enregistré sous sa forme développée (ID d’événement 4104).2
- Activez également la transcription en parallèle. L’enregistrement de la session, entrées et sorties comprises, peut être centralisé sur un partage en écriture seule.2
- Une fois la journalisation activée, révisez aussi la taille des journaux. Avec la taille par défaut, ils sont écrasés en peu de temps.2
- Grâce à AMSI, le script est transmis au produit antivirus avant son exécution. Actif à partir de PowerShell 5.0, sur Windows 10 ou ultérieur.1
- Désactivez l’ancien moteur PowerShell 2.0. S’il subsiste, il reste possible de basculer vers cet ancien moteur, où ni la journalisation ni AMSI ne s’appliquent.3
- La stratégie d’exécution n’est pas une frontière de sécurité. La documentation officielle le précise explicitement. N’en faites pas l’axe principal de votre défense.4
- Utilisez le mode de langage comme résultat de WDAC/AppLocker. Une configuration manuelle peut être contournée et ne fonctionne pas comme une véritable fonctionnalité de sécurité.5
- Pour la délégation de privilèges, utilisez JEA. Il permet d’autoriser « uniquement cette commande, avec ce paramètre », et de faire fonctionner l’exploitation sans jamais distribuer de privilèges d’administrateur.6
2. Que protège-t-on — la visibilité d’abord, la restriction ensuite
Les quatre mesures traitées dans cet article couvrent chacune une couche différente. Voici d’abord la vue d’ensemble.
flowchart TB
U["Administrateurs, support technique, scripts d'automatisation<br/>(et un attaquant ayant pénétré le système)"]
DEL["[Délégation] JEA (chapitre 7)<br/>Ne pas distribuer de privilèges d'administrateur, ne transmettre que les opérations autorisées"]
LIM["[Restriction] Mode de langage + WDAC/AppLocker (chapitre 6)<br/>Restreindre le code exécutable et la syntaxe utilisable"]
SCAN["[Inspection] AMSI (chapitre 4)<br/>Transmettre le contenu au produit antivirus juste avant l'exécution<br/>Fermer les voies de contournement en désactivant PowerShell 2.0"]
RUN["Opérations dans PowerShell"]
REC["[Enregistrement] Journalisation des blocs de script (4104), transcription (chapitre 3)<br/>Conserver la trace une fois l'obfuscation levée"]
U --> DEL
DEL --> LIM
LIM --> SCAN
SCAN --> RUN
RUN --> REC
L’enregistrement crée un état où l’on peut « remonter la trace après coup », la délégation réduit le privilège lui-même, et l’inspection comme la restriction arrêtent les choses avant l’exécution. Ces quatre mesures ne se substituent pas les unes aux autres : elles couvrent des couches différentes. Cela dit, il existe un ordre de priorité pour leur mise en œuvre.
| Étape | Action | Effet |
|---|---|---|
| 1. Visibilité | Journalisation des blocs de script, transcription | On sait ce qui s’est passé. L’investigation a posteriori devient possible |
| 2. Assainissement de base | Désactivation de PowerShell 2.0, maintien de la version à jour, vérification d’AMSI | Ferme les voies de contournement de l’inspection |
| 3. Limitation des privilèges | Délégation via JEA, réduction des privilèges d’administrateur | Limite l’étendue des dégâts |
| 4. Restriction de l’exécution | WDAC/AppLocker + mode de langage contraint | Arrête l’exécution même du code non approuvé |
Dans de nombreux environnements, la situation s’améliore déjà considérablement rien qu’en réalisant les étapes 1 et 3. L’étape 4 a un coût de mise en œuvre élevé ; c’est un domaine à faire progresser en vérifiant son impact opérationnel au fur et à mesure.
3. Activer la journalisation — 4104 est la priorité absolue
La journalisation de PowerShell comporte trois niveaux.2
| Type | Contenu enregistré | ID d’événement |
|---|---|---|
| Journal des modules | Détail de l’exécution du pipeline pour les modules spécifiés | 4103 |
| Journalisation des blocs de script | Le texte du code exécuté (après levée de l’obfuscation) | 4104 |
| Emplacement du journal | Microsoft-Windows-PowerShell/Operational pour 5.1, PowerShellCore/Operational pour 7 |
─ |
| Transcription | Enregistre les entrées et sorties de la session dans un fichier texte | ─ (sortie fichier) |
Ces trois éléments se configurent tous via la stratégie de groupe, sous « Configuration ordinateur > Modèles d’administration > Composants Windows > Windows PowerShell ». Il est également possible de les configurer directement dans le registre.2
# Active la journalisation des blocs de script (nécessite des privilèges d'administrateur ; généralement distribuée via GPO)
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
# -Type est un paramètre dynamique ajouté par le fournisseur de registre. Il permet de préciser le type de la valeur (DWord)
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
# PowerShell 7 (pwsh) lit une clé de stratégie différente. Configurez les deux
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\ScriptBlockLogging'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
# Active la transcription et la centralise sur un partage en écriture seule
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting' -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory' -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader' -Value 1 -Type DWord
# Pour la transcription aussi, PowerShell 7 utilise une clé différente. Écrivez la même valeur
$key = 'HKLM:\SOFTWARE\Policies\Microsoft\PowerShellCore\Transcription'
New-Item -Path $key -Force | Out-Null
Set-ItemProperty -Path $key -Name 'EnableTranscripting' -Value 1 -Type DWord
Set-ItemProperty -Path $key -Name 'OutputDirectory' -Value '\\logsrv\pstranscripts$'
Set-ItemProperty -Path $key -Name 'EnableInvocationHeader' -Value 1 -Type DWord
PowerShell 7 lit les stratégies sous PowerShellCore, et non sous Windows\PowerShell. Si vous vous contentez de configurer uniquement le côté 5.1, l’enregistrement des scripts exécutés sous pwsh manquera intégralement. Pour la journalisation des blocs de script comme pour la transcription, écrivez la même valeur dans les deux clés (si vous distribuez via GPO, configurez également chacun des modèles correspondants).
La valeur de la journalisation des blocs de script tient à sa résistance à l’obfuscation. Même une commande encodée en Base64 ou un code assemblé par concaténation de chaînes est enregistré sous sa forme développée au moment de l’exécution.2 La possibilité de reconstituer « ce qui a été exécuté » lors de l’investigation d’une attaque dépend entièrement de la présence de ce réglage.
La vérification de l’enregistrement se fait avec Get-WinEvent (« Explorer efficacement le journal des événements avec Get-WinEvent »).
# Vérifie la journalisation des blocs de script du dernier jour.
# Comme Windows PowerShell (5.1) et PowerShell 7 écrivent dans des journaux différents,
# on cible les deux
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational'
ID = 4104
StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, LogName, @{ n='Script'; e={ $_.Message } } -First 20
Pour vérifier que l’activation a réussi, procédez selon ces trois points.
- Après avoir appliqué le réglage, ouvrez une nouvelle session PowerShell. La journalisation des blocs de script ne s’applique qu’aux sessions démarrées après son activation.2 Ne concluez pas à un échec en testant dans une fenêtre déjà ouverte.
- Exécutez une commande repère dans la nouvelle session, et vérifiez qu’elle revient bien comme un événement 4104. Si la requête ci-dessus renvoie un événement contenant le fragment de code que vous venez d’exécuter, c’est actif. Si rien ne revient, vérifiez avec
Get-ItemPropertyla valeur de la clé de stratégie (EnableScriptBlockLoggingà1) et laquelle des clés, 5.1 ou 7, a reçu l’écriture. - Vérifiez que vous ne vous trompez pas de journal. Si l’exécution s’est faite sous
pwsh, l’emplacement estPowerShellCore/Operational. Le malentendu le plus fréquent avec ce réglage est de ne regarder queMicrosoft-Windows-PowerShell/Operationalet de conclure à tort que « rien n’est enregistré ».
Une fois activée, révisez impérativement la taille des journaux. Avec la taille par défaut, ils tournent en quelques heures à quelques jours, et il ne reste plus rien au moment où l’on en a besoin.
Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
Select-Object LogName, MaximumSizeInBytes, RecordCount
wevtutil sl Microsoft-Windows-PowerShell/Operational /ms:1073741824 # exemple d'extension à 1 Go
wevtutil sl PowerShellCore/Operational /ms:1073741824 # ne pas oublier le côté PowerShell 7
# Vérifie que le changement a été appliqué : succès si MaximumSizeInBytes vaut 1073741824
Get-WinEvent -ListLog 'Microsoft-Windows-PowerShell/Operational', 'PowerShellCore/Operational' |
Select-Object LogName, MaximumSizeInBytes
wevtutil sl n’affiche rien même en cas de succès : comparer MaximumSizeInBytes avant et après le réglage est donc le seul moyen de vérification. Le changement est refusé si vous n’exécutez pas avec des privilèges d’administrateur ; si la valeur ne change pas, suspectez d’abord ce point.
Faire de la destination d’agrégation des transcriptions un partage « où l’on peut écrire mais pas supprimer »
Le point essentiel est que la destination de sortie des transcriptions soit un partage où l’utilisateur peut écrire mais ne peut pas supprimer. Si vous les placez en local, elles seront effacées sur un poste compromis.
Configurez à la fois les autorisations de partage et les autorisations NTFS (le privilège effectif est le plus restrictif des deux). Le point essentiel est de n’accorder à l’utilisateur que « la création de fichiers dans le dossier » et « la lecture/écriture des fichiers qu’il a lui-même créés », et de ne jamais lui accorder la suppression (DE) ni la suppression de sous-dossiers et de fichiers (DC).
Clarifions d’emblée ce que cette configuration protège et ce qu’elle ne protège pas. Ce qu’elle protège, ce sont deux points : « ne toucher en aucun cas aux enregistrements d’autrui » et « ne pas pouvoir non plus supprimer ses propres enregistrements ». Ce qu’elle ne protège pas, c’est « l’écrasement de son propre enregistrement », ce qui ne peut pas être bloqué par la seule ACL (la raison est expliquée plus loin). Malgré cela, le simple fait qu’un compte compromis ne puisse plus effacer les traces d’autrui change considérablement la manière de circonscrire les dégâts. Si un poste tombe, les enregistrements des autres postes subsistent.
# À exécuter du côté du serveur d'agrégation des journaux (nécessite des privilèges d'administrateur)
$path = 'D:\PSTranscripts'
$null = New-Item -Path $path -ItemType Directory -Force
# Crée le partage. Les utilisateurs n'ont que la modification (écriture) ; seul le responsable des journaux a l'administration
New-SmbShare -Name 'pstranscripts$' -Path $path `
-ChangeAccess 'EXAMPLE\Domain Users' -FullAccess 'EXAMPLE\LogAdmins'
# Coupe l'héritage depuis le dossier parent (second argument $false = ne pas conserver les ACE héritées).
# Si le contrôle total de CREATOR OWNER hérité par ce mécanisme subsiste,
# on se retrouve dans l'état « chacun peut supprimer les fichiers qu'il a créés », ce qui vide l'agrégation de son sens.
#
# Cependant, SetAccessRuleProtection ne retire que les « ACE héritées » ;
# les ACE attribuées directement à ce dossier restent en place. Le New-Item -Force ci-dessus
# réussit même sur un dossier existant, donc si une autorisation de modification a été
# accordée directement par le passé à Domain Users ou à Everyone, elle
# survivra même après l'exécution de toutes les lignes suivantes. L'utilisateur pourrait alors toujours écraser ou supprimer les transcriptions d'autrui.
# On retire donc d'abord toutes les attributions directes, avant de ne remettre en place que celles qui sont nécessaires
$acl = Get-Acl -Path $path
$acl.SetAccessRuleProtection($true, $false)
foreach ($ace in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($ace) }
# Une fois tout retiré, on ajoute le côté administration et SYSTEM dans le même $acl avant de l'appliquer en une seule fois.
# Si l'on fait « vider » puis « remettre en place » avec deux Set-Acl séparés, il existe un instant où personne n'a plus accès
foreach ($id in @('EXAMPLE\LogAdmins', 'SYSTEM')) {
$acl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
$id, 'FullControl', 'ContainerInherit, ObjectInherit', 'None', 'Allow'))
}
Set-Acl -Path $path -AclObject $acl
# N'autorise l'utilisateur qu'à la « création » dans le dossier. Le point essentiel est de ne pas ajouter (OI) :
# cette autorisation n'est pas héritée par les fichiers du dossier
# WD = création de fichier AD = création de dossier X = parcours du dossier RA = lecture des attributs
# ajouter (OI) reviendrait à accorder jusqu'à « l'écriture de données dans les fichiers existants »
icacls $path /grant 'EXAMPLE\Domain Users:(CI)(WD,AD,X,RA)'
# Permet à celui qui écrit de lire et écrire uniquement « les fichiers qu'il a lui-même créés ».
# CREATOR OWNER est remplacé par « le créateur » au moment de la création du fichier,
# donc cela ne s'applique pas aux fichiers d'autrui. On n'accorde pas DE (suppression)
# RD,WD,AD = lecture, écriture, ajout de données RA,WA = lecture/écriture des attributs
# REA,WEA = lecture/écriture des attributs étendus RC = lecture des autorisations
# S = synchronisation. Inclus dans GENERIC_READ/GENERIC_WRITE, donc impossible à retirer
# On serait tenté de se limiter à AD (ajout) seul, mais dans ce cas pas une seule ligne de transcription ne pourrait être écrite (voir plus loin)
icacls $path /grant 'CREATOR OWNER:(OI)(IO)(RD,WD,AD,REA,WEA,RA,WA,RC,S)'
# C'est le point essentiel. Les lignes ci-dessus seules ne suffisent pas.
# L'utilisateur qui a créé le fichier en devient le « propriétaire ».
# Comme Windows accorde implicitement au propriétaire READ_CONTROL et WRITE_DAC,
# même sans avoir attribué WD ni DE, le propriétaire peut réécrire lui-même le DACL
# et se réattribuer les privilèges d'écrasement et de suppression. Autrement dit, un compte compromis
# peut effacer ses propres traces. Lorsqu'une ACE OWNER RIGHTS est présente, le système
# ignore les READ_CONTROL / WRITE_DAC implicites accordés au propriétaire
icacls $path /grant 'OWNER RIGHTS:(OI)(IO)(RA,REA)'
icacls $path # vérifie le résultat de la configuration
Si vous réutilisez un dossier existant, purgez systématiquement les ACE accordées directement. SetAccessRuleProtection($true, $false) ne retire que les ACE héritées ; celles directement attachées au dossier restent en place.7 Comme New-Item -Force réussit même sur un dossier existant, réutiliser un dossier qui a un historique — par exemple « on avait temporairement accordé le privilège de modification à Domain Users » — laisse cette attribution directe survivre même après avoir restreint CREATOR OWNER par la suite, et l’utilisateur peut toujours écraser ou supprimer les transcriptions d’autrui. C’est pour cette raison que le code ci-dessus purge tout d’un coup avant de tout remettre en place. Ce n’est pas nécessaire si vous créez un dossier neuf, mais cela ne coûte rien de le faire quand même. Après application, vérifiez visuellement la sortie de icacls $path pour vous assurer qu’aucun sujet inattendu n’y figure.
C’est ici que la façon d’appliquer (OI) détermine la réussite ou l’échec de la mesure. Si vous accordez WD à l’utilisateur avec (OI), cette ACE est héritée par tous les fichiers du dossier, et il suffit de connaître le nom d’un fichier pour écraser ou tronquer la transcription d’autrui.7 Cela empêche d’atteindre l’objectif « l’enregistrement subsiste même après compromission du poste ou du compte ». Comme ci-dessus, séparez le privilège de création dans le dossier (non hérité) de la lecture/écriture des seuls fichiers qu’on a soi-même créés, via CREATOR OWNER.
Et ne supprimez surtout pas la ligne OWNER RIGHTS. L’utilisateur qui crée un fichier en devient le propriétaire. Windows accorde implicitement au propriétaire READ_CONTROL et WRITE_DAC :8 même sans avoir attribué DE dans une ACE, le propriétaire peut réécrire lui-même le DACL et se réattribuer le privilège de suppression. Tant que « on peut supprimer soi-même son propre enregistrement » reste possible, restreindre CREATOR OWNER ne sert à rien. En plaçant une ACE OWNER RIGHTS (S-1-3-4), le système ignore les READ_CONTROL / WRITE_DAC implicites accordés au propriétaire, et c’est seulement à partir de là que « on peut écrire mais pas supprimer » devient effectif.8
Pourquoi ne pas se limiter à AD (ajout) seul
On pourrait être tenté de penser : « puisqu’on ne veut pas d’écrasement, il suffit de donner AD seul à CREATOR OWNER ». Mais dans ce cas, pas une seule ligne de transcription ne sera écrite.
Sous Windows, « ne pouvoir qu’ajouter » ne se réalise que lorsque le rédacteur ouvre le fichier en spécifiant FILE_APPEND_DATA seul. La référence Microsoft définit FILE_APPEND_DATA ainsi : « (pour un fichier local, si ce drapeau est spécifié sans FILE_WRITE_DATA, les opérations d’écriture n’écrasent pas les données existantes) ».9 Autrement dit, l’ajout exclusif est une propriété de la façon dont on ouvre le fichier, et non quelque chose que l’on obtient simplement en plaçant AD dans une ACE.
Or, le point d’écriture de Start-Transcript ne s’ouvre pas en ajout exclusif. L’implémentation de PowerShell ouvre d’abord avec FileMode.OpenOrCreate + FileAccess.ReadWrite, et ne rouvre avec FileMode.Append + FileAccess.Write qu’en cas d’échec.10 Côté .NET, FileAccess.Read/Write sont respectivement transposés en GENERIC_READ/GENERIC_WRITE, et FileMode.Append est simplement remplacé en interne par FileMode.OpenOrCreate avant de se positionner en fin de fichier.11 Il n’existe aucun chemin qui demande FILE_APPEND_DATA seul.
FILE_GENERIC_WRITE comprend FILE_WRITE_DATA, FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, READ_CONTROL et SYNCHRONIZE ; FILE_GENERIC_READ comprend FILE_READ_DATA, FILE_READ_ATTRIBUTES, FILE_READ_EA, READ_CONTROL et SYNCHRONIZE.9 Le contrôle d’accès examine la totalité des droits demandés : ce CreateFile est donc refusé pour un utilisateur qui ne détient que AD,RA,REA. Même si le partage autorise la modification (Change), l’échec survient côté NTFS. En voulant protéger l’enregistrement, on finirait par l’arrêter lui-même.
C’est pourquoi l’ACL de cette section n’utilise pas AD seul, mais adopte la forme « autoriser la lecture/écriture, sans accorder DE (suppression), WDAC (changement de privilège) ni WO (changement de propriétaire) ». Le propriétaire peut écraser ou tronquer sa propre transcription. Acceptez que ce point ne puisse pas être bloqué par l’ACL seule.
Si vous voulez bloquer même l’écrasement, il ne reste qu’à écrire vers un emplacement que le rédacteur ne peut pas atteindre. Il existe trois options.
- Avec JEA, utilisez
TranscriptDirectorydu fichier de configuration de session (chapitre 7). Cette sortie n’est pas écrite par l’utilisateur connecté, mais par Local System ; la documentation Microsoft exige elle-même que « les utilisateurs standards n’aient aucun accès à ce dossier » et que l’accès soit « limité aux seuls administrateurs de sécurité chargés de l’audit ».12 L’utilisateur n’a alors même pas besoin de privilège, et toute la problématique de cette section disparaît - L’ingérer via le transfert d’événements Windows ou un SIEM. La journalisation des blocs de script (4104) apparaît dans le journal des événements, elle peut donc être transférée
- Mettre en place un processus de collecte maison qui ouvre le fichier en ajout exclusif. Si vous écrivez vous-même le code qui demande
FILE_APPEND_DATAseul à l’ouverture, alors leADcôté ACE prend enfin son sens
Notez enfin que ce que l’ACL protège se limite strictement à « vis-à-vis des utilisateurs qui écrivent sur ce partage ». L’administrateur du serveur de fichiers peut prendre possession des fichiers, et si le poste est totalement compromis, il est possible d’altérer les données avant même leur écriture vers le partage. Comprenez la conception de privilèges de cette section comme garantissant que « les utilisateurs d’un même partage ne peuvent pas s’effacer mutuellement », rien de plus.
La signification des symboles de privilège icacls (WD, AD, DE, DC, etc.) et des indicateurs d’héritage ((OI), (CI), (IO)) est répertoriée dans la référence des commandes Windows.7 Notez également que cette configuration doit impérativement être testée une première fois sur une machine de validation avant d’être déployée. Comme la session en cours d’exécution continue d’ajouter des données au même fichier de transcription, restreindre trop les privilèges peut faire échouer l’enregistrement lui-même. Le plus sûr est de vérifier concrètement trois points : « l’écriture fonctionne depuis le poste de l’utilisateur », « l’utilisateur ne peut ni lire ni modifier les fichiers existants d’autrui », et « l’utilisateur ne peut pas supprimer ses propres fichiers ».
4. Fermer les voies de contournement — PowerShell 2.0 et AMSI
Grâce à AMSI (Antimalware Scan Interface), à partir de PowerShell 5.0, le contenu du script est transmis au produit antivirus juste avant l’exécution, et examiné une fois l’obfuscation levée.1 Si vous utilisez un produit compatible, y compris Microsoft Defender, cela fonctionne sans configuration supplémentaire.
Le problème est que les anciens moteurs ne disposent pas de ce mécanisme. Si le moteur Windows PowerShell 2.0 reste activé, il subsiste la possibilité de basculer, via powershell.exe -Version 2, vers un environnement où ni la journalisation ni AMSI ne s’appliquent. Cette fonctionnalité est obsolète et sa désactivation est recommandée.3
# Vérifie l'état du moteur PowerShell 2.0 et le désactive (nécessite des privilèges d'administrateur ; un redémarrage peut être requis)
Get-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2* |
Select-Object FeatureName, State
Disable-WindowsOptionalFeature -Online -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart
Notez que si PowerShell 7 (pwsh) est déployé, les clés de configuration comme l’emplacement des journaux diffèrent de ceux de 5.1 (la stratégie se trouve sous PowerShellCore, le journal est PowerShellCore/Operational). Dans un environnement où les deux sont utilisés, appliquez la configuration, l’ajustement de taille des journaux et les requêtes d’investigation aux deux versions. Pour la coexistence des versions, voir « Différences entre Windows PowerShell 5.1 et PowerShell 7 ».
5. Ne pas se tromper sur la place de la stratégie d’exécution
Clarifions de nouveau ce point. La stratégie d’exécution n’est pas une frontière de sécurité. La documentation officielle précise explicitement qu’il s’agit d’une fonctionnalité de sécurité destinée à empêcher un utilisateur d’exécuter involontairement un script, et non d’un mécanisme qui bloque une action malveillante.4
Cela dit, elle n’est pas sans valeur. La signature apporte une autre valeur : pouvoir vérifier que le module distribué n’a pas été altéré (« Distribution et mise à jour interne des modules PowerShell »). Ce qui compte, c’est de bien comprendre son rôle avant de l’utiliser ; croire que « passer en AllSigned suffit à sécuriser » est la compréhension la plus dangereuse qui soit.
6. Le mode de langage — à utiliser en combinaison avec le contrôle applicatif
Une session PowerShell possède un mode de langage, qui restreint les éléments de langage utilisables.5
| Mode | Ce qui est utilisable | Positionnement en pratique |
|---|---|---|
| FullLanguage | Tous les éléments de langage (par défaut) | Session normale |
| ConstrainedLanguage | Toutes les applets de commande fonctionnent, et les boucles, embranchements conditionnels, expansions de chaîne et références de propriété restent utilisables. Mais les types .NET utilisables sont limités à une liste blanche, et Add-Type ne peut charger que des assemblys signés |
Mode qui bascule automatiquement sous WDAC/AppLocker. Les opérations interactives et les tâches d’administration courantes restent globalement possibles telles quelles |
| RestrictedLanguage | Les commandes peuvent s’exécuter, mais les blocs de script sont inutilisables. Seules les variables $PSCulture, $PSUICulture, $true, $false, $null sont disponibles, seuls les opérateurs de comparaison -eq, -gt, -lt sont autorisés, et l’affectation, la référence de propriété et l’appel de méthode sont impossibles |
Mode utilisé pour le chargement des manifestes de module (.psd1). Il n’est pas prévu pour un usage interactif par une personne |
| NoLanguage | Le langage de script lui-même est désactivé. Ni script ni variable ne sont utilisables ; seuls les appels d’applets de commande et de commandes natives sont possibles | Mode par défaut de la configuration de session JEA (RestrictedRemoteServer). Un mode conçu pour créer un guichet qui « n’exécute que des commandes prédéfinies » |
Les trois modes restrictifs ne constituent pas une gradation, mais correspondent à des usages différents. ConstrainedLanguage vise « un environnement où une personne écrit et utilise des scripts, en n’y bloquant que l’usage de types dangereux », tandis que NoLanguage vise « ne laisser personne écrire de script du tout » ; ce dernier ne peut être mis en œuvre que dans un guichet limité comme celui de JEA.5
Le mode actuel peut être vérifié ainsi.
$ExecutionContext.SessionState.LanguageMode
Ce qui compte, c’est la méthode de configuration. Le mode de langage contraint fonctionne comme un basculement automatique de PowerShell lorsqu’un contrôle applicatif de type liste blanche est configuré via WDAC (Windows Defender Application Control) ou AppLocker.5 Une configuration manuelle, par exemple via une variable d’environnement, se contourne facilement et ne fonctionne pas comme une véritable fonctionnalité de sécurité. Comprenez que restreindre uniquement le mode de langage sans mettre en place de contrôle applicatif demande de l’effort pour un effet quasi nul.
7. JEA — déléguer « uniquement les opérations nécessaires »
Ce qui détermine l’ampleur réelle des dégâts est, dans la plupart des cas, l’étendue des privilèges. Dans une situation où « le support technique dispose de privilèges d’administrateur » ou où « tous les responsables d’exploitation appartiennent au groupe Domain Admin », la compromission d’un seul poste devient la compromission de toute l’entreprise.
JEA (Just Enough Administration) est un mécanisme qui délègue uniquement des opérations spécifiques, sans jamais transmettre de privilèges d’administrateur.6 Il répond exactement à un besoin comme « confier au support technique uniquement le redémarrage d’un service applicatif ».
Ce chapitre repose sur un prérequis différent — JEA s’appuie sur le traitement à distance
Alors que les chapitres 3 à 6 se bouclent entièrement avec une configuration locale, JEA est construit directement sur le mécanisme du traitement à distance de PowerShell (WinRM).13 « Déléguer avec JEA » signifie enregistrer, sur le serveur ciblé, un point de connexion dédié (une configuration de session), et y faire connecter l’utilisateur. La possibilité de le reproduire dans votre organisation se joue d’abord sur ce point.
Trois éléments sont nécessaires.
| Prérequis | Contenu |
|---|---|
| Version de PowerShell | JEA est disponible à partir de PowerShell 5.013 |
| Activation du traitement à distance | Le traitement à distance de PowerShell doit être activé sur le serveur ciblé. Il l’est par défaut à partir de Windows Server 2012 ; sinon, exécutez Enable-PSRemoting dans un PowerShell disposant de privilèges d’administrateur13 |
| Emplacement | Le fichier de fonctionnalités de rôle (.psrc) se place dans le dossier RoleCapabilities d’un module sur le serveur ciblé, et la configuration de session (.pssc) s’enregistre sur le serveur ciblé. Il ne s’agit pas d’éléments à placer sur le poste de l’utilisateur6 |
Autrement dit, les étapes 1 à 3 ci-dessous sont un travail à effectuer sur le serveur du côté délégué, avec des privilèges d’administrateur. Sur le poste de l’utilisateur, il suffit de pouvoir se connecter à ce serveur. La configuration et le durcissement du traitement à distance lui-même sont rassemblés dans « Introduction à PowerShell Remoting (WinRM) ».
Étape 1 : définir les opérations autorisées dans le fichier de fonctionnalités de rôle (.psrc)
Le fichier de fonctionnalités de rôle doit obligatoirement se trouver dans le dossier RoleCapabilities d’un module PowerShell. Créer simplement un dossier ne suffit pas à le faire reconnaître comme un module, et il ne pourra pas être résolu par son nom depuis RoleDefinitions, décrit plus loin. Il faut donc d’abord créer le module conteneur (un dossier doté d’un manifeste).6
# Crée le module qui contiendra les fonctionnalités de rôle (un manifeste est nécessaire)
$moduleRoot = 'C:\Program Files\WindowsPowerShell\Modules\KsJea'
$null = New-Item -Path "$moduleRoot\RoleCapabilities" -ItemType Directory -Force
# Le dossier du module doit contenir au moins un fichier portant le même nom que le dossier
$null = New-Item -Path "$moduleRoot\KsJea.psm1" -ItemType File -Force
New-ModuleManifest -Path "$moduleRoot\KsJea.psd1" -RootModule 'KsJea.psm1'
# Crée le gabarit du fichier de fonctionnalités de rôle (le nom de fichier devient le nom du rôle).
# Comme le nom de rôle est résolu « par le nom seul » parmi tous les modules de PSModulePath,
# un nom générique comme 'HelpDesk' peut entrer en collision avec le .psrc d'un autre module.
# En cas de collision, rien ne garantit lequel sera choisi, ce qui peut accorder des privilèges non voulus.
# Utilisez un nom unique préfixé par celui de l'organisation
New-PSRoleCapabilityFile -Path "$moduleRoot\RoleCapabilities\KsHelpDesk.psrc"
# Vérifie que le module est bien visible
Get-Module -Name KsJea -ListAvailable
# KsHelpDesk.psrc (extrait) — déclare « quoi » et « jusqu'où » autoriser
@{
GUID = '....'
# Les commandes en lecture seule peuvent être exposées telles quelles
VisibleCmdlets = @(
'Get-Service',
'Get-EventLog'
)
# Restart-Service, qui modifie l'état, n'est volontairement pas inclus dans VisibleCmdlets (voir plus loin)
# VisibleFunctions ne fait que restreindre les « fonctions chargées dans la session »,
# il ne définit pas les fonctions. Écrivez le contenu des fonctions personnalisées dans FunctionDefinitions,
# puis mentionnez également leur nom dans VisibleFunctions (les deux sont nécessaires)
VisibleFunctions = @('Get-KsAppStatus', 'Restart-KsAppService')
FunctionDefinitions = @(
@{
Name = 'Get-KsAppStatus'
ScriptBlock = {
# Le corps de la fonction s'exécute dans le mode de langage par défaut et n'est donc pas soumis aux contraintes de JEA.
# Ne transmettez jamais l'entrée de l'utilisateur telle quelle à une commande dangereuse
Get-Service -Name 'KsAppService' |
Microsoft.PowerShell.Utility\Select-Object Name, Status, StartType
}
},
@{
# Le redémarrage est exposé comme une fonction qui « ne reçoit sa cible que par argument »
Name = 'Restart-KsAppService'
ScriptBlock = {
param(
[Parameter(Mandatory)]
[ValidateSet('KsAppService', 'Spooler')]
[string] $Name
)
Microsoft.PowerShell.Management\Restart-Service -Name $Name
}
}
)
VisibleExternalCommands = @()
}
ValidateSet seul dans VisibleCmdlets ne suffit pas à restreindre en toute sécurité une commande qui change l’état du système. La restriction par Parameters et ValidateSet n’est évaluée que lorsque ce paramètre est effectivement lié. Comme Restart-Service peut recevoir un ServiceController par le pipeline,
Get-Service WinRM | Restart-Service
écrit ainsi, -Name n’est jamais lié, et ValidateSet est simplement contourné. Sur un point de terminaison censé n’autoriser que le redémarrage de KsAppService et Spooler, il devient possible de redémarrer n’importe quel service.
Le même phénomène se produit avec un autre jeu de paramètres. Même en ajoutant un ValidateSet sur LogName pour Get-WinEvent,
Get-WinEvent -ProviderName Microsoft-Windows-Security-Auditing -MaxEvents 1
écrit ainsi, LogName n’est pas lié et la restriction ne s’applique pas. Comme la session JEA s’exécute en tant qu’administrateur virtuel, il devient possible dans ce cas de lire jusqu’au journal Security.
C’est pourquoi l’exemple ci-dessus n’inclut pas Restart-Service dans VisibleCmdlets, et ne publie que la fonction wrapper qui ne reçoit sa cible que par argument (Restart-KsAppService). Si le seul chemin d’entrée est l’argument, il n’y a aucun moyen de le contourner.
La restriction par Parameters et ValidateSet ne fonctionne que « quand ce paramètre est lié ». Pour une commande dont la liaison peut être évitée via l’entrée du pipeline ou un autre jeu de paramètres, la restriction elle-même devient inopérante. Considérez que, en règle générale, une commande dont vous voulez restreindre les arguments doit être enveloppée dans une fonction wrapper.
Il y a ici deux pièges de conception faciles à rencontrer. Le premier est que VisibleFunctions ne crée aucune fonction. Une fonction dont seul le nom est mentionné n’existe pas dans la session, et l’utilisateur voit « cette commande n’existe pas ». Définissez vos fonctions personnalisées via FunctionDefinitions, puis mentionnez-les également dans VisibleFunctions.14 Si leur nombre augmente, il devient plus facile à gérer de les extraire dans un module de script et de publier les fonctions de ce module via VisibleFunctions.
Le second est que le corps de la fonction n’est pas soumis aux contraintes de JEA.14 Si vous voulez utiliser des commandes contraintes que JEA remplace, comme Select-Object, avec leur comportement d’origine, appelez-les avec leur nom complet, comme Microsoft.PowerShell.Utility\Select-Object ci-dessus. Autrement dit, tout est possible à l’intérieur d’une fonction : évitez absolument d’écrire un code qui transmettrait une entrée utilisateur à Invoke-Expression.
Étape 2 : définir, dans le fichier de configuration de session (.pssc), qui se voit attribuer quel rôle
# SessionType : serveur distant restreint (NoLanguage par défaut)
# RunAsVirtualAccount : s'exécute en tant que compte administrateur virtuel
# TranscriptDirectory : enregistre le contenu exécuté
$pssc = @{
Path = '.\KsHelpDesk.pssc'
SessionType = 'RestrictedRemoteServer'
RunAsVirtualAccount = $true
TranscriptDirectory = 'C:\JeaTranscripts'
RoleDefinitions = @{ 'EXAMPLE\HelpDesk' = @{ RoleCapabilities = 'KsHelpDesk' } }
}
New-PSSessionConfigurationFile @pssc
Étape 3 : enregistrer
Register-PSSessionConfiguration -Name 'KsHelpDesk' -Path .\KsHelpDesk.pssc -Force
Côté utilisateur, la connexion se fait ainsi.
Enter-PSSession -ComputerName 'appsrv01' -ConfigurationName 'KsHelpDesk'
# Seules les commandes autorisées sont utilisables. Restart-Service aussi se limite aux services indiqués
JEA repose sur trois points essentiels.6
- L’utilisateur ne détient pas de privilège d’administrateur. L’exécution se fait du côté du compte virtuel
- La session est configurée comme un serveur distant restreint. Le mode de langage y est restreint par défaut, empêchant l’exécution de code arbitraire
- Le contenu exécuté est enregistré sous forme de transcription. On peut auditer qui a fait quoi
Notez que, comme indiqué précédemment, le fichier de fonctionnalités de rôle doit se trouver sous le dossier RoleCapabilities d’un module, et suppose que ce module soit trouvable depuis $env:PSModulePath. Si le nom de rôle indiqué dans RoleDefinitions ne peut pas être résolu, vérifiez d’abord avec Get-Module -ListAvailable que le module est visible.6
Le point de terminaison enregistré peut être listé avec Get-PSSessionConfiguration. Comme indiqué au début, JEA suppose une configuration d’exécution à distance ; en cas d’échec de connexion, commencez le diagnostic du côté WinRM plutôt que du côté de la définition JEA (« Introduction à PowerShell Remoting (WinRM) »).
8. Liste de contrôle pratique
| Élément | Priorité | État |
|---|---|---|
| La journalisation des blocs de script (4104) a été activée | Haute | Distribuée à toute l’entreprise via GPO215 |
| La taille des journaux liés à PowerShell a été augmentée | Haute | Avec la taille par défaut, ils disparaissent en quelques jours |
| La transcription a été activée et centralisée sur un partage en écriture seule | Haute | Ne pas la placer en local sur le poste2 |
| Le moteur PowerShell 2.0 a été désactivé | Haute | Voie de contournement de la journalisation et d’AMSI3 |
| Le produit antivirus est compatible avec AMSI | Haute | Actif par défaut1 |
| La stratégie d’exécution n’est pas comprise à tort comme une frontière de sécurité | Moyenne | La signature a une autre valeur4 |
| La portée des privilèges d’administrateur accordés a été auditée | Haute | C’est l’étendue des privilèges qui détermine l’ampleur des dégâts |
| Les tâches routinières ont été déléguées via JEA | Moyenne | Se traduit directement par une réduction des privilèges d’administrateur6 |
| WDAC/AppLocker a été envisagé | Moyenne | La restriction du mode de langage va de pair avec cela5 |
| La configuration des journaux a aussi été distribuée côté PowerShell 7 | Moyenne | La configuration diffère entre 5.1 et 7 |
9. Conclusion
- Interdire PowerShell a une efficacité réelle faible et arrête l’activité. La politique à adopter est « la visibilité et la restriction ».
- La priorité absolue est l’activation de la journalisation des blocs de script (4104). Même le code obfusqué est enregistré sous sa forme développée. Réalisez-la en même temps que l’augmentation de la taille des journaux.
- Pour la transcription, le point essentiel est de la centraliser sur un partage que l’utilisateur ne peut pas supprimer.
- Désactivez le moteur PowerShell 2.0, afin de ne laisser subsister aucune voie où ni la journalisation ni AMSI ne s’appliquent.
- La stratégie d’exécution n’est pas une frontière de sécurité. La signature a une valeur en tant que détection d’altération, mais n’en faites pas l’axe principal de votre défense.
- L’étendue des privilèges détermine l’étendue des dégâts. En déléguant « uniquement les opérations nécessaires » via JEA, l’exploitation peut fonctionner sans jamais distribuer de privilèges d’administrateur.
Téléchargement du code d’exemple
Le code présenté dans cet article est distribué sous une forme directement exécutable. Le paquet contient l’activation de la configuration des journaux et la création d’un point de terminaison JEA.
Télécharger le code d’exemple (zip)
Les exemples de cet article dépendant de Windows et du tenant, ils n’ont pas fait l’objet d’une validation d’exécution. L’analyse syntaxique et l’analyse statique via PSScriptAnalyzer ont été réalisées pour tous les fichiers, mais vérifiez impérativement leur comportement sur votre propre machine de validation.
# Analyse syntaxique + analyse statique (exécutable même hors Windows)
./Invoke-SampleTests.ps1
Les valeurs de configuration (chemins, noms de serveur, identifiants de locataire, etc.) sont des exemples. Ne les exécutez pas telles quelles en production : adaptez-les à votre propre environnement.
Articles connexes
- Stratégie d’exécution et signature de script PowerShell — sortir de l’exploitation qui « couvre tout avec Bypass »
- Explorer efficacement le journal des événements avec Get-WinEvent — la vitesse du filtrage détermine le temps d’investigation
- Introduction à PowerShell Remoting (WinRM) — administrer plusieurs postes Windows d’un seul coup
- La gestion sécurisée des identifiants sous PowerShell — bannir les mots de passe en clair des scripts
- Liste de contrôle minimale de sécurité pour une application Windows
- Lire le « Top 10 des menaces de sécurité de l’information 2026 » de l’IPA du point de vue des systèmes métier
Domaines de conseil associés
合同会社小村ソフト (Komura Software LLC) prend en charge la revue de configuration de sécurité des environnements d’exploitation Windows, la conception opérationnelle incluant la délégation de privilèges (JEA), ainsi que le conseil sur la mise en place et l’exploitation des journaux d’audit.
- Conseil technique et revue de conception
- Investigation des incidents et analyse des causes
- Modification et maintenance de logiciels Windows existants
- Contact
Références
-
Microsoft Learn, Antimalware Scan Interface (AMSI). Sur le fait qu’AMSI est un mécanisme qui transmet du contenu depuis des applications et des services vers un produit antimalware quelconque pour examen, que les moteurs de script Windows, y compris PowerShell, y sont intégrés, et qu’un script obfusqué peut être examiné sous son contenu au moment de l’exécution. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, about_Logging_Windows. Sur les trois types de fonctionnalités de journalisation (journal des modules, journalisation des blocs de script, transcription), leur activation via la stratégie de groupe ou le registre (sous HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell), le fait que la journalisation des blocs de script enregistre, sous l’ID d’événement 4104, le code après levée de l’obfuscation, que le journal des modules est enregistré sous l’ID d’événement 4103, ainsi que sur les réglages OutputDirectory et EnableInvocationHeader de la transcription. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Microsoft Learn, Dépréciation de Windows PowerShell 2.0. Sur le fait que le moteur Windows PowerShell 2.0 est déprécié et que sa désactivation est recommandée, et qu’il est fourni comme fonctionnalité optionnelle de Windows dont l’activation et la désactivation peuvent être basculées. ↩ ↩2 ↩3
-
Microsoft Learn, about_Execution_Policies. Sur le fait que la stratégie d’exécution n’est pas une frontière de sécurité mais une fonctionnalité de sécurité destinée à empêcher un utilisateur d’exécuter involontairement un script, et sur l’existence de plusieurs moyens de la contourner. ↩ ↩2 ↩3
-
Microsoft Learn, about_Language_Modes. Sur les éléments de langage disponibles dans chacun des modes FullLanguage, ConstrainedLanguage, RestrictedLanguage et NoLanguage, la vérification du mode actuel via $ExecutionContext.SessionState.LanguageMode, le fait que PowerShell fonctionne en mode de langage contraint dans un environnement où le contrôle applicatif WDAC ou AppLocker est actif, et le fait que la configuration manuelle du mode de langage n’est pas conçue pour fonctionner comme une fonctionnalité de sécurité. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Présentation de Just Enough Administration (JEA). Sur le fait que JEA est un mécanisme qui délègue uniquement des tâches d’administration spécifiques sans accorder de privilège d’administrateur, sur la restriction via VisibleCmdlets et via Parameters/ValidateSet dans le fichier de fonctionnalités de rôle (.psrc), sur RestrictedRemoteServer, RunAsVirtualAccount, TranscriptDirectory et RoleDefinitions du fichier de configuration de session (.pssc), sur l’enregistrement via Register-PSSessionConfiguration, et sur le fait que le mode de langage est restreint dans une session JEA. Pour la nécessité de placer le fichier de fonctionnalités de rôle dans le dossier RoleCapabilities d’un module PowerShell (le rendre détectable comme module, création via New-PSRoleCapabilityFile), voir JEA Role Capabilities. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, icacls. Sur le fait qu’il s’agit d’une commande d’affichage et de modification des listes de contrôle d’accès des fichiers et dossiers, sur l’attribution de privilèges via /grant et la suppression de privilèges déjà attribués via /remove:g, sur les symboles de privilège détaillés utilisables dans le masque de privilège (DE = suppression, DC = suppression de sous-dossiers et de fichiers, WD = écriture de données/création de fichier, AD = ajout de données/création de sous-dossier, WA = écriture d’attributs, WEA = écriture d’attributs étendus, RA = lecture d’attributs, X = exécution/parcours, F = accès complet, etc.), ainsi que sur les indicateurs d’héritage ((OI) = héritage d’objet, (CI) = héritage de conteneur). Pour la création du partage et la spécification des autorisations d’accès, voir New-SmbShare (-FullAccess / -ChangeAccess / -ReadAccess) ; pour la désactivation de l’héritage, voir ObjectSecurity.SetAccessRuleProtection (le premier argument protège l’héritage, le second indique si les ACE héritées doivent être conservées). Cette méthode ne concerne que le traitement des ACE héritées ; les ACE directement attribuées à l’objet en sont exclues. Pour retirer une attribution directe, utilisez ObjectSecurity.RemoveAccessRuleSpecific ou une méthode équivalente pour les retirer individuellement. ↩ ↩2 ↩3
-
Microsoft Learn, Groupes d’identités spéciales (Special Identity Groups). Sur le fait que
OWNER RIGHTS(S-1-3-4) est un groupe représentant le propriétaire actuel de l’objet, et que lorsqu’une ACE portant ce SID est appliquée à un objet, le système ignore lesREAD_CONTROLetWRITE_DACimplicites accordés au propriétaire. Inversement, en l’absence de cette ACE, le propriétaire peut réécrire implicitement le DACL. ↩ ↩2 -
Microsoft Learn, File Access Rights Constants. Sur le fait que
FILE_APPEND_DATAest défini comme « For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing data if this flag is specified without FILE_WRITE_DATA.) », que l’écriture en ajout exclusif ne se réalise que lorsqueFILE_APPEND_DATAest spécifié sansFILE_WRITE_DATAà l’ouverture, ainsi que sur la signification deFILE_WRITE_DATA/FILE_ADD_FILEet deFILE_DELETE_CHILD. Pour le mappage des droits d’accès génériques (FILE_GENERIC_WRITE=FILE_APPEND_DATA+FILE_WRITE_ATTRIBUTES+FILE_WRITE_DATA+FILE_WRITE_EA+STANDARD_RIGHTS_WRITE+SYNCHRONIZE,FILE_GENERIC_READ=FILE_READ_ATTRIBUTES+FILE_READ_DATA+FILE_READ_EA+STANDARD_RIGHTS_READ+SYNCHRONIZE), voir File Security and Access Rights. ↩ ↩2 -
PowerShell,
TranscriptionOption.FlushContentToDisk(src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs). Sur le fait que le point d’écriture de la transcription s’ouvre avecnew FileStream(this.Path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read), et n’est rouvert avecnew FileStream(this.Path, FileMode.Append, FileAccess.Write, FileShare.Read)qu’en cas d’échec, ainsi que sur l’absence de tout chemin demandantFILE_APPEND_DATAseul. ↩ -
.NET,
SafeFileHandle.Open(src/libraries/System.Private.CoreLib/src/Microsoft/Win32/SafeHandles/SafeFileHandle.Windows.cs). Sur le fait queFileAccess.Read/FileAccess.Writesont respectivement transposés enGENERIC_READ/GENERIC_WRITE, et queFileMode.Appendest simplement remplacé parFileMode.OpenOrCreateavant l’appel àCreateFile, sans queFILE_APPEND_DATAseul ne soit jamais utilisé dans le masque d’accès. ↩ -
Microsoft Learn, Configurations de session JEA. Sur le fait que spécifier un dossier dans
TranscriptDirectorydu fichier de configuration de session déclenche automatiquement l’enregistrement des transcriptions, et sur la citation « Transcripts are written to the folder by the Local System account, which requires read and write access to the directory. Standard users should have no access to the folder. Limit the number of security administrators that have access to audit the transcripts. » (les transcriptions sont écrites dans le dossier par le compte Local System, qui nécessite un accès en lecture et écriture au répertoire ; les utilisateurs standards ne devraient avoir aucun accès au dossier ; limiter le nombre d’administrateurs de sécurité ayant accès pour auditer les transcriptions). ↩ -
Microsoft Learn, Prérequis de JEA. Sur le fait que JEA est disponible à partir de PowerShell 5.0, que le traitement à distance de PowerShell constitue le socle de JEA et doit être activé et correctement protégé avant d’utiliser JEA, qu’il est activé par défaut à partir de Windows Server 2012, et que, dans le cas contraire, il peut être activé en exécutant Enable-PSRemoting dans une fenêtre PowerShell disposant de privilèges d’administrateur. ↩ ↩2 ↩3
-
Microsoft Learn, JEA Role Capabilities. Sur la nécessité de définir des fonctions personnalisées via FunctionDefinitions puis de les mentionner également dans VisibleFunctions (« Don’t forget to add the name of your custom functions to the VisibleFunctions field so they can be run by the JEA users. »), sur le fait que le corps de la fonction (le bloc de script) s’exécute dans le mode de langage par défaut du système et n’est pas soumis aux contraintes de JEA, sur la nécessité d’un nom complet (Microsoft.PowerShell.Utility\Select-Object) pour utiliser dans leur implémentation d’origine les commandes contraintes que JEA remplace, sur la méthode consistant à extraire les fonctions dans un module de script et à les publier via VisibleFunctions lorsqu’elles sont nombreuses, et sur la nécessité, dans le dossier du module, d’un fichier portant le même nom que le dossier. ↩ ↩2
-
Microsoft Learn, Set-ItemProperty. Sur le fait que, lors de l’utilisation du fournisseur de registre, -Type est ajouté comme paramètre dynamique permettant de spécifier le type de données de la valeur de registre (String / ExpandString / Binary / DWord / MultiString / QWord, etc.), y compris le fait que la valeur est créée si elle n’existe pas. ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Examiner les journaux d'événements en pratique avec Get-WinEvent — La rapidité du filtrage détermine le temps d'investigation
Ce guide présente comment rendre plus efficace l'investigation des journaux d'événements Windows avec PowerShell. Il explique pourquoi fi...
Politique d'audit de sécurité Windows et investigation des journaux d'événements — devenir capable de lire un 4625
Un guide pratique pour répondre à la demande « peux-tu vérifier les journaux d'échec de connexion ? ». Il couvre la relation entre la pol...
Guide pratique de Windows LAPS ── En finir avec le mot de passe administrateur local commun à tous les PC
Un mot de passe administrateur local commun à tous les PC est le terreau des attaques Pass-the-Hash, où la compromission d'une seule mach...
Guide pratique du magasin de certificats Windows — Faut-il l'installer côté utilisateur ou côté ordinateur ?
Faut-il placer un certificat client dans le magasin utilisateur ou dans le magasin ordinateur ? Ce guide pratique règle méthodiquement le...
Le pare-feu Windows et les applications métier — enregistrer les règles entrantes depuis l'installateur
« Ça fonctionne sur le poste de développement mais impossible de communiquer chez le client » a pour cause classique le pare-feu Windows....
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Faut-il interdire PowerShell en interne comme mesure de sécurité ?
- Ce n'est pas recommandé, car l'efficacité réelle est faible et les effets de bord sont importants. PowerShell constitue l'infrastructure d'administration de Windows elle-même ; il n'est en réalité qu'une fonctionnalité au-dessus de .NET. Bloquer l'exécutable n'empêche pas d'appeler la même API par un autre moyen, ce qui en fait rarement un obstacle réel pour un attaquant, alors que l'administration légitime et l'automatisation, elles, s'arrêtent à coup sûr. La politique réaliste n'est pas l'interdiction mais « la visibilité et la restriction ». Consignez ce qui a été exécuté avec la journalisation des blocs de script, désactivez les anciennes versions, et si nécessaire, réduisez le périmètre exécutable avec le mode de langage ou JEA.
- En activant la journalisation des blocs de script, ne va-t-on pas se retrouver submergé par le volume de journaux ?
- Le volume augmente effectivement, donc activez-la en même temps que vous ajustez la taille des journaux. Si vous laissez le journal Microsoft-Windows-PowerShell/Operational à sa taille maximale par défaut, il sera écrasé en peu de temps et ne conservera plus ce dont vous avez besoin au moment critique. En pratique, prévoyez une taille de journal suffisante et, si nécessaire, agrégez avec un SIEM ou le transfert d'événements. Notez qu'il existe aussi un réglage plus détaillé qui enregistre jusqu'au « début et à la fin de chaque appel », mais comme son volume de sortie est très important, on active habituellement uniquement la journalisation par défaut des blocs de script.
- Passer la stratégie d'exécution à AllSigned constitue-t-il une mesure de sécurité ?
- La stratégie d'exécution n'est pas une frontière de sécurité. La documentation officielle précise elle-même qu'il s'agit d'un mécanisme destiné à empêcher un utilisateur d'exécuter involontairement un script dangereux, et non à arrêter une action malveillante, car il existe plusieurs façons de le contourner. La signature a une autre valeur, celle de pouvoir vérifier l'intégrité de ce qui est distribué, mais l'axe principal de la défense doit reposer sur la visibilité par la journalisation, l'inspection par AMSI, et la restriction des privilèges par le mode de langage ou JEA.
- Le mode de langage contraint (ConstrainedLanguage) peut-il être configuré manuellement ?
- Le configurer manuellement, par exemple via une variable d'environnement, n'est pas recommandé, car le contournement est facile et cela ne fonctionne pas comme une véritable fonctionnalité de sécurité. Le mode de langage contraint est conçu, à l'origine, pour être un basculement automatique de PowerShell résultant de la mise en place d'un contrôle applicatif via WDAC (Windows Defender Application Control) ou AppLocker. Restreindre uniquement le mode de langage sans mettre en place de contrôle applicatif n'apporte pas de défense réellement efficace.
- Je voudrais confier au support technique uniquement le redémarrage d'un service précis sur un serveur.
- JEA (Just Enough Administration) est le mécanisme conçu pour cet usage. Vous définissez, dans un fichier de fonctionnalités de rôle, « cette commande, ce paramètre, uniquement cette valeur sont autorisés », puis vous l'enregistrez comme configuration de session. L'utilisateur peut alors exécuter uniquement les opérations autorisées via un compte virtuel, sans jamais détenir de privilèges d'administrateur. La session est configurée comme un serveur distant restreint, et par défaut le mode de langage y est également limité, ce qui ne laisse aucune marge pour exécuter du code arbitraire. Le contenu exécuté peut être consigné sous forme de transcription.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.