mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-24 23:18:11 +02:00
Handle native WinRM manifest IDs without Int32 overflow
This commit is contained in:
1 parent
f403521068
commit
b61a3c6bca
8 files changed
+29
-8
No files matched your search
@@ -4,7 +4,7 @@
|
||||
|
||||
**改善:**
|
||||
|
||||
- Server 2022/2025 の Windows PowerShell 5.1/PowerShell 7 で、公開 provider-packs コマンドの Plan、DryRun、Configure、冪等性、前提不足・手動対象の拒否、部分適用を実機検証する使い捨て CI を追加。DNS Client、CAPI2、WinRM、RDP Client の設定と復元記録・ハッシュを確認し、完全な ACL、保持モード、大きい既存バッファ、他チャネル、サービス、全監査マスクの保持とテスト後の正確な復元を検証。イベント生成や Sigma 対応の証明は含みません。(@Shirofune-Security)
|
||||
- Server 2022/2025 の Windows PowerShell 5.1/PowerShell 7 で、公開 provider-packs コマンドの Plan、DryRun、Configure、冪等性、前提不足・手動対象の拒否、部分適用を実機検証する使い捨て CI を追加。DNS Client、CAPI2、WinRM、RDP Client の設定と復元記録・ハッシュを確認し、完全な ACL、保持モード、大きい既存バッファ、他チャネル、サービス、全監査マスクの保持とテスト後の正確な復元を検証。イベント生成や Sigma 対応の証明は含みません。 WinRM のマニフェスト ID をネイティブの Int64 として比較し、対象外の大きい ID により必要なイベントの確認が失敗する不具合も修正。 (@Shirofune-Security)
|
||||
|
||||
- Server 2022/2025 と Windows PowerShell 5.1/PowerShell 7 の破棄可能な環境で、Securityログ警告設定の公開CLIを検証します。未設定・0・高いしきい値、早い警告値の維持、DryRun、再実行、不正型の拒否と完全な復元を確認し、無関係な設定は保持します。ログ枯渇や警告イベント生成は検証範囲外です。 (@Shirofune-Security)
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
**Improvements:**
|
||||
|
||||
- Added disposable native acceptance for public provider-pack Plan, DryRun, Configure, idempotence, missing/manual refusal and partial outcomes on Server 2022/2025 under Windows PowerShell 5.1/PowerShell 7. Actual DNS Client, CAPI2, WinRM and RDP Client configuration preserves descriptors, retention, larger buffers, unrelated channels, services and all audit masks, with journal/hash evidence and exact fixture cleanup; no event or Sigma credit. (@Shirofune-Security)
|
||||
- Added disposable native acceptance for public provider-pack Plan, DryRun, Configure, idempotence, missing/manual refusal and partial outcomes on Server 2022/2025 under Windows PowerShell 5.1/PowerShell 7. Actual DNS Client, CAPI2, WinRM and RDP Client configuration preserves descriptors, retention, larger buffers, unrelated channels, services and all audit masks, with journal/hash evidence and exact fixture cleanup; no event or Sigma credit. Fixed WinRM manifest inspection to preserve native Int64 event IDs, so unrelated large IDs no longer block the selected event schema. (@Shirofune-Security)
|
||||
|
||||
- Verify public Security-log warning configuration on disposable Server 2022/2025 hosts under Windows PowerShell 5.1/PowerShell 7: absent/zero/higher thresholds, earlier-threshold preservation, dry run, idempotence, wrong-type refusal and exact cleanup. Preserve unrelated registry values, channel configuration, audit masks and CrashOnAuditFail; no log-exhaustion or warning-event claim. (@Shirofune-Security)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The dedicated `Native provider configuration acceptance` workflow tests the public `provider-packs` command on disposable GitHub-hosted Windows Server 2022 and 2025, separately under Windows PowerShell 5.1 and PowerShell 7. It complements the read-only manifest inventory and mocked failure tests described in [the provider-pack guide](native-provider-packs.md).
|
||||
|
||||
This fixture is destructive to the selected channels' temporary configuration and can discard records when restoring smaller buffers. It requires `-AllowDisposableProviderWrite`, `GITHUB_ACTIONS=true` and `RUNNER_ENVIRONMENT=github-hosted`; do not run it on ordinary machines. It makes no production configuration changes outside the existing public command's declared scope.
|
||||
This fixture is destructive to the selected channels' temporary configuration and can discard records when restoring smaller buffers. It requires `-AllowDisposableProviderWrite`, `GITHUB_ACTIONS=true` and `RUNNER_ENVIRONMENT=github-hosted`; do not run it on ordinary machines. Production behavior is unchanged; only this opted-in disposable fixture prepares and restores the temporary test settings.
|
||||
|
||||
## Actual public behavior checked
|
||||
|
||||
@@ -24,3 +24,5 @@ Each selected channel has independent cleanup that restores original enablement,
|
||||
`original.json`, public JSON reports, command output, actual journals, `completed.json`, `cleanup.json` and a SHA256 manifest are retained for seven days by the workflow. The manifest binds the fixture, product helpers, catalog, corpus and full reviewed rule-source bytes. Event records are not restored, and no retention-duration, Windows 11, domain/DC/ADCS, positive installed-DNS, forwarding or Sigma acceptance is implied. This advances issues #386 and #366 without closing their broader acceptance work.
|
||||
|
||||
The underlying enablement, size, retention and backup options follow Microsoft's [wevtutil command reference](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/wevtutil). The product's existing channel floors and schema gates remain unchanged.
|
||||
|
||||
The first native run exposed a WinRM manifest bug: an unrelated event ID `3221734403` overflowed the reader's signed 32-bit cast and made the whole provider schema unknown. The reader now compares [EventMetadata.Id in its native Int64 domain](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventing.reader.eventmetadata.id), then parses only the exact reviewed event/channel templates. Focused tests also require refusal when only unrelated large IDs exist; schema gates are unchanged.
|
||||
@@ -53,12 +53,15 @@ function Get-WelaProviderPackSchema {
|
||||
if ([guid]$provider.Id -eq [guid]::Empty) { throw 'Provider GUID is unknown.' }
|
||||
if (@($logs[0].ProviderNames) -notcontains $Pack.provider -or @($provider.LogLinks.LogName) -notcontains $Pack.channel) { throw 'Provider/channel links disagree.' }
|
||||
$events = @()
|
||||
# EventMetadata.Id is Int64; WinRM includes unrelated IDs above Int32.MaxValue.
|
||||
# Compare before parsing selected templates, without narrowing the native ID.
|
||||
$expectedIds = @($Pack.events | ForEach-Object { [long]$_.id })
|
||||
foreach ($event in $provider.Events) {
|
||||
if (@($Pack.events.id) -contains [int]$event.Id -and $event.LogLink.LogName -eq $Pack.channel) {
|
||||
if ($expectedIds -contains [long]$event.Id -and $event.LogLink.LogName -eq $Pack.channel) {
|
||||
$fields = @(Get-WelaProviderTemplateFields -Template $event.Template)
|
||||
$sha = [Security.Cryptography.SHA256]::Create()
|
||||
try { $templateHash = ([BitConverter]::ToString($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes([string]$event.Template)))).Replace('-','').ToLowerInvariant() } finally { $sha.Dispose() }
|
||||
$events += [pscustomobject]@{ Id=[int]$event.Id; Version=[int]$event.Version; Channel=[string]$event.LogLink.LogName; Fields=$fields; TemplateSha256=$templateHash }
|
||||
$events += [pscustomobject]@{ Id=[long]$event.Id; Version=[int]$event.Version; Channel=[string]$event.LogLink.LogName; Fields=$fields; TemplateSha256=$templateHash }
|
||||
}
|
||||
}
|
||||
[pscustomobject]@{ State='Observed'; Provider=[string]$provider.Name; ProviderGuid=[string]$provider.Id; ChannelType=[string]$logs[0].LogType; Events=$events; Diagnostic=$null }
|
||||
|
||||
@@ -74,6 +74,7 @@ $before=@{};$raw=@{};$prepared=@{};$preparedRaw=@{};$services=@(Services);$polic
|
||||
foreach($channel in $channels){$before[$channel]=Get-WelaNativeChannel $channel;if(Test-WelaNativeChannelSnapshot $before[$channel]){$raw[$channel]=Read-Raw $channel}}
|
||||
$rawText=@{};foreach($channel in $raw.Keys){$rawText[$channel]=$raw[$channel].OuterXml}
|
||||
Save 'original.json' @{Channels=$before;RawXml=$rawText;Services=$services;AuditMasks=$policies;Engine=$PSVersionTable.PSVersion.ToString()}
|
||||
function Stable-Selected {foreach($channel in $selected.channel){Assert (Test-WelaNativeChannelSnapshotEqual $configured[$channel] (Get-WelaNativeChannel $channel)) 'Idempotent/refused/partial invocation preserves the expected complete selected-channel tuple.'}}
|
||||
function Preserved {
|
||||
foreach($channel in $channels){
|
||||
$now=Get-WelaNativeChannel $channel
|
||||
@@ -95,6 +96,7 @@ try {
|
||||
$initial=Public 'initial' 'Plan' $names
|
||||
foreach($entry in $initial.ControlsPlan){Assert ($entry.ProviderEvidence.CanConfigure -and $entry.ProviderEvidence.Schema.State -ceq 'Observed' -and $entry.ProviderEvidence.Schema.Provider -ceq $entry.Pack.provider) 'Exact actual provider/schema permits the selected pack.'}
|
||||
Assert ($initial.ControlsPlan.Count -eq 4) 'Exactly four explicit packs are observed.'
|
||||
foreach($channel in $raw.Keys){Assert ((Read-Raw $channel).OuterXml -ceq $raw[$channel].OuterXml) 'Initial public Plan preserves every registered channel configuration.'}
|
||||
foreach($pack in $selected){
|
||||
$channel=$pack.channel;$mutated+=,$channel
|
||||
$size=if($pack.id -ceq 'winrm'){2147483648L}else{1048576L}
|
||||
@@ -111,12 +113,13 @@ try {
|
||||
$null=Public 'grant-option' 'Configure' $names -Expected 1 -Extra @('-GrantEventLogReaders')
|
||||
foreach($channel in $selected.channel){Assert (Test-WelaNativeChannelSnapshotEqual $prepared[$channel] (Get-WelaNativeChannel $channel)) 'Plan, DryRun and invalid options preserve prepared actual state.'}
|
||||
Preserved
|
||||
$configured=@{}
|
||||
$applied=Public 'configure' 'Configure' $names
|
||||
Assert ($applied.Action -ceq 'Configure' -and $applied.Scope -ceq 'native-channel-settings-only' -and $applied.Results.Count -eq 4 -and @($applied.Results|Where-Object Status -cne 'Applied').Count -eq 0) 'All four explicit configurations are actually Applied.'
|
||||
$journal=@(Get-Content "$root/configure-journal/before.jsonl"|ForEach-Object {$_|ConvertFrom-Json})
|
||||
Assert ($journal.Count -eq 4 -and @($journal|Where-Object {$selected.channel -notcontains $_.Target.Channel}).Count -eq 0) 'Exactly four selected changes have durable original journals.'
|
||||
foreach($pack in $selected){
|
||||
$channel=$pack.channel;$entry=@($applied.Results|Where-Object {$_.Target.Channel -ceq $channel});$j=@($journal|Where-Object {$_.Target.Channel -ceq $channel});$now=Get-WelaNativeChannel $channel
|
||||
$channel=$pack.channel;$entry=@($applied.Results|Where-Object {$_.Target.Channel -ceq $channel});$j=@($journal|Where-Object {$_.Target.Channel -ceq $channel});$now=Get-WelaNativeChannel $channel;$configured[$channel]=$now
|
||||
$minimum=if($pack.id -ceq 'capi2'){102432768L}elseif($pack.id -ceq 'winrm'){2147483648L}else{33554432L}
|
||||
Assert ($entry.Count -eq 1 -and $j.Count -eq 1 -and (Test-WelaNativeChannelSnapshotEqual $j[0].Before $prepared[$channel]) -and (Test-WelaNativeChannelSnapshotEqual $entry[0].Before $prepared[$channel])) 'Native journal and result retain exact prepared before-state.'
|
||||
Assert ($now.IsEnabled -and $now.MaximumSizeInBytes -eq $minimum -and (Test-WelaNativeChannelSnapshotEqual $entry[0].After $now)) 'Exact native enable/floor/larger-buffer readback matches Applied after-state.'
|
||||
@@ -126,16 +129,20 @@ try {
|
||||
Preserved
|
||||
$repeat=Public 'repeat' 'Configure' $names
|
||||
Assert (@($repeat.Results|Where-Object Status -cne 'AlreadyCompliant').Count -eq 0 -and -not(Test-Path "$root/repeat-journal/before.jsonl")) 'Native repeat is idempotent and journals no write.'
|
||||
Stable-Selected
|
||||
$manual=Public 'manual' 'Configure' @('dns-server-analytical','dns-server-classic') -Expected 1
|
||||
Assert ($manual.Results.Count -eq 2 -and @($manual.Results|Where-Object Status -cne 'Failed').Count -eq 0 -and -not(Test-Path "$root/manual-journal/before.jsonl")) 'Both actual manual-only selections fail without channel mutation or journal.'
|
||||
Stable-Selected
|
||||
$missing=Public 'missing-dns' 'Configure' @('dns-server-audit') -Expected 1
|
||||
Assert ($missing.Results[0].Status -ceq 'Failed' -and $missing.ControlsPlan[0].ProviderEvidence.Service.State -ceq 'Not installed' -and -not(Test-Path "$root/missing-dns-journal/before.jsonl")) 'Missing actual DNS service cannot be replaced by an assumed server role.'
|
||||
Stable-Selected
|
||||
# A genuine partial public run must retain one success and one manual refusal.
|
||||
$capi='Microsoft-Windows-CAPI2/Operational';$null=Invoke-WelaNative wevtutil.exe @('sl',$capi,'/e:false');$partialBefore=Get-WelaNativeChannel $capi
|
||||
$partial=Public 'partial' 'Configure' @('capi2','dns-server-analytical') -Expected 1
|
||||
Assert (@($partial.Results|Where-Object Status -ceq 'Applied').Count -eq 1 -and @($partial.Results|Where-Object Status -ceq 'Failed').Count -eq 1 -and (Get-WelaNativeChannel $capi).IsEnabled) 'Actual partial configuration retains one verified change and explicit nonzero failure.'
|
||||
$partialJournal=@(Get-Content "$root/partial-journal/before.jsonl"|ForEach-Object {$_|ConvertFrom-Json})
|
||||
Assert ($partialJournal.Count -eq 1 -and $partialJournal[0].Target.Channel -ceq $capi -and (Test-WelaNativeChannelSnapshotEqual $partialJournal[0].Before $partialBefore)) 'Partial run journals only its actual selected write.'
|
||||
Stable-Selected
|
||||
Preserved
|
||||
Save 'completed.json' @{Status='Passed';Assertions=$count;ActualAppliedControls=5;IdempotentControls=4;ManualRefusals=3;MissingServiceRefusals=1;ReadyRuleCredit=0}
|
||||
}catch{$primary=$_}
|
||||
|
||||
@@ -43,6 +43,10 @@ function Get-WinEvent {
|
||||
$channel=if($f.TemplateMode -eq 'wrongchannel'){'Other/Operational'}else{$p.channel}
|
||||
$events+= [pscustomobject]@{Id=$e.id;Version=0;LogLink=[pscustomobject]@{LogName=$channel};Template=$template}
|
||||
}
|
||||
if($f.LargeIds){
|
||||
if($f.LargeOnly){$events=@()}
|
||||
foreach($large in @([long]3221734403,[long]4294967295)){$events+=[pscustomobject]@{Id=$large;Version=0;LogLink=[pscustomobject]@{LogName=$p.channel};Template='unselected template is never parsed'}}
|
||||
}
|
||||
[pscustomobject]@{Name=$ListProvider;Id='11111111-1111-1111-1111-111111111111';LogLinks=@([pscustomobject]@{LogName=$p.channel});Events=$events}
|
||||
}
|
||||
function Read-Host {param($Prompt) if($f.PromptSchemaDrift){$f.TemplateMode='missing'};$f.Prompt}
|
||||
@@ -95,6 +99,11 @@ try {
|
||||
Assert (@($entry.RuleReviews|Where-Object Eligibility -ne 'Conditional').Count -eq 0 -and $report.ReadyRules -eq 0) 'Provider settings never convert incomplete rule evidence into Ready.'
|
||||
Assert ($entry.ProviderEvidence.Schema.Events[0].Fields[0].InType -eq 'win:UnicodeString' -and $entry.ProviderEvidence.Schema.Events[0].TemplateSha256.Length -eq 64) 'Report retains runtime version, native field types and template fingerprint.'
|
||||
Assert ($f.Writes.Count -eq 0 -and -not(Test-Path $backup)) 'Read-only plan creates no journal and makes no channel changes.'
|
||||
Reset;$f.LargeIds=$true;$r=Invoke-WelaProviderPackCommand -Action Plan -Names winrm
|
||||
Assert ($r.ExitCode -eq 0 -and $r.ControlsPlan[0].ProviderEvidence.CanConfigure) 'Actual WinRM Int64 event IDs above Int32 do not invalidate unrelated selected event6.'
|
||||
Assert ($r.ControlsPlan[0].ProviderEvidence.Schema.Events.Count -eq 1 -and $r.ControlsPlan[0].ProviderEvidence.Schema.Events[0].Id -eq 6) 'Only the exact reviewed event6 enters schema evidence; large unselected IDs/templates are excluded.'
|
||||
Reset;$f.LargeIds=$true;$f.LargeOnly=$true;$r=Invoke-WelaProviderPackCommand -Action Configure -Names winrm -Auto -BackupPath $backup
|
||||
Assert ($r.ExitCode -eq 1 -and -not $r.ControlsPlan[0].ProviderEvidence.CanConfigure -and $f.Writes.Count -eq 0) 'Unrelated large native IDs cannot substitute for a missing selected event6.'
|
||||
Reset;$f.States['Microsoft-Windows-DNS-Client/Operational'].State='Not installed';$f.States['Microsoft-Windows-DNS-Client/Operational'].IsEnabled=$null
|
||||
$r=Invoke-WelaProviderPackCommand -Action Configure -Names dns-client -Auto -BackupPath $backup
|
||||
Assert ($r.ExitCode -eq 1 -and $f.Writes.Count -eq 0) 'Missing actual channel metadata cannot be replaced by provider-manifest availability.'
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
**改善:**
|
||||
|
||||
- Server 2022/2025 の Windows PowerShell 5.1/PowerShell 7 で、公開 provider-packs コマンドの Plan、DryRun、Configure、冪等性、前提不足・手動対象の拒否、部分適用を実機検証する使い捨て CI を追加。DNS Client、CAPI2、WinRM、RDP Client の設定と復元記録・ハッシュを確認し、完全な ACL、保持モード、大きい既存バッファ、他チャネル、サービス、全監査マスクの保持とテスト後の正確な復元を検証。イベント生成や Sigma 対応の証明は含みません。(@Shirofune-Security)
|
||||
- Server 2022/2025 の Windows PowerShell 5.1/PowerShell 7 で、公開 provider-packs コマンドの Plan、DryRun、Configure、冪等性、前提不足・手動対象の拒否、部分適用を実機検証する使い捨て CI を追加。DNS Client、CAPI2、WinRM、RDP Client の設定と復元記録・ハッシュを確認し、完全な ACL、保持モード、大きい既存バッファ、他チャネル、サービス、全監査マスクの保持とテスト後の正確な復元を検証。イベント生成や Sigma 対応の証明は含みません。 WinRM のマニフェスト ID をネイティブの Int64 として比較し、対象外の大きい ID により必要なイベントの確認が失敗する不具合も修正。 (@Shirofune-Security)
|
||||
|
||||
- Server 2022/2025 と Windows PowerShell 5.1/PowerShell 7 の破棄可能な環境で、Securityログ警告設定の公開CLIを検証します。未設定・0・高いしきい値、早い警告値の維持、DryRun、再実行、不正型の拒否と完全な復元を確認し、無関係な設定は保持します。ログ枯渇や警告イベント生成は検証範囲外です。 (@Shirofune-Security)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
**Improvements:**
|
||||
|
||||
- Added disposable native acceptance for public provider-pack Plan, DryRun, Configure, idempotence, missing/manual refusal and partial outcomes on Server 2022/2025 under Windows PowerShell 5.1/PowerShell 7. Actual DNS Client, CAPI2, WinRM and RDP Client configuration preserves descriptors, retention, larger buffers, unrelated channels, services and all audit masks, with journal/hash evidence and exact fixture cleanup; no event or Sigma credit. (@Shirofune-Security)
|
||||
- Added disposable native acceptance for public provider-pack Plan, DryRun, Configure, idempotence, missing/manual refusal and partial outcomes on Server 2022/2025 under Windows PowerShell 5.1/PowerShell 7. Actual DNS Client, CAPI2, WinRM and RDP Client configuration preserves descriptors, retention, larger buffers, unrelated channels, services and all audit masks, with journal/hash evidence and exact fixture cleanup; no event or Sigma credit. Fixed WinRM manifest inspection to preserve native Int64 event IDs, so unrelated large IDs no longer block the selected event schema. (@Shirofune-Security)
|
||||
|
||||
- Verify public Security-log warning configuration on disposable Server 2022/2025 hosts under Windows PowerShell 5.1/PowerShell 7: absent/zero/higher thresholds, earlier-threshold preservation, dry run, idempotence, wrong-type refusal and exact cleanup. Preserve unrelated registry values, channel configuration, audit masks and CrashOnAuditFail; no log-exhaustion or warning-event claim. (@Shirofune-Security)
|
||||
|
||||
|
||||
Reference in new issue
Block a user