diff --git a/.github/workflows/audit-catalog-mappings.yml b/.github/workflows/audit-catalog-mappings.yml new file mode 100644 index 00000000..e5bf0fb6 --- /dev/null +++ b/.github/workflows/audit-catalog-mappings.yml @@ -0,0 +1,25 @@ +name: Audit catalog mapping regressions +on: + push: + branches: ['**'] + pull_request: + workflow_dispatch: +permissions: + contents: read +jobs: + audit-catalog-mappings: + runs-on: windows-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Catalog, ambiguity and renderer fixtures on Windows PowerShell 5.1 + shell: powershell + run: ./tests/AuditCatalogMappings.Tests.ps1 + - name: Native identifier observations on Windows PowerShell 5.1 + shell: powershell + run: ./tests/AuditCatalogMappings.Windows.Tests.ps1 + - name: Catalog, ambiguity and renderer fixtures on PowerShell 7 + shell: pwsh + run: ./tests/AuditCatalogMappings.Tests.ps1 + - name: Native identifier observations on PowerShell 7 + shell: pwsh + run: ./tests/AuditCatalogMappings.Windows.Tests.ps1 diff --git a/CHANGELOG-Japanese.md b/CHANGELOG-Japanese.md index 61fa3982..40c69bbe 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -6,6 +6,7 @@ - CIS v4.0.0 Level 2向けに、Windows PowerShell 5.1トランスクリプトの明示的な監査・計画・設定機能を追加しました。管理者が選択した既存の出力ディレクトリを確認し、ポリシーとは分けて報告します。型を含むレジストリ変更前の状態を保存し、共有される32/64ビットのビューと変更後の状態を検証します。呼び出しヘッダーの設定は保持し、ACL・共有・保存期間は変更せず、Sigma EVTX検知範囲の向上も自動加算しません。使い捨て環境の実トランスクリプトテストでは元のポリシーを復元します。中央保存先の権限と収集は別途検証が必要です。 (#405) (@Shirofune-Security) - 通常の設定でDCのField Engineeringを自動的にレベル5へ変更せず、既存のLDAP 1644診断設定を保持するようにしました。明示的な`ldap-diagnostics`の監査・計画・設定を追加し、保持、しきい値を指定した診断、MDIの旧設定削除を選択できます。役割・ビルド確認、型付き復旧記録、競合検出、順序付きの読み戻しと最終状態確認に対応します。LDAP専用オプションを他のプロファイルコマンドへ指定すると、実行前に拒否します。イベント生成・量・転送は隔離DCでの検証が必要です。 (#404) (@Shirofune-Security) +- 従来のToken Right Adjusted EventsのGUIDを修正し、すべてのベースラインでRPCとトークン監査の状態を個別に評価するようにしました。実行時のカタログID・明示的な名前別表記の検証と、ハッシュ付きの読み取り専用EventID対応表レビューを追加しました。曖昧な対応、カテゴリだけの対応、不明な対応から検知可能とは判断しません。 (#403) (@Shirofune-Security) - MDIのドメイン/Exchange Configuration監査と、明示的に選択した証明書テンプレート/登録サービスオブジェクト向けに、任意実行の`ad-object-sacl`監査・計画・設定・保守的なロールバックを追加しました。接続先DCとスキーマGUIDを検証し、既存のセキュリティ設定を保持したまま不足する監査ACEだけをSACLに追加します。変更前のSDDLと追加ACEを保存し、書き込み後と最終状態を確認します。任意のdMSA前提条件が不明な場合は未確認のスキップ項目として報告し、独立した他の5種類のドメインクラスの監査ACEは引き続き設定します。実効監査ポリシー、継承・レプリケーション、4662/5136イベントの証拠は隔離DCで別途検証が必要です。Sigma検知範囲の向上は未検証です。 (#402) (@Shirofune-Security) - Microsoft WEF Appendix Cのチャネルを監査・計画・設定する任意実行の`channel-settings`を追加しました。CAPI2の有効化、原典の正確なバイト数、明示的に指定したEvent Log Readersの読み取りACEに対応します。既存のセキュリティ記述子・ACEと大きいバッファを保持し、安全に扱えないACLは変更しません。共通の復旧記録、書き込み直前の状態確認と読み戻しで失敗を報告します。Appendix E/Fの標準チャネル一覧からSysmonとEMETを除外し、実際のIDによるアクセス・イベント生成・収集は未検証と表示します。Windowsラボでの検証は別途必要です。 (#401) (@Shirofune-Security) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b1f3a7..cca442c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added explicit CIS v4.0.0 Level 2 Windows PowerShell 5.1 transcription audit, plan and configure actions. An operator-selected existing output directory is checked and reported separately from policy; typed canonical registry writes are journaled, verified through shared 32/64-bit views and checked for drift while preserving invocation-header preferences. No ACL/share/retention changes or automatic Sigma EVTX credit are introduced; disposable native transcript tests restore original policy, and central authorization/collection remains a deployment check. (#405) (@Shirofune-Security) - Preserved LDAP 1644 diagnostics during normal configuration instead of automatically enabling Field Engineering level 5 on DCs. Added explicit `ldap-diagnostics` audit/plan/configure modes for preservation, tunable diagnostics and MDI legacy cleanup, with role/build checks, typed recovery snapshots, race guards, ordered readback and final drift checks. LDAP-only options are rejected before unrelated profile commands can run. Generated events, volume and forwarding remain isolated-DC validation. (#404) (@Shirofune-Security) +- Corrected the legacy Token Right Adjusted Events GUID so RPC and token auditing are assessed independently in every baseline. Added runtime catalog identity/alias checks and a read-only, fingerprinted EventID mapping review that preserves ambiguous, category-only and unknown candidates without detection credit. (#403) (@Shirofune-Security) - Added opt-in `ad-object-sacl` audit, plan, configure and conservative rollback actions for MDI domain/Exchange Configuration auditing and explicitly selected certificate template/enrollment service objects. Exact DC binding, schema GUID checks, additive SACL-only changes, pre-write SDDL/ACE receipts and read-back preserve existing security entries. Unknown optional dMSA prerequisites are reported as a separate skipped gap while the five independent domain class ACEs continue. Effective audit policy, inheritance/replication and 4662/5136 event evidence remain separate isolated-DC checks; no Sigma uplift is claimed. (#402) (@Shirofune-Security) - Added opt-in `channel-settings` audit, plan and configure actions for Microsoft WEF Appendix C, including CAPI2 enablement, exact source byte sizes and an explicitly requested Event Log Readers read ACE. Existing descriptor components/ACEs and larger buffers are preserved or unsafe ACL edits are refused; shared journaling, fresh-state guards and readback report failures. Native Appendix E/F channel inventories exclude Sysmon/EMET and retain unverified identity access, generation and ingestion prerequisites. Windows lab evidence remains pending. (#401) (@Shirofune-Security) diff --git a/WELA.ps1 b/WELA.ps1 index ce4847fa..2761963e 100644 --- a/WELA.ps1 +++ b/WELA.ps1 @@ -66,6 +66,7 @@ $SaclTargetsPath = Join-Path $ScriptRoot "config/audit_sacl_targets.json" . (Join-Path $ScriptRoot "scripts/WmiNamespaceAuditing.ps1") . (Join-Path $ScriptRoot "scripts/PowerShellTranscription.ps1") Import-Module (Join-Path $ScriptRoot "modules/AuditProfiles.psm1") -ErrorAction Stop +Import-Module (Join-Path $ScriptRoot "modules/AuditCatalog.psm1") -ErrorAction Stop Import-Module (Join-Path $ScriptRoot "modules/NativeProviders.psm1") -ErrorAction Stop Import-Module (Join-Path $ScriptRoot "modules/EventLogSettings.psm1") -ErrorAction Stop . (Join-Path $ScriptRoot "scripts/EventLogConfiguration.ps1") @@ -336,7 +337,9 @@ function GetBaselineConfig { if (-not (Test-Path -Path $script:BaselineConfigPath)) { throw "Baseline config not found: $script:BaselineConfigPath" } - return Get-Content -Path $script:BaselineConfigPath -Raw | ConvertFrom-Json + $data = Get-Content -Path $script:BaselineConfigPath -Raw | ConvertFrom-Json + Assert-WelaAuditCatalog -Catalog $data.catalog -CanonicalCatalog (Import-WelaAuditProfiles).catalog + return $data } function GetBaselineNames { diff --git a/config/baselines.json b/config/baselines.json index 368ad7db..708405ec 100644 --- a/config/baselines.json +++ b/config/baselines.json @@ -456,7 +456,7 @@ "subCategory": "Token Right Adjusted Events", "select": { "type": "guid", - "guid": "0CCE922E-69AE-11D9-BED3-505054503030" + "guid": "0CCE924A-69AE-11D9-BED3-505054503030" }, "currentSetting": { "type": "auditpol" diff --git a/docs/audit-catalog-mappings.md b/docs/audit-catalog-mappings.md new file mode 100644 index 00000000..1ee7d66d --- /dev/null +++ b/docs/audit-catalog-mappings.md @@ -0,0 +1,17 @@ +# Audit identifiers and EventID mapping review + +`Token Right Adjusted Events` now uses `0CCE924A-69AE-11D9-BED3-505054503030` in the legacy baseline catalog, matching the versioned audit catalog. RPC keeps `0CCE922E-69AE-11D9-BED3-505054503030`. This fixes legacy ASD/Microsoft assessments that previously displayed RPC state for token auditing. Recommendations and enablement masks are unchanged. + +Every baseline load checks canonical name/GUID pairs and rejects duplicate legacy identifiers. Only three explicit spelling aliases are accepted: `Non-Sensitive Privilege Use`, `User / Device Claims`, and `Central Policy Staging`. They map to the existing canonical names; no fuzzy matching or arbitrary GUID aliases are allowed. + +Review the bundled EventID candidates without reading or changing Windows: + +```powershell +./scripts/Review-AuditCatalog.ps1 -ResultsPath mapping-review.json +``` + +The export fingerprints the mapping file, lists candidates and reasons per EventID, and separates blank category headings. Missing mappings or candidates with only a category GUID are unknown. Multiple subcategories, unresolved object types, outcomes and role context remain conditional. `DetectionReady` is always false: an identifier review cannot verify a detection. This helper does not replace full rule eligibility or establish the complete Boolean/field requirements of a Sigma rule. + +The bundled CSV remains a historical candidate map, not a universally valid event-generation contract. For example, 4703 appears against both Token Right Adjusted and Authorization Policy Change. Microsoft's [Token Right Adjusted page](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/audit-token-right-adjusted) lists it, while the [4703 event page](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4703) names Authorization Policy Change. WELA retains the conflict rather than inventing a build-independent resolution. Microsoft also states that Token Right Adjusted has no Failure events; setting a Failure mask is not proof of Failure records. + +Fixtures check malformed/duplicate identifiers, unknown events, ambiguous 4703/object mappings, and independent RPC/token state through all four legacy baseline renderers. Windows CI runs [`auditpol /list /subcategory:* /v`](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/auditpol-list) and native policy queries under Windows PowerShell 5.1/PowerShell 7. These checks validate identifiers/readback, not generated event XML. Build-specific client, member-server, DC and CA event/outcome validation remains separate acceptance work for issue #380. Native Windows functionality only; Sysmon is out of scope. diff --git a/modules/AuditCatalog.psm1 b/modules/AuditCatalog.psm1 new file mode 100644 index 00000000..e8563fee --- /dev/null +++ b/modules/AuditCatalog.psm1 @@ -0,0 +1,72 @@ +# Identifier validation is separate from recommendations and detection readiness. +Set-StrictMode -Version 2.0 + +function ConvertTo-WelaCanonicalAuditName { + param([string]$Name) + # Only reviewed spelling aliases; do not accept arbitrary fuzzy matches. + switch -CaseSensitive ($Name) { + 'Non-Sensitive Privilege Use' { return 'Non Sensitive Privilege Use' } + 'User / Device Claims' { return 'User/Device Claims' } + 'Central Policy Staging' { return 'Central Access Policy Staging' } + default { return $Name } + } +} + +function Assert-WelaAuditCatalog { + [CmdletBinding()] + param([Parameter(Mandatory)]$Catalog, [Parameter(Mandatory)]$CanonicalCatalog) + $canonical = @{}; $canonicalGuids = @{}; $seen = @{}; $ids = @{} + foreach ($row in $CanonicalCatalog) { + if (-not $row.id -or $canonical.ContainsKey($row.id) -or + $row.guid -notmatch '^[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$' -or $canonicalGuids.ContainsKey($row.guid)) { + throw 'Invalid or duplicate canonical audit identifier.' + } + $canonical[$row.id] = $row.guid; $canonicalGuids[$row.guid] = $true + } + foreach ($row in @($Catalog | Where-Object { $_.currentSetting.type -eq 'auditpol' })) { + $name = ConvertTo-WelaCanonicalAuditName $row.subCategory + if (-not $row.id -or $ids.ContainsKey($row.id)) { throw "Duplicate or empty legacy audit id: $($row.id)" } + if ($row.select.type -ne 'guid' -or -not $canonical.ContainsKey($name) -or $canonical[$name] -ine $row.select.guid) { + throw "Audit catalog name/GUID mismatch: $($row.subCategory) / $($row.select.guid)" + } + if ($seen.ContainsKey($row.select.guid)) { throw "Duplicate legacy audit GUID: $($row.select.guid)" } + $seen[$row.select.guid] = $true; $ids[$row.id] = $true + } +} + +function Get-WelaEventMappingReview { + [CmdletBinding()] + param( + [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Mappings, + [Parameter(Mandatory)]$CanonicalCatalog, + [Parameter(Mandatory)][ValidateRange(0,65535)][int]$EventId + ) + $byGuid = @{} + foreach ($row in $CanonicalCatalog) { $byGuid[$row.guid] = $row.id } + # Empty category-heading rows must never become EventID 0 through a cast. + $matches = @($Mappings | Where-Object { $_.'Event ID' -match '^\d+$' -and [int]$_.'Event ID' -eq $EventId }) + $candidates = @(); $uncertain = @() + foreach ($row in $matches) { + if (-not $row.Subcategory -or -not $byGuid.ContainsKey($row.GUID)) { + $uncertain += 'CategoryOnlyOrUnknownGuid'; continue + } + if ((ConvertTo-WelaCanonicalAuditName $row.Subcategory) -cne $byGuid[$row.GUID]) { + $uncertain += 'NameGuidMismatch'; continue + } + $candidates += [pscustomobject]@{ Name=$byGuid[$row.GUID]; Guid=$row.GUID } + } + $candidates = @($candidates | Sort-Object Guid -Unique) + $reasons = @($uncertain | Select-Object -Unique) + if (-not $matches.Count) { $reasons += 'NoMapping' } + if ($candidates.Count -gt 1) { $reasons += 'MultipleSubcategories' } + # Even an unambiguous mapping does not prove outcome, object, fields or source role. + $reasons += 'OutcomeObjectAndRoleUnverified' + [pscustomobject]@{ + EventId=$EventId + State=$(if (-not $matches.Count -or -not $candidates.Count) { 'Unknown' } else { 'Conditional' }) + MappingCount=$matches.Count; Candidates=$candidates; Reasons=$reasons + DetectionReady=$false + } +} + +Export-ModuleMember -Function Assert-WelaAuditCatalog, Get-WelaEventMappingReview diff --git a/scripts/Review-AuditCatalog.ps1 b/scripts/Review-AuditCatalog.ps1 new file mode 100644 index 00000000..c2ae1ede --- /dev/null +++ b/scripts/Review-AuditCatalog.ps1 @@ -0,0 +1,24 @@ +# Developer-facing, read-only catalog review. No Windows query or policy changes. +param([string]$ResultsPath) +$ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force +Import-Module (Join-Path $PSScriptRoot '../modules/AuditCatalog.psm1') -Force +$root = Split-Path $PSScriptRoot -Parent +$canonical = (Import-WelaAuditProfiles).catalog +$legacy = Get-Content (Join-Path $root 'config/baselines.json') -Raw | ConvertFrom-Json +Assert-WelaAuditCatalog -Catalog $legacy.catalog -CanonicalCatalog $canonical +$mappingPath = Join-Path $root 'config/eid_subcategory_mapping.csv' +$mappings = @(Import-Csv -LiteralPath $mappingPath) +$rows = @($mappings | Where-Object { $_.'Event ID' -match '^\d+$' } | ForEach-Object { [int]$_.'Event ID' } | Sort-Object -Unique | ForEach-Object { + Get-WelaEventMappingReview -Mappings $mappings -CanonicalCatalog $canonical -EventId $_ +}) +$result = [pscustomobject]@{ + SchemaVersion=1; Scope='catalog-identifiers-and-mapping-uncertainty'; GeneratedUtc=[DateTime]::UtcNow.ToString('o') + CanonicalCount=$canonical.Count; LegacyCount=@($legacy.catalog | Where-Object { $_.currentSetting.type -eq 'auditpol' }).Count + MappingSha256=(Get-FileHash -LiteralPath $mappingPath -Algorithm SHA256).Hash + CategoryHeadingRows=@($mappings | Where-Object { -not $_.'Event ID' }).Count + Provenance='Bundled mapping candidates, not a build-specific event-generation contract. See docs/audit-catalog-mappings.md.' + Events=$rows; DetectionReadyCount=0 +} +if ($ResultsPath) { $result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $ResultsPath -Encoding UTF8 -ErrorAction Stop } +$result diff --git a/tests/AuditCatalogMappings.Tests.ps1 b/tests/AuditCatalogMappings.Tests.ps1 new file mode 100644 index 00000000..c6d3ca8a --- /dev/null +++ b/tests/AuditCatalogMappings.Tests.ps1 @@ -0,0 +1,62 @@ +$ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force +Import-Module (Join-Path $PSScriptRoot '../modules/AuditCatalog.psm1') -Force +$script:count = 0 +function Assert($Condition, $Message) { if (-not $Condition) { throw $Message }; $script:count++ } +function Reject([scriptblock]$Action) { + try { & $Action; throw 'Expected rejection did not occur.' } + catch { Assert ($_.Exception.Message -match 'mismatch|Duplicate|duplicate') "Unexpected failure: $_" } +} +$root = Split-Path $PSScriptRoot -Parent +$canonical = (Import-WelaAuditProfiles).catalog +$legacyPath = Join-Path $root 'config/baselines.json' +$legacy = Get-Content $legacyPath -Raw | ConvertFrom-Json +Assert-WelaAuditCatalog $legacy.catalog $canonical +foreach ($case in @('wrong-guid','duplicate-guid','unknown-name','duplicate-id')) { + $copy = Get-Content $legacyPath -Raw | ConvertFrom-Json + $row = $copy.catalog | Where-Object subCategory -eq 'Token Right Adjusted Events' + switch ($case) { + 'wrong-guid' { $row.select.guid='0CCE922E-69AE-11D9-BED3-505054503030' } + 'duplicate-guid' { $copy.catalog += $row } + 'unknown-name' { $row.subCategory='Token Right Adjusted Typo' } + 'duplicate-id' { ($copy.catalog | Where-Object subCategory -eq 'RPC Events').id=$row.id } + } + Reject { Assert-WelaAuditCatalog $copy.catalog $canonical } +} +$mappings = @(Import-Csv (Join-Path $root 'config/eid_subcategory_mapping.csv')) +$ambiguous = Get-WelaEventMappingReview $mappings $canonical 4703 +Assert ($ambiguous.State -eq 'Conditional' -and $ambiguous.Candidates.Count -eq 2 -and $ambiguous.Reasons -contains 'MultipleSubcategories' -and -not $ambiguous.DetectionReady) '4703 conflicting source mappings must remain conditional.' +$object = Get-WelaEventMappingReview $mappings $canonical 4663 +Assert ($object.State -eq 'Conditional' -and -not $object.DetectionReady) 'Object EventID alone never establishes matching object/SACL/outcome.' +$unknown = Get-WelaEventMappingReview $mappings $canonical 65535 +Assert ($unknown.State -eq 'Unknown' -and $unknown.Candidates.Count -eq 0) 'Unknown events cannot acquire a policy or readiness.' +$zero = Get-WelaEventMappingReview $mappings $canonical 0 +Assert ($zero.MappingCount -eq 0 -and $zero.State -eq 'Unknown') 'Blank category rows must not turn into EventID zero.' +$category = Get-WelaEventMappingReview $mappings $canonical 4608 +Assert ($category.Reasons -contains 'CategoryOnlyOrUnknownGuid' -and -not $category.DetectionReady) 'Category GUIDs cannot establish advanced subcategory readiness.' +$rpc = Get-WelaEventMappingReview $mappings $canonical 5712 +Assert ($rpc.Candidates.Count -eq 1 -and $rpc.Candidates[0].Name -eq 'RPC Events' -and $rpc.State -eq 'Conditional') 'A unique mapping still retains outcome/context uncertainty.' +$bad = [pscustomobject]@{'Event ID'='5712';Subcategory='Token Right Adjusted Events';GUID='0CCE922E-69AE-11D9-BED3-505054503030'} +$mismatch = Get-WelaEventMappingReview @($bad) $canonical 5712 +Assert ($mismatch.State -eq 'Unknown' -and $mismatch.Reasons -contains 'NameGuidMismatch') 'Mismatched candidate metadata must not be accepted.' + +# Exercise the actual legacy renderer for every named baseline with distinct RPC/token state. +. (Join-Path $root 'WELA.ps1') help -Role Client -Build 26100 6>$null | Out-Null +function GetAuditpol { @{ '0CCE922E-69AE-11D9-BED3-505054503030'='Failure'; '0CCE924A-69AE-11D9-BED3-505054503030'='Success' } } +function CheckRegistryValue { $false } +function Get-WelaNativeSources { @() } +function Get-WelaNativeSourceState { 'Unknown' } +function Get-WelaOutgoingNtlmState { [pscustomobject]@{Description='Unknown';PolicySource='Unknown'} } +function Get-WelaDomainNtlmState { [pscustomobject]@{Description='Unknown'} } +foreach ($baselineName in $legacy.baselines.PSObject.Properties.Name) { + $rows = BuildAuditResult -all_rules @() -Baseline $baselineName -enabledguid @('0CCE924A-69AE-11D9-BED3-505054503030') + Assert (($rows | Where-Object SubCategory -eq 'RPC Events').CurrentSetting -eq 'Failure') "$baselineName must read RPC independently." + Assert (($rows | Where-Object SubCategory -eq 'Token Right Adjusted Events').CurrentSetting -eq 'Success') "$baselineName must read Token independently." +} +$tmp = Join-Path ([IO.Path]::GetTempPath()) ('wela-mapping-review-'+[guid]::NewGuid().ToString('N')+'.json') +try { + & (Join-Path $root 'scripts/Review-AuditCatalog.ps1') -ResultsPath $tmp | Out-Null + $export = Get-Content $tmp -Raw | ConvertFrom-Json + Assert ($export.DetectionReadyCount -eq 0 -and $export.MappingSha256.Length -eq 64 -and $export.Events.Count -gt 100) 'Review export retains corpus fingerprint and explicit no-readiness semantics.' +} finally { Remove-Item $tmp -ErrorAction SilentlyContinue } +Write-Host "PASS: $script:count catalog/mapping assertions; all legacy baselines preserve distinct RPC/token state." diff --git a/tests/AuditCatalogMappings.Windows.Tests.ps1 b/tests/AuditCatalogMappings.Windows.Tests.ps1 new file mode 100644 index 00000000..202710ef --- /dev/null +++ b/tests/AuditCatalogMappings.Windows.Tests.ps1 @@ -0,0 +1,21 @@ +# Query only: no audit-policy writes or benign event generation. +$ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force +Import-Module (Join-Path $PSScriptRoot '../modules/AuditCatalog.psm1') -Force +$legacy = Get-Content (Join-Path $PSScriptRoot '../config/baselines.json') -Raw | ConvertFrom-Json +$canonical = (Import-WelaAuditProfiles).catalog +Assert-WelaAuditCatalog $legacy.catalog $canonical +$listing = @(& auditpol.exe /list '/subcategory:*' /v 2>&1) +if ($LASTEXITCODE -ne 0) { throw "auditpol listing failed: $($listing -join ' ')" } +$text = $listing -join "`n" +$current = Get-WelaEffectiveAuditPolicy +foreach ($name in @('RPC Events','Token Right Adjusted Events')) { + $row = $legacy.catalog | Where-Object subCategory -eq $name + if ($text -notmatch [regex]::Escape($row.select.guid) -or -not $current.ContainsKey($row.select.guid)) { throw "Native Windows omitted $name / $($row.select.guid)." } + # The hosted image uses English; on localized hosts only GUID presence is asserted. + if ([Globalization.CultureInfo]::InstalledUICulture.TwoLetterISOLanguageName -eq 'en') { + if (-not @($listing | Where-Object { $_ -match [regex]::Escape($row.select.guid) -and $_ -match [regex]::Escape($name) }).Count) { throw "Native name/GUID mismatch for $name." } + } + Write-Host "$name $($row.select.guid) observed mask=$($current[$row.select.guid])" +} +Write-Host 'PASS: native audit identifiers queried; no policy changes or event-generation claims.' diff --git a/tests/AuditProfileOutput.Tests.ps1 b/tests/AuditProfileOutput.Tests.ps1 index 029cdb02..c577ffff 100644 --- a/tests/AuditProfileOutput.Tests.ps1 +++ b/tests/AuditProfileOutput.Tests.ps1 @@ -1,6 +1,7 @@ # Exercise the real profile, audit renderer, rule coverage and CSV output with # injected audit observations. Only temporary files are written; no Windows policy changes. $ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditCatalog.psm1') -Force Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force $tokens = $null; $parseErrors = $null $ast = [System.Management.Automation.Language.Parser]::ParseFile((Join-Path $PSScriptRoot '../WELA.ps1'), [ref]$tokens, [ref]$parseErrors) diff --git a/tests/NativeProviders.Tests.ps1 b/tests/NativeProviders.Tests.ps1 index 6683eba3..de0a3b8b 100644 --- a/tests/NativeProviders.Tests.ps1 +++ b/tests/NativeProviders.Tests.ps1 @@ -1,5 +1,7 @@ # Real catalog + public audit renderer/exports; Windows reads are injected at the OS boundary. $ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force +Import-Module (Join-Path $PSScriptRoot '../modules/AuditCatalog.psm1') -Force Import-Module (Join-Path $PSScriptRoot '../modules/NativeProviders.psm1') -Force $module = Get-Module NativeProviders & $module { diff --git a/website/docs/resources/changelog.ja.md b/website/docs/resources/changelog.ja.md index cfb70ecf..d0618df0 100644 --- a/website/docs/resources/changelog.ja.md +++ b/website/docs/resources/changelog.ja.md @@ -9,6 +9,7 @@ - CIS v4.0.0 Level 2向けに、Windows PowerShell 5.1トランスクリプトの明示的な監査・計画・設定機能を追加しました。管理者が選択した既存の出力ディレクトリを確認し、ポリシーとは分けて報告します。型を含むレジストリ変更前の状態を保存し、共有される32/64ビットのビューと変更後の状態を検証します。呼び出しヘッダーの設定は保持し、ACL・共有・保存期間は変更せず、Sigma EVTX検知範囲の向上も自動加算しません。使い捨て環境の実トランスクリプトテストでは元のポリシーを復元します。中央保存先の権限と収集は別途検証が必要です。 (#405) (@Shirofune-Security) - 通常の設定でDCのField Engineeringを自動的にレベル5へ変更せず、既存のLDAP 1644診断設定を保持するようにしました。明示的な`ldap-diagnostics`の監査・計画・設定を追加し、保持、しきい値を指定した診断、MDIの旧設定削除を選択できます。役割・ビルド確認、型付き復旧記録、競合検出、順序付きの読み戻しと最終状態確認に対応します。LDAP専用オプションを他のプロファイルコマンドへ指定すると、実行前に拒否します。イベント生成・量・転送は隔離DCでの検証が必要です。 (#404) (@Shirofune-Security) +- 従来のToken Right Adjusted EventsのGUIDを修正し、すべてのベースラインでRPCとトークン監査の状態を個別に評価するようにしました。実行時のカタログID・明示的な名前別表記の検証と、ハッシュ付きの読み取り専用EventID対応表レビューを追加しました。曖昧な対応、カテゴリだけの対応、不明な対応から検知可能とは判断しません。 (#403) (@Shirofune-Security) - MDIのドメイン/Exchange Configuration監査と、明示的に選択した証明書テンプレート/登録サービスオブジェクト向けに、任意実行の`ad-object-sacl`監査・計画・設定・保守的なロールバックを追加しました。接続先DCとスキーマGUIDを検証し、既存のセキュリティ設定を保持したまま不足する監査ACEだけをSACLに追加します。変更前のSDDLと追加ACEを保存し、書き込み後と最終状態を確認します。任意のdMSA前提条件が不明な場合は未確認のスキップ項目として報告し、独立した他の5種類のドメインクラスの監査ACEは引き続き設定します。実効監査ポリシー、継承・レプリケーション、4662/5136イベントの証拠は隔離DCで別途検証が必要です。Sigma検知範囲の向上は未検証です。 (#402) (@Shirofune-Security) - Microsoft WEF Appendix Cのチャネルを監査・計画・設定する任意実行の`channel-settings`を追加しました。CAPI2の有効化、原典の正確なバイト数、明示的に指定したEvent Log Readersの読み取りACEに対応します。既存のセキュリティ記述子・ACEと大きいバッファを保持し、安全に扱えないACLは変更しません。共通の復旧記録、書き込み直前の状態確認と読み戻しで失敗を報告します。Appendix E/Fの標準チャネル一覧からSysmonとEMETを除外し、実際のIDによるアクセス・イベント生成・収集は未検証と表示します。Windowsラボでの検証は別途必要です。 (#401) (@Shirofune-Security) diff --git a/website/docs/resources/changelog.md b/website/docs/resources/changelog.md index 87fc44a5..f7c7e52c 100644 --- a/website/docs/resources/changelog.md +++ b/website/docs/resources/changelog.md @@ -9,6 +9,7 @@ - Added explicit CIS v4.0.0 Level 2 Windows PowerShell 5.1 transcription audit, plan and configure actions. An operator-selected existing output directory is checked and reported separately from policy; typed canonical registry writes are journaled, verified through shared 32/64-bit views and checked for drift while preserving invocation-header preferences. No ACL/share/retention changes or automatic Sigma EVTX credit are introduced; disposable native transcript tests restore original policy, and central authorization/collection remains a deployment check. (#405) (@Shirofune-Security) - Preserved LDAP 1644 diagnostics during normal configuration instead of automatically enabling Field Engineering level 5 on DCs. Added explicit `ldap-diagnostics` audit/plan/configure modes for preservation, tunable diagnostics and MDI legacy cleanup, with role/build checks, typed recovery snapshots, race guards, ordered readback and final drift checks. LDAP-only options are rejected before unrelated profile commands can run. Generated events, volume and forwarding remain isolated-DC validation. (#404) (@Shirofune-Security) +- Corrected the legacy Token Right Adjusted Events GUID so RPC and token auditing are assessed independently in every baseline. Added runtime catalog identity/alias checks and a read-only, fingerprinted EventID mapping review that preserves ambiguous, category-only and unknown candidates without detection credit. (#403) (@Shirofune-Security) - Added opt-in `ad-object-sacl` audit, plan, configure and conservative rollback actions for MDI domain/Exchange Configuration auditing and explicitly selected certificate template/enrollment service objects. Exact DC binding, schema GUID checks, additive SACL-only changes, pre-write SDDL/ACE receipts and read-back preserve existing security entries. Unknown optional dMSA prerequisites are reported as a separate skipped gap while the five independent domain class ACEs continue. Effective audit policy, inheritance/replication and 4662/5136 event evidence remain separate isolated-DC checks; no Sigma uplift is claimed. (#402) (@Shirofune-Security) - Added opt-in `channel-settings` audit, plan and configure actions for Microsoft WEF Appendix C, including CAPI2 enablement, exact source byte sizes and an explicitly requested Event Log Readers read ACE. Existing descriptor components/ACEs and larger buffers are preserved or unsafe ACL edits are refused; shared journaling, fresh-state guards and readback report failures. Native Appendix E/F channel inventories exclude Sysmon/EMET and retain unverified identity access, generation and ingestion prerequisites. Windows lab evidence remains pending. (#401) (@Shirofune-Security)