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 541fe6ed..df1efca2 100644 --- a/CHANGELOG-Japanese.md +++ b/CHANGELOG-Japanese.md @@ -6,6 +6,8 @@ - 読み取り専用の`rule-eligibility`を追加し、ルール・対応表のハッシュ、ルールごとの判定理由、対象外の理由、分子・分母を明示します。任意で取り込んだラボ資料を、対応範囲を限定した完全なルール定義、ネイティブXML、設定、収集・クエリ実行の証拠と照合し、未対応・未確認の項目はConditionalとします。監査のCSV/JSON/HTMLとNavigator出力では、設定が有効なだけでルールを利用可能と判定しません。取り込んだReady判定は記録された環境・時点に限られ、実環境での一連の検証を保証しません。 (#407) (@Shirofune-Security) - Windows標準のドメイン/Kerberos環境向けに、送信元設定と明示的に選択した収集サーバーのサブスクリプションを監査・計画・設定する任意実行の`wef-source`と`wec-collector`を追加しました。実際の収集先ID、既存リスナーと範囲を限定した受信規則、送信元SID、メンバーホストでの読み取り権限の追加、明示的なASDのWSMan強化設定と共通チャネル設定を、復旧記録・変更検出・読み戻しで確認します。DCのグループ管理権限と異なる既存サブスクリプションは変更しません。JSONにはクエリ・チャネル・実行状態の証拠を保持し、実効読み取り権限・イベント到着・転送後のSigma検知範囲は未検証と表示します。隔離Windows環境での配備検証は別途必要です。 (#406) (@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 6c520015..9a8616c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Added read-only `rule-eligibility` reports with pinned corpus/mapping hashes, per-rule reasons, explicit scope exclusions and numerator/denominator totals. Optional imported lab artifacts are checked against a narrow complete-rule parser, native XML, configuration, ingestion and query evidence; unsupported or incomplete cases stay Conditional. Audit CSV/JSON/HTML and Navigator outputs no longer treat enabled settings as proven usable rules. Imported Ready results apply only to their recorded context/time; no live end-to-end validation is implied. (#407) (@Shirofune-Security) - Added separate opt-in `wef-source` and `wec-collector` audit, plan and configure commands for native domain/Kerberos source settings and explicitly selected collector subscriptions. Actual collector identity, scoped existing ingress/listener prerequisites, explicit source SIDs, additive member-host read permissions, optional ASD WSMan hardening and shared channel controls are checked with journals, drift guards and readback. DC group authority and different existing subscriptions are preserved. JSON retains query/channel/runtime evidence without claiming effective read access, event arrival or forwarded Sigma coverage; isolated Windows deployment validation remains pending. (#406) (@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 93b59bd5..f060e8eb 100644 --- a/WELA.ps1 +++ b/WELA.ps1 @@ -63,6 +63,7 @@ $SaclTargetsPath = Join-Path $ScriptRoot "config/audit_sacl_targets.json" . (Join-Path $ScriptRoot "scripts/WmiNamespaceAuditing.ps1") Import-Module (Join-Path $ScriptRoot "modules/AuditProfiles.psm1") -ErrorAction Stop Import-Module (Join-Path $ScriptRoot "modules/RuleEligibility.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") @@ -335,7 +336,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 702806a2..68fcc27a 100644 --- a/tests/AuditProfileOutput.Tests.ps1 +++ b/tests/AuditProfileOutput.Tests.ps1 @@ -2,6 +2,7 @@ # injected audit observations. Only temporary files are written; no Windows policy changes. $ErrorActionPreference = 'Stop' Import-Module (Join-Path $PSScriptRoot '../modules/RuleEligibility.psm1') -Force +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 5b1aca4d..ed87c0cb 100644 --- a/tests/NativeProviders.Tests.ps1 +++ b/tests/NativeProviders.Tests.ps1 @@ -1,6 +1,8 @@ # Real catalog + public audit renderer/exports; Windows reads are injected at the OS boundary. $ErrorActionPreference = 'Stop' Import-Module (Join-Path $PSScriptRoot '../modules/RuleEligibility.psm1') -Force +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 ebf0c81d..b0fc6d0b 100644 --- a/website/docs/resources/changelog.ja.md +++ b/website/docs/resources/changelog.ja.md @@ -9,6 +9,8 @@ - 読み取り専用の`rule-eligibility`を追加し、ルール・対応表のハッシュ、ルールごとの判定理由、対象外の理由、分子・分母を明示します。任意で取り込んだラボ資料を、対応範囲を限定した完全なルール定義、ネイティブXML、設定、収集・クエリ実行の証拠と照合し、未対応・未確認の項目はConditionalとします。監査のCSV/JSON/HTMLとNavigator出力では、設定が有効なだけでルールを利用可能と判定しません。取り込んだReady判定は記録された環境・時点に限られ、実環境での一連の検証を保証しません。 (#407) (@Shirofune-Security) - Windows標準のドメイン/Kerberos環境向けに、送信元設定と明示的に選択した収集サーバーのサブスクリプションを監査・計画・設定する任意実行の`wef-source`と`wec-collector`を追加しました。実際の収集先ID、既存リスナーと範囲を限定した受信規則、送信元SID、メンバーホストでの読み取り権限の追加、明示的なASDのWSMan強化設定と共通チャネル設定を、復旧記録・変更検出・読み戻しで確認します。DCのグループ管理権限と異なる既存サブスクリプションは変更しません。JSONにはクエリ・チャネル・実行状態の証拠を保持し、実効読み取り権限・イベント到着・転送後のSigma検知範囲は未検証と表示します。隔離Windows環境での配備検証は別途必要です。 (#406) (@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 5ce33815..fe70dd57 100644 --- a/website/docs/resources/changelog.md +++ b/website/docs/resources/changelog.md @@ -9,6 +9,8 @@ - Added read-only `rule-eligibility` reports with pinned corpus/mapping hashes, per-rule reasons, explicit scope exclusions and numerator/denominator totals. Optional imported lab artifacts are checked against a narrow complete-rule parser, native XML, configuration, ingestion and query evidence; unsupported or incomplete cases stay Conditional. Audit CSV/JSON/HTML and Navigator outputs no longer treat enabled settings as proven usable rules. Imported Ready results apply only to their recorded context/time; no live end-to-end validation is implied. (#407) (@Shirofune-Security) - Added separate opt-in `wef-source` and `wec-collector` audit, plan and configure commands for native domain/Kerberos source settings and explicitly selected collector subscriptions. Actual collector identity, scoped existing ingress/listener prerequisites, explicit source SIDs, additive member-host read permissions, optional ASD WSMan hardening and shared channel controls are checked with journals, drift guards and readback. DC group authority and different existing subscriptions are preserved. JSON retains query/channel/runtime evidence without claiming effective read access, event arrival or forwarded Sigma coverage; isolated Windows deployment validation remains pending. (#406) (@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)