diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index ccbcbfe4..38ecb054 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -6,7 +6,7 @@ **改善:** -- Windows PowerShell 5.1 のモジュール/スクリプトブロック監査を明示的に選択する `powershell-logging` Audit/Plan/Configure を追加しました。モジュール名、型付き変更前記録、観測したポリシーの変化と再読取を確認し、呼出しログ・転写・Core設定・既存モジュール一覧を保持します。Server 2022/2025 の破棄可能なテストで正確な 4103/4104 と復元を要求し、PowerShell 7 のフォールバック、管理ポリシーの継続性、転送、Sigma 対応は別途検証します。(関連 #364, #366, #387) (@Shirofune-Security) +- Windows PowerShell 5.1 のモジュール/スクリプトブロック監査を明示的に選択する `powershell-logging` Audit/Plan/Configure を追加しました。モジュール名、事前の記録容量、型付き変更前記録、観測したポリシーの変化と再読取を確認し、呼出しログ・転写・Core設定・既存モジュール一覧を保持します。Server 2022/2025 の破棄可能なテストで正確な 4103/4104 と復元を要求し、PowerShell 7 のフォールバック、管理ポリシーの継続性、転送、Sigma 対応は別途検証します。(関連 #364, #366, #387) (@Shirofune-Security) - 既に有効なローカル保存先で、現在の実行ユーザーによる固定 Windows PowerShell 5.1 子プロセスの完了した自動トランスクリプトを検証する `transcript-probe` を追加しました。実トークン・ログオン、標準ヘッダー/フッター、nonce・PID・時刻、件数を制限したファイル識別と出典確認により不明な結果を保持し、ポリシーやACLの変更・明示的トランスクリプト開始・Sigma/EVTX加算は行いません。Server 2022/2025と両WELA実行エンジン向けに、使い捨て標準ユーザーの成功・拒否テストを追加しました。 (#436) (@Shirofune-Security) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7e18486..6757423a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ **Improvements:** -- Added explicit `powershell-logging` Audit/Plan/Configure for selected Windows PowerShell 5.1 module and script-block logging. Reviewed module names, typed original journals, full observed-policy drift guards and native readback preserve invocation/transcription/Core settings and other module entries. Disposable Server 2022/2025 tests require exact local 4103/4104 evidence and policy cleanup; PowerShell 7 fallback, managed-host persistence, forwarding and Sigma readiness remain separate. (Related #364, #366, #387) (@Shirofune-Security) +- Added explicit `powershell-logging` Audit/Plan/Configure for selected Windows PowerShell 5.1 module and script-block logging. Reviewed module names, preflight inventory-capacity checks, typed original journals, full observed-policy drift guards and native readback preserve invocation/transcription/Core settings and other module entries. Disposable Server 2022/2025 tests require exact local 4103/4104 evidence and policy cleanup; PowerShell 7 fallback, managed-host persistence, forwarding and Sigma readiness remain separate. (Related #364, #366, #387) (@Shirofune-Security) - Added opt-in `transcript-probe` to verify a fixed current-account Windows PowerShell 5.1 child produces its own completed automatic transcript in an already enabled local destination. Actual token/logon, native header/footer, nonce/PID/time, bounded file identity and source checks preserve unverified outcomes without changing policy or ACLs, invoking explicit transcription or granting Sigma/EVTX credit. Added disposable standard-writer success/denial tests for Server 2022/2025 under both WELA host engines. (#436) (@Shirofune-Security) diff --git a/docs/powershell-logging.md b/docs/powershell-logging.md index e81fe981..587b182b 100644 --- a/docs/powershell-logging.md +++ b/docs/powershell-logging.md @@ -39,7 +39,7 @@ An existing matching module entry is retained. The command does not delete or re Microsoft documents machine policy precedence over user policy, module pipeline logging, script-block logging, and the additional volume generated by invocation start/stop logging in [Windows PowerShell Group Policy settings](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_group_policy_settings?view=powershell-5.1). The [ADMX mapping](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-admx-powershellexecutionpolicy#enablemodulelogging) documents the ModuleLogging registry location. This command offers explicit source controls; it does not import a Microsoft/CIS/ASD baseline or claim complete compliance with one. -`SOFTWARE\Policies` is [shared across registry views](https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys). Both views are compared before a native-view write; no literal `Wow6432Node` policy tree is created. Snapshots include the Windows PowerShell machine and current-user policy trees, each bounded to 64 keys, eight levels, 128 values per key and one Mi character serialized data. Incomplete, denied, unstable or excessive trees are refused. Existing owner/group/DACL observations are recorded; these snapshots do not claim registry SACL enumeration or effective access for other principals. +`SOFTWARE\Policies` is [shared across registry views](https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys). Both views are compared before a native-view write; no literal `Wow6432Node` policy tree is created. Snapshots include the Windows PowerShell machine and current-user policy trees, each bounded to 64 keys, eight levels, 128 values per key and one Mi character serialized data. Incomplete, denied, unstable or excessive trees are refused. Preflight projects all selected additions and refuses predictable key/value-count or serialized-value growth beyond these limits before any journal or write. New keys' inherited descriptor sizes remain subject to native readback. Existing owner/group/DACL observations are recorded; these snapshots do not claim registry SACL enumeration or effective access for other principals. PowerShell 7 has [separate PowerShell Core settings and an optional Windows-policy fallback](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_group_policy_settings?view=powershell-7.5). Its registry policy trees are observed and preserved; `powershell.config.json`, session behavior and fallback selection are not assessed. **A PowerShell 7 deployment using that fallback may inherit changes to Windows PowerShell policy.** Running WELA under PowerShell 7 does not establish PowerShell 7 event coverage. Existing sessions are not restarted or asserted to adopt the changes. @@ -55,7 +55,7 @@ For manual recovery, compare the original journal, confirmed results and current ## Native validation and limits -The focused suite covers explicit selection, exact types, module-name collisions, preservation, journal-before-write, missing-key creation, idempotence, dry runs, refused output reuse, partial failures and approval-time drift. Public CLI tests reject unrelated/profile/role options before dispatch. +The focused suite covers explicit selection, exact types, module-name collisions, preservation, journal-before-write, missing-key creation, inventory-capacity boundaries, idempotence, dry runs, refused output reuse, partial failures and approval-time drift. Public CLI tests reject unrelated/profile/role options before dispatch. The opt-in disposable Server 2022/2025 matrix runs WELA under Windows PowerShell 5.1 and PowerShell 7. It saves native state, prepares selected disabled values, exercises the public Audit/Plan/DryRun/Configure path, checks original journals and idempotence, then launches a new fixed **native Windows PowerShell 5.1** utility command with a unique benign marker. Native Operational event XML must match the owned child PID, provider GUID/name, actual SID, computer, record boundary and measured process-lifetime interval; script-block evidence additionally matches the exact script path/text and complete fragment counts. Only the fixture prepares policy or generates events. Wrong-type refusal and exact typed policy/key, channel and all-59-mask cleanup are required. Native event artifacts must be reviewed before claiming a matrix run passed. diff --git a/scripts/PowerShellLogging.ps1 b/scripts/PowerShellLogging.ps1 index ff9cc361..9d76b441 100644 --- a/scripts/PowerShellLogging.ps1 +++ b/scripts/PowerShellLogging.ps1 @@ -85,12 +85,32 @@ function Get-WelaPsLoggingDefinitions { if ($Control -contains 'ScriptBlock') {[pscustomobject]@{Control='ScriptBlock';Path='ScriptBlockLogging';Name='EnableScriptBlockLogging';Type='DWord';Value=1}} } function Test-WelaPsLoggingValue {param($Snapshot,$Definition) $value=Get-WelaPsLoggingValue $Snapshot.Machine $Definition.Path $Definition.Name;return $null -ne $value -and $value.Type -ceq $Definition.Type -and (ConvertTo-WelaPsLoggingKey $value.Value) -ceq (ConvertTo-WelaPsLoggingKey $Definition.Value)} +function Assert-WelaPsLoggingCapacity { + param($Snapshot,[array]$Definitions) + # Project predictable inventory growth before writing; new inherited access descriptors + # still require native readback. Projection never mutates the captured original state. + $tree=ConvertTo-WelaPsLoggingKey $Snapshot.Machine|ConvertFrom-Json;$rows=@{} + foreach($row in $tree.Keys){$rows[$row.Path]=$row} + foreach($definition in $Definitions){ + $paths=@('');$path='';foreach($part in $definition.Path.Split('\')){$path=if($path){$path+'\'+$part}else{$part};$paths+=$path} + foreach($path in $paths){ + if(-not $rows.ContainsKey($path)){$rows[$path]=[pscustomobject]@{Path=$path;Values=@();Children=@();Access=''}} + if($path){$separator=$path.LastIndexOf('\');$parent=if($separator -ge 0){$path.Substring(0,$separator)}else{''};$leaf=if($separator -ge 0){$path.Substring($separator+1)}else{$path};$rows[$parent].Children=@(@($rows[$parent].Children)+$leaf|Sort-Object -Unique)} + } + $row=$rows[$definition.Path];$row.Values=@($row.Values|Where-Object Name -ine $definition.Name)+[pscustomobject]@{Name=$definition.Name;Type=$definition.Type;Value=$definition.Value} + if($row.Values.Count -gt 128){throw 'Selected changes exceed the 128-value policy inventory capacity; no write is safe.'} + } + if($rows.Count -gt 64){throw 'Selected changes exceed the 64-key policy inventory capacity; no write is safe.'} + $tree.Exists=($rows.Count -gt 0);$tree.Keys=@($rows.Values|Sort-Object Path) + if((ConvertTo-WelaPsLoggingKey $tree).Length -gt 1048576){throw 'Selected changes exceed the policy snapshot character capacity; no write is safe.'} +} function Assert-WelaPsLoggingKnown { param($Snapshot,[array]$Definitions) foreach ($definition in $Definitions) { $value=Get-WelaPsLoggingValue $Snapshot.Machine $definition.Path $definition.Name if ($value -and ($value.Type -cne $definition.Type -or ($definition.Type -eq 'DWord' -and $value.Value -notin @(0,1)) -or ($definition.Type -eq 'String' -and $value.Value -cne $definition.Value))) {throw "Selected policy value has an unknown type/value or a name collision: $($definition.Path)/$($definition.Name)."} } + Assert-WelaPsLoggingCapacity $Snapshot $Definitions if (@($Definitions|Where-Object Control -eq Module).Count) { foreach($key in @($Snapshot.Machine.Keys|Where-Object Path -ieq 'ModuleLogging\ModuleNames')) {foreach($value in $key.Values) {if($value.Type -cne 'String' -or [string]::IsNullOrWhiteSpace($value.Value)){throw 'Existing module-name policy contains an unsupported type/empty value; preserve and review it.'}}} } diff --git a/tests/PowerShellLogging.Tests.ps1 b/tests/PowerShellLogging.Tests.ps1 index 3907ea69..1fe04866 100644 --- a/tests/PowerShellLogging.Tests.ps1 +++ b/tests/PowerShellLogging.Tests.ps1 @@ -56,6 +56,25 @@ try { Reset;$badName=Get-WelaPsLoggingValue $script:observed.Machine 'ModuleLogging\ModuleNames' existing;$badName.Type='DWord';$badName.Value=1 $bad=Invoke-WelaPowerShellLogging -Action Configure -Control Module -ModuleName Microsoft.PowerShell.Utility -Auto -BackupPath (Join-Path $root 'badname') Assert ($bad.ExitCode -eq 1 -and -not (Test-Path (Join-Path $root 'badname'))) 'Unknown module value fails entire preflight' + foreach($countBefore in @(127,128)){ + Reset;$row=@($script:observed.Machine.Keys|Where-Object Path -eq 'ModuleLogging\ModuleNames')[0] + $row.Values=@(1..$countBefore|ForEach-Object{[pscustomobject]@{Name=('Existing'+$_);Type='String';Value=('Existing'+$_)}}) + $capacityPath=Join-Path $root ('value-capacity-'+$countBefore);$captured=ConvertTo-WelaPsLoggingKey $script:observed + $capacity=Invoke-WelaPowerShellLogging -Action Configure -Control Module -ModuleName NewModule -DryRun -BackupPath $capacityPath + Assert (($capacity.ExitCode -eq 0) -eq ($countBefore -eq 127)) '127 names permit one addition; 128 names refuse before the first write' + Assert ($script:writes -eq 0 -and -not (Test-Path $capacityPath) -and (ConvertTo-WelaPsLoggingKey $script:observed) -ceq $captured) 'Capacity preflight preserves the snapshot without writes or journals' + } + foreach($countBefore in @(63,64)){ + Reset;$script:observed.Machine.Keys=@((Row '' @() @('ModuleLogging')),(Row 'ModuleLogging' @([pscustomobject]@{Name='EnableModuleLogging';Type='DWord';Value=0}))) + foreach($index in 1..($countBefore-2)){$name='Existing'+$index;$script:observed.Machine.Keys+=Row $name;$script:observed.Machine.Keys[0].Children+=$name} + $capacity=Invoke-WelaPowerShellLogging -Action Plan -Control Module -ModuleName NewModule + Assert (($capacity.ExitCode -eq 0) -eq ($countBefore -eq 63)) '63 keys permit the missing selected key; 64 keys refuse predictable readback overflow' + } + Reset;$row=@($script:observed.Machine.Keys|Where-Object Path -eq Transcription)[0];$row.Values+=[pscustomobject]@{Name='LargeUnrelated';Type='String';Value=''} + $space=1048576-(ConvertTo-WelaPsLoggingKey $script:observed.Machine).Length;$row.Values[-1].Value='x'*$space + Assert ((ConvertTo-WelaPsLoggingKey $script:observed.Machine).Length -eq 1048576) 'Character-cap boundary fixture fits the current reader exactly' + $capacity=Invoke-WelaPowerShellLogging -Action Configure -Control Module -ModuleName NewModule -Auto -BackupPath (Join-Path $root 'character-capacity') + Assert ($capacity.ExitCode -eq 1 -and $script:writes -eq 0 -and -not (Test-Path (Join-Path $root 'character-capacity'))) 'Predictable serialized-value growth beyond character cap is refused before writes' Reset;$script:failWrite=$true;$failed=Invoke-WelaPowerShellLogging -Action Configure -Control Module,ScriptBlock -ModuleName Microsoft.PowerShell.Utility -Auto -BackupPath (Join-Path $root 'writefailure') Assert ($failed.ExitCode -eq 1 -and $script:writes -eq 1 -and @($failed.Results|Where-Object Status -eq Skipped).Count -eq 2) 'Native failure stops and explicitly reports later writes' Reset;$script:corrupt=$true;$failed=Invoke-WelaPowerShellLogging -Action Configure -Control Module,ScriptBlock -ModuleName Microsoft.PowerShell.Utility -Auto -BackupPath (Join-Path $root 'corrupt') diff --git a/website/docs/resources/changelog.ja.md b/website/docs/resources/changelog.ja.md index 66ce16d7..99e27073 100644 --- a/website/docs/resources/changelog.ja.md +++ b/website/docs/resources/changelog.ja.md @@ -9,7 +9,7 @@ **改善:** -- Windows PowerShell 5.1 のモジュール/スクリプトブロック監査を明示的に選択する `powershell-logging` Audit/Plan/Configure を追加しました。モジュール名、型付き変更前記録、観測したポリシーの変化と再読取を確認し、呼出しログ・転写・Core設定・既存モジュール一覧を保持します。Server 2022/2025 の破棄可能なテストで正確な 4103/4104 と復元を要求し、PowerShell 7 のフォールバック、管理ポリシーの継続性、転送、Sigma 対応は別途検証します。(関連 #364, #366, #387) (@Shirofune-Security) +- Windows PowerShell 5.1 のモジュール/スクリプトブロック監査を明示的に選択する `powershell-logging` Audit/Plan/Configure を追加しました。モジュール名、事前の記録容量、型付き変更前記録、観測したポリシーの変化と再読取を確認し、呼出しログ・転写・Core設定・既存モジュール一覧を保持します。Server 2022/2025 の破棄可能なテストで正確な 4103/4104 と復元を要求し、PowerShell 7 のフォールバック、管理ポリシーの継続性、転送、Sigma 対応は別途検証します。(関連 #364, #366, #387) (@Shirofune-Security) - 既に有効なローカル保存先で、現在の実行ユーザーによる固定 Windows PowerShell 5.1 子プロセスの完了した自動トランスクリプトを検証する `transcript-probe` を追加しました。実トークン・ログオン、標準ヘッダー/フッター、nonce・PID・時刻、件数を制限したファイル識別と出典確認により不明な結果を保持し、ポリシーやACLの変更・明示的トランスクリプト開始・Sigma/EVTX加算は行いません。Server 2022/2025と両WELA実行エンジン向けに、使い捨て標準ユーザーの成功・拒否テストを追加しました。 (#436) (@Shirofune-Security) diff --git a/website/docs/resources/changelog.md b/website/docs/resources/changelog.md index 7c3b626e..25b6dba4 100644 --- a/website/docs/resources/changelog.md +++ b/website/docs/resources/changelog.md @@ -9,7 +9,7 @@ **Improvements:** -- Added explicit `powershell-logging` Audit/Plan/Configure for selected Windows PowerShell 5.1 module and script-block logging. Reviewed module names, typed original journals, full observed-policy drift guards and native readback preserve invocation/transcription/Core settings and other module entries. Disposable Server 2022/2025 tests require exact local 4103/4104 evidence and policy cleanup; PowerShell 7 fallback, managed-host persistence, forwarding and Sigma readiness remain separate. (Related #364, #366, #387) (@Shirofune-Security) +- Added explicit `powershell-logging` Audit/Plan/Configure for selected Windows PowerShell 5.1 module and script-block logging. Reviewed module names, preflight inventory-capacity checks, typed original journals, full observed-policy drift guards and native readback preserve invocation/transcription/Core settings and other module entries. Disposable Server 2022/2025 tests require exact local 4103/4104 evidence and policy cleanup; PowerShell 7 fallback, managed-host persistence, forwarding and Sigma readiness remain separate. (Related #364, #366, #387) (@Shirofune-Security) - Added opt-in `transcript-probe` to verify a fixed current-account Windows PowerShell 5.1 child produces its own completed automatic transcript in an already enabled local destination. Actual token/logon, native header/footer, nonce/PID/time, bounded file identity and source checks preserve unverified outcomes without changing policy or ACLs, invoking explicit transcription or granting Sigma/EVTX credit. Added disposable standard-writer success/denial tests for Server 2022/2025 under both WELA host engines. (#436) (@Shirofune-Security)