From 5be186f952792935f27d57ebc8b19138b4252b16 Mon Sep 17 00:00:00 2001 From: Shirofune-Security <43838376+Shirofune-Security@users.noreply.github.com> Date: Fri, 18 Sep 2026 21:58:33 +0900 Subject: [PATCH] Add six missing native audit subcategories with source-specific masks --- .../workflows/test-native-audit-controls.yml | 19 ++++++ config/audit_profiles.json | 65 +++++++++++++++++- docs/audit-profiles.md | 2 +- tests/NativeAuditControls.Tests.ps1 | 68 +++++++++++++++++++ .../docs/commands/native-audit-controls.md | 45 ++++++++++++ 5 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/test-native-audit-controls.yml create mode 100644 tests/NativeAuditControls.Tests.ps1 create mode 100644 website/docs/commands/native-audit-controls.md diff --git a/.github/workflows/test-native-audit-controls.yml b/.github/workflows/test-native-audit-controls.yml new file mode 100644 index 00000000..02faeac6 --- /dev/null +++ b/.github/workflows/test-native-audit-controls.yml @@ -0,0 +1,19 @@ +name: Native audit control regressions +on: + push: + branches: ['**'] + pull_request: + workflow_dispatch: +permissions: + contents: read +jobs: + test: + runs-on: windows-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Verify native policy masks and dependencies (Windows PowerShell 5.1) + shell: powershell + run: ./tests/NativeAuditControls.Tests.ps1 + - name: Verify native policy masks and dependencies (PowerShell 7) + shell: pwsh + run: ./tests/NativeAuditControls.Tests.ps1 diff --git a/config/audit_profiles.json b/config/audit_profiles.json index 4a467042..cc16eb39 100644 --- a/config/audit_profiles.json +++ b/config/audit_profiles.json @@ -98,7 +98,7 @@ "DomainController", "ADCS" ], - "prerequisites": "" + "prerequisites": "Only generates application-group management evidence when the corresponding application-group activity occurs." }, { "id": "Computer Account Management", @@ -509,7 +509,7 @@ "DomainController", "ADCS" ], - "prerequisites": "A matching kernel-object SACL is required; enabling the subcategory alone does not generate object events." + "prerequisites": "Requires a matching target-object SACL and access rights/type. Enabling this audit policy does not install kernel-object SACLs or guarantee detection coverage." }, { "id": "Other Object Access Events", @@ -906,10 +906,69 @@ "mode": "optional", "mask": 3, "note": "Opt-in subcategory prerequisite only. Use configure-sacl separately for targeted file/registry SACLs." + }, + "Group Membership": { + "mode": "exact", + "mask": 1, + "sourceIds": [ + "ms-sct", + "cis-client", + "cis-server", + "asd" + ], + "evidence": "CIS v4 17.5.2; Microsoft SCT; ASD native Group Membership Success." + }, + "Application Group Management": { + "mode": "exact", + "mask": 3, + "sourceIds": [ + "cis-client", + "cis-server" + ], + "evidence": "CIS v4 17.2.1: Success and Failure." + }, + "Authorization Policy Change": { + "mode": "exact", + "mask": 1, + "sourceIds": [ + "cis-client", + "cis-server", + "ms-sct" + ], + "evidence": "CIS v4 17.7.3 and Server 2025 SCT: Success. The separate WEF profile retains Success and Failure." + }, + "MPSSVC Rule-Level Policy Change": { + "mode": "exact", + "mask": 3, + "sourceIds": [ + "ms-sct", + "cis-client", + "cis-server", + "ms-wef" + ], + "evidence": "CIS v4 17.7.4; Microsoft SCT and WEF: Success and Failure." + }, + "IPsec Driver": { + "mode": "exact", + "mask": 3, + "sourceIds": [ + "cis-client", + "cis-server", + "ms-audit" + ], + "evidence": "CIS v4 17.9.1 and Microsoft generic audit recommendation: Success and Failure." + }, + "Kernel Object": { + "mode": "exact", + "mask": 3, + "sourceIds": [ + "asd" + ], + "evidence": "ASD native audit policy: Success and Failure; matching object SACLs are a separate prerequisite." } }, "roleOverrides": {}, - "note": "Shared replacement for the 34-policy configure list. Policies irrelevant to the selected role are not applied. The three targeted SACL prerequisites are opt-in." + "note": "WELA native audit-policy profile: original configure controls plus six source-backed baseline gaps. Role-specific controls are not applied outside their roles. File/Registry/Handle optional policies and object SACL dependencies remain separate. Source profiles retain their own exact/minimum masks." }, { "id": "windows-defaults-reviewed-2026-09", diff --git a/docs/audit-profiles.md b/docs/audit-profiles.md index 8437ad62..5710c9d5 100644 --- a/docs/audit-profiles.md +++ b/docs/audit-profiles.md @@ -31,7 +31,7 @@ The WELA and documentary guide profiles currently cover the reviewed Windows 11/ | Profile | Version / meaning | | --- | --- | -| `wela-2.2.0` | Reviewed WELA development snapshot `8ef938f0966e86adc527395f50f907c43e843d1e`; retains the 34 existing success/failure policies, with irrelevant roles skipped and three SACL prerequisites optional | +| `wela-2.2.0` | Reviewed WELA development snapshot `8ef938f0966e86adc527395f50f907c43e843d1e`; extends the 34 existing policies with [six native audit controls](../website/docs/commands/native-audit-controls.md), with irrelevant roles skipped and three SACL prerequisites optional | | `windows-defaults-reviewed-2026-09` | Documentary effective-default model; **reference only**, cannot be applied or used to reset an OS | | `microsoft-sct-win11-24h2`, `microsoft-sct-win11-25h2` | Official SCT Policy Analyzer settings, exact masks | | `microsoft-sct-server2022`, `microsoft-sct-server2025-2602` | Official SCT member/DC settings; AD CS uses the member-server baseline | diff --git a/tests/NativeAuditControls.Tests.ps1 b/tests/NativeAuditControls.Tests.ps1 new file mode 100644 index 00000000..d2002dd5 --- /dev/null +++ b/tests/NativeAuditControls.Tests.ps1 @@ -0,0 +1,68 @@ +# Pure policy regressions. No Windows settings are read or changed. +$ErrorActionPreference = 'Stop' +Import-Module (Join-Path $PSScriptRoot '../modules/AuditProfiles.psm1') -Force +$config = Import-WelaAuditProfiles +$assertions = 0 +function Assert-Equal($Actual, $Expected, [string]$Message) { + if ($Actual -cne $Expected) { throw "$Message : expected '$Expected', got '$Actual'." } + $script:assertions++ +} +$expected = @{ + 'Group Membership' = @{ Guid = '0CCE9249-69AE-11D9-BED3-505054503030'; Mask = 1 } + 'Application Group Management' = @{ Guid = '0CCE9239-69AE-11D9-BED3-505054503030'; Mask = 3 } + 'Authorization Policy Change' = @{ Guid = '0CCE9231-69AE-11D9-BED3-505054503030'; Mask = 1 } + 'MPSSVC Rule-Level Policy Change' = @{ Guid = '0CCE9232-69AE-11D9-BED3-505054503030'; Mask = 3 } + 'IPsec Driver' = @{ Guid = '0CCE9213-69AE-11D9-BED3-505054503030'; Mask = 3 } + 'Kernel Object' = @{ Guid = '0CCE921F-69AE-11D9-BED3-505054503030'; Mask = 3 } +} +$current = @{} +foreach ($policy in $config.catalog) { $current[$policy.guid] = 0 } +foreach ($role in @('Client', 'MemberServer', 'DomainController', 'ADCS')) { + $plan = Get-WelaAuditProfilePlan -Profile 'wela-2.2.0' -Role $role -Build 26100 -Current $current + foreach ($name in $expected.Keys) { + $row = @($plan.policies | Where-Object { $_.id -eq $name }) + Assert-Equal $row.Count 1 "$role/$name has exactly one plan row" + Assert-Equal $row[0].guid $expected[$name].Guid "$role/$name canonical GUID" + Assert-Equal $row[0].targetMask $expected[$name].Mask "$role/$name applies the intended mask" + Assert-Equal ($row[0].sourceIds.Count -gt 0) $true "$role/$name retains provenance" + } + $kernel = $plan.policies | Where-Object { $_.id -eq 'Kernel Object' } + Assert-Equal ($kernel.prerequisites -match 'SACL') $true "$role kernel auditing reports object-SACL dependency" +} +# The WELA extension must not overwrite another guide's semantics. +$inherited = $current.Clone() +$inherited[$expected['Group Membership'].Guid] = 2 +$inherited[$expected['Authorization Policy Change'].Guid] = 2 +$cis = Get-WelaAuditProfilePlan -Profile 'cis-win11-v4-l1' -Role Client -Build 26100 -Current $inherited +foreach ($name in @('Group Membership', 'Authorization Policy Change')) { + $row = $cis.policies | Where-Object { $_.id -eq $name } + Assert-Equal $row.mode 'minimum' "CIS $name is a minimum" + Assert-Equal $row.targetMask 3 "CIS $name preserves inherited failure auditing" +} +$wef = Get-WelaAuditProfilePlan -Profile 'microsoft-wef-reviewed-2026-09' -Role Client -Build 26100 -Current $current +Assert-Equal (($wef.policies | Where-Object { $_.id -eq 'Authorization Policy Change' }).targetMask) 3 'WEF authorization auditing retains both outcomes' +$server = Get-WelaAuditProfilePlan -Profile 'microsoft-sct-server2025-2602' -Role MemberServer -Build 26100 -Current $current +Assert-Equal (($server.policies | Where-Object { $_.id -eq 'Authorization Policy Change' }).targetMask) 1 'Server 2025 SCT authorization target remains success' +$asd = Get-WelaAuditProfilePlan -Profile 'asd-native-2021-10' -Role Client -Build 26100 -Current $current +Assert-Equal (($asd.policies | Where-Object { $_.id -eq 'Kernel Object' }).targetMask) 3 'ASD kernel target remains both outcomes' +Assert-Equal (($asd.policies | Where-Object { $_.id -eq 'Detailed File Share' }).mode) 'not-configured' 'ASD detailed-share setting is not silently enabled' +# Exercise the actual shared apply path using an in-memory provider. +$script:policyState = $current.Clone() +$script:writes = @{} +$plan = Get-WelaAuditProfilePlan -Profile 'wela-2.2.0' -Role Client -Build 26100 -Current $script:policyState +$result = Invoke-WelaAuditProfilePlan -Plan $plan -ReadPolicy { $script:policyState } -WritePolicy { + param($Guid, $Mask) + $script:policyState[$Guid] = $Mask + $script:writes[$Guid] = $Mask +} -ReadContext { [pscustomobject]@{ Role = 'Client'; Build = 26100 } } -Confirm:$false +Assert-Equal $result.success $true 'Shared apply reports verified success with matching readback' +foreach ($name in $expected.Keys) { + Assert-Equal $script:writes[$expected[$name].Guid] $expected[$name].Mask "Apply requests correct $name mask" +} +$script:writes = @{} +$result = Invoke-WelaAuditProfilePlan -Plan $plan -ReadPolicy { $script:policyState } -WritePolicy { + param($Guid, $Mask) + $script:writes[$Guid] = $Mask +} -ReadContext { [pscustomobject]@{ Role = 'Client'; Build = 26100 } } -Confirm:$false +Assert-Equal $script:writes.Count 0 'Reapply is idempotent after all controls match' +Write-Host "PASS: $assertions native-audit-control assertions (mocked; no host changes)." diff --git a/website/docs/commands/native-audit-controls.md b/website/docs/commands/native-audit-controls.md new file mode 100644 index 00000000..a7e6a93a --- /dev/null +++ b/website/docs/commands/native-audit-controls.md @@ -0,0 +1,45 @@ +# Native audit controls and their prerequisites + +The WELA native profile supports the following audit subcategories in addition to +its original configure policy list. Source-specific profiles keep their own +success/failure masks; selecting a profile does not combine all guides globally. + +| Subcategory | WELA target | Other reviewed requirements | +| --- | --- | --- | +| Group Membership | Success | Microsoft SCT, CIS v4 and ASD native: Success; CIS specifies a minimum. | +| Application Group Management | Success and Failure | CIS v4 section 17.2.1: both outcomes. | +| Authorization Policy Change | Success | CIS v4 and Server 2025 SCT: Success; Microsoft WEF Appendix A: both outcomes. | +| MPSSVC Rule-Level Policy Change | Success and Failure | Microsoft SCT, CIS v4 and Microsoft WEF: both outcomes. | +| IPsec Driver | Success and Failure | CIS v4 and Microsoft generic audit guidance: both outcomes. | +| Kernel Object | Success and Failure | ASD native: both outcomes; matching object SACLs and access semantics are separate prerequisites. | + +The mask describes policy configuration, not a promise that every operation emits +both kinds of event. Event generation depends on Windows version, role, object +access, and whether the activity occurs. Application Group Management events are +only relevant when application groups are used. Group Membership events provide +logon group context; they do not substitute for Security Group Management events. +IPsec Driver auditing does not enable IPsec or define connection security rules. + +Enabling Kernel Object auditing does not create a matching audit ACE on every +object. The plan records this dependency; WELA must not count a rule as verified +solely because the auditpol setting is enabled. The existing `configure-sacl` +command handles selected file/registry targets, not arbitrary kernel objects or +AD directory objects. + +Use `plan` to inspect the selected profile and its source provenance before +applying it. The plan distinguishes exact and minimum masks, settings the source +leaves unconfigured, and controls that do not apply to the selected role/build. +Minimum Success or Failure requirements preserve the other effective audit bit. +The advanced-audit profile does not install Sysmon or change firewall enforcement. + +## Source versions + +- [Microsoft Security Compliance Toolkit](https://www.microsoft.com/en-us/download/details.aspx?id=55319): Windows 11 24H2/25H2 and Windows Server 2022/2025 v2602 packages. +- [Microsoft audit recommendations](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/plan/security-best-practices/audit-policy-recommendations). +- [Microsoft WEF Appendix A](https://learn.microsoft.com/en-us/windows/security/operating-system-security/device-management/use-windows-event-forwarding-to-assist-in-intrusion-detection). +- [ASD Windows event logging and forwarding](https://www.cyber.gov.au/business-government/detecting-responding-to-threats/event-logging/windows-event-logging-and-forwarding): 2021 publication, native fallback. +- CIS Windows 11 Enterprise and Windows Server 2022 **v4.0.0**: historical reviewed benchmarks, not a claim about current CIS requirements. The profile data records control numbers and source links. + +Tests exercise policy masks, source-profile differences and the object-auditing +dependency. They do not establish live event production or detection coverage; +validate those on the applicable Windows roles with representative benign events.