From ed16baca4675996ad4058ea5de69a588ef2b8db9 Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Tue, 18 Aug 2026 17:48:24 -0400 Subject: [PATCH 1/2] feat(detection): distinguish CA-shaped schema v1 templates in SchemaV1 findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add IsCATemplate property (pKIDefaultKeySpec -eq 2) via new Set-IsCATemplate, wired into Initialize-AdcsObjectStore - add Get-IssueTextOverride: generic Overrides mechanism (When property-equality → alternate Issue/Fix/Revert text) - migrate SchemaV1 CAOverride to Overrides; CA templates get leave-in-place guidance instead of supersession (#98) - tests: 4 Set-IsCATemplate, 6 Get-IssueTextOverride, 3 SchemaV1 CA-branch; 637/637 pass PS 7 (PS 5.1 failures pre-existing PSCertutil/PSWriteHTML stubs) --- .../schema-v1-ca-template-detection/spec.md | 14 ++- Classes/LS2AdcsObject.ps1 | 2 + Private/Data/ESCDefinitions.ps1 | 31 ++++++ .../Initialize/Initialize-AdcsObjectStore.ps1 | 1 + Private/Set/Set-IsCATemplate.ps1 | 49 +++++++++ Private/Utility/Get-IssueTextOverride.ps1 | 59 ++++++++++ Public/Find-LS2VulnerableTemplate.ps1 | 10 +- Tests/Private/Set/Set-IsCATemplate.Tests.ps1 | 74 +++++++++++++ .../Utility/Get-IssueTextOverride.Tests.ps1 | 102 ++++++++++++++++++ .../Find-LS2VulnerableTemplate.Tests.ps1 | 52 +++++++++ Tests/Shared/TestHelpers.psm1 | 1 + 11 files changed, 387 insertions(+), 8 deletions(-) create mode 100644 Private/Set/Set-IsCATemplate.ps1 create mode 100644 Private/Utility/Get-IssueTextOverride.ps1 create mode 100644 Tests/Private/Set/Set-IsCATemplate.Tests.ps1 create mode 100644 Tests/Private/Utility/Get-IssueTextOverride.Tests.ps1 diff --git a/.scratch/schema-v1-ca-template-detection/spec.md b/.scratch/schema-v1-ca-template-detection/spec.md index 3330916..8ed453d 100644 --- a/.scratch/schema-v1-ca-template-detection/spec.md +++ b/.scratch/schema-v1-ca-template-detection/spec.md @@ -19,14 +19,18 @@ Locksmith2 distinguishes CA-shaped schema v1 certificate templates from end-enti ## Decisions so far -_None recorded yet._ +- Add a new `IsCATemplate` property ([bool], defaults `$false`) to `LS2AdcsObject`. Rationale: every synthetic property in the class (`Enabled`, `AuthenticationEKUExist`, `DangerousEditor`, etc.) is declared on the class and set by a `Set-*` function — computed inline in Find functions is not an established pattern, and PS class properties cannot be ad-hoc added later without `Add-Member` hacks. +- Wire enrichment via a new `Set-IsCATemplate` function in `Private/Set/`, inserted into the existing template pipeline in `Initialize-AdcsObjectStore`. Rationale: one-function-per-file rule, matches the `Set-TemplateEnabled` precedent, and keeps Find functions data-only. Implementation: `$_.IsCATemplate = ($_.pKIDefaultKeySpec -eq 2)` for template objects. +- Do NOT change SchemaV1 `Conditions`. The property does not affect which templates match the technique — CA-shaped schema v1 templates are still findings, just with different remediation text. Branching belongs in the Find function/issue text, not the data Conditions. +- Find function: in the SchemaV1 branch of `Find-LS2VulnerableTemplate`, select Issue/Fix/Revert text based on `$template.IsCATemplate`. Since `ESCDefinitions.ps1` holds one IssueTemplate per technique, add a sibling key (e.g. `CAOverride = @{ IssueTemplate = ...; FixTemplate = ...; RevertTemplate = ... }`) inside the SchemaV1 entry rather than a new top-level technique — keeps the technique list clean and the data-driven pattern intact. ## Not yet specified -- Exact wording of the CA-shaped schema v1 template issue/fix/revert text. -- Whether to add a new computed property (`IsCATemplate`) to `LS2AdcsObject` or compute it inline in `Find-LS2VulnerableTemplate`. -- Where to wire the enrichment (existing `Set-*` pipeline vs. a new `Set-IsCATemplate` function). -- Test coverage: unit tests for `Set-IsCATemplate` (if created) and `Find-LS2VulnerableTemplate` SchemaV1 branch for both CA and non-CA schema v1 templates. +- Test coverage: unit tests for `Set-IsCATemplate` and `Find-LS2VulnerableTemplate` SchemaV1 branch for both CA and non-CA schema v1 templates. + +### CA-shaped wording decision + +Leave the CA template in place. Issue text states that replacing SubCA certificates requires planning and testing rather than simple supersession; Fix script contains no supersession instructions — awareness/documentation only. ## Out of scope diff --git a/Classes/LS2AdcsObject.ps1 b/Classes/LS2AdcsObject.ps1 index 93795fd..23769da 100644 --- a/Classes/LS2AdcsObject.ps1 +++ b/Classes/LS2AdcsObject.ps1 @@ -70,6 +70,7 @@ [bool]$AuditingIncomplete [object[]]$DisableExtensionList [Nullable[bool]]$SecurityExtensionDisabled + [bool]$IsCATemplate [object[]]$WebEnrollmentEndpoints [Nullable[bool]]$HasLinkedGroupOIDPolicy # true when ≥1 CertificatePolicy OID links to a group [string[]]$LinkedGroupOIDPolicies # group DNs linked via OID application policies @@ -166,6 +167,7 @@ $this.SANFlagEnabled = $null $this.AuditFilter = $null $this.DisableExtensionList = @() + $this.IsCATemplate = $false # Initialize CA-specific properties $this.CAAdministrators = @() diff --git a/Private/Data/ESCDefinitions.ps1 b/Private/Data/ESCDefinitions.ps1 index 7f69803..1918317 100644 --- a/Private/Data/ESCDefinitions.ps1 +++ b/Private/Data/ESCDefinitions.ps1 @@ -819,6 +819,37 @@ $script:ESCDefinitions = data { "# No automated revert. Template schema version cannot be changed via script." "# If you superseded this template, re-enable the old template and remove the superseding relationship." ) + + # Override for CA-shaped schema v1 templates (pKIDefaultKeySpec -eq 2). + # Supersession has not been observed to work reliably for CA templates in live + # environments, so CA-shaped templates get different remediation guidance. + Overrides = @( + @{ + When = @{ Property = 'IsCATemplate'; Value = $true } + IssueTemplate = @( + "The certificate template `$(TemplateName) is a CA template that uses schema version 1.`n`n" + "Schema v1 templates were introduced in Windows 2000 and lack several security features " + "available in later schema versions. Certificates issued from schema v1 templates do not " + "include the CA security extension (szOID_NTDS_CA_SECURITY_EXT), reducing their compatibility " + "with strong certificate mapping requirements.`n`n" + "Because this template issues CA certificates, it cannot be safely superseded like an " + "end-entity template — supersession has not been observed to work reliably for CA templates " + "in live environments. Replacing SubCA certificates requires planning and testing rather " + "than simple supersession. Leave this template in place until a tested replacement plan exists." + ) + FixTemplate = @( + "# No automated fix. This is a CA-shaped schema v1 template." + "# Do NOT supersede this template — supersession is unreliable for CA templates." + "# Replacing SubCA certificates requires planning and testing:" + "# 1. Inventory all CAs and subordinate CAs issued from this template." + "# 2. Plan CA certificate renewal/replacement against a schema v2+ CA template." + "# 3. Test the replacement process in a lab before production changes." + ) + RevertTemplate = @( + "# No action taken, no revert required." + ) + } + ) } } } diff --git a/Private/Initialize/Initialize-AdcsObjectStore.ps1 b/Private/Initialize/Initialize-AdcsObjectStore.ps1 index a8604b4..1fbf12d 100644 --- a/Private/Initialize/Initialize-AdcsObjectStore.ps1 +++ b/Private/Initialize/Initialize-AdcsObjectStore.ps1 @@ -80,6 +80,7 @@ Set-ManagerApprovalNotRequired | Set-AuthorizedSignatureNotRequired | Set-TemplateEnabled | + Set-IsCATemplate | Set-Owner | Set-HasNonStandardOwner diff --git a/Private/Set/Set-IsCATemplate.ps1 b/Private/Set/Set-IsCATemplate.ps1 new file mode 100644 index 0000000..0d3a9df --- /dev/null +++ b/Private/Set/Set-IsCATemplate.ps1 @@ -0,0 +1,49 @@ +function Set-IsCATemplate { + <# + .SYNOPSIS + Adds the IsCATemplate property to AD CS certificate template objects. + + .DESCRIPTION + Examines the pKIDefaultKeySpec attribute of certificate template objects to + determine whether a template is CA-shaped (intended to issue CA certificates). + + A template is considered CA-shaped when pKIDefaultKeySpec -eq 2 (AT_SIGNATURE), + which is the key specification used for CA signing keys. End-entity templates + use pKIDefaultKeySpec -eq 1 (AT_KEYEXCHANGE) or leave the attribute unset. + + This distinction matters for remediation guidance: superseding a schema v1 + end-entity template is routine, but supersession has not been observed to + work reliably for CA templates in live environments. + + .PARAMETER AdcsObject + One or more LS2AdcsObject objects representing AD CS objects. + + .INPUTS + LS2AdcsObject objects. + + .OUTPUTS + LS2AdcsObject objects with the IsCATemplate property set. + + .EXAMPLE + $templates | Set-IsCATemplate + Sets IsCATemplate on all certificate template objects. + + .NOTES + pKIDefaultKeySpec values: + 1 = AT_KEYEXCHANGE (end-entity) + 2 = AT_SIGNATURE (CA-shaped) + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory, ValueFromPipeline)] + [LS2AdcsObject[]]$AdcsObject + ) + + process { + $AdcsObject | Where-Object SchemaClassName -EQ 'pKICertificateTemplate' | ForEach-Object { + $_.IsCATemplate = ($_.pKIDefaultKeySpec -eq 2) + Write-Verbose "Template '$($_.cn)': IsCATemplate = $($_.IsCATemplate)" + $_ + } + } +} diff --git a/Private/Utility/Get-IssueTextOverride.ps1 b/Private/Utility/Get-IssueTextOverride.ps1 new file mode 100644 index 0000000..57736a4 --- /dev/null +++ b/Private/Utility/Get-IssueTextOverride.ps1 @@ -0,0 +1,59 @@ +function Get-IssueTextOverride { + <# + .SYNOPSIS + Selects issue text overrides for an AD CS object based on technique Overrides. + + .DESCRIPTION + Technique definitions in ESCDefinitions.ps1 may carry an Overrides array. Each + override specifies a single When condition (property equality) and alternate + IssueTemplate/FixTemplate/RevertTemplate text. + + This function returns the first override whose When condition matches the + object, or the base config when no override matches. Callers use the returned + hashtable's IssueTemplate/FixTemplate/RevertTemplate in place of the base + config's text. + + Example override shape: + Overrides = @( + @{ + When = @{ Property = 'IsCATemplate'; Value = $true } + IssueTemplate = '...' + FixTemplate = '...' + RevertTemplate = '...' + } + ) + + .PARAMETER Config + A technique definition hashtable from $script:ESCDefinitions. + + .PARAMETER AdcsObject + The LS2AdcsObject being evaluated. + + .OUTPUTS + Hashtable — the matching override merged into the issue-text role, or Config unchanged. + + .EXAMPLE + $textConfig = Get-IssueTextOverride -Config $config -AdcsObject $template + + .NOTES + Overrides only carry text. Placeholder expansion remains the caller's job. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [hashtable]$Config, + + [Parameter(Mandatory)] + [LS2AdcsObject]$AdcsObject + ) + + foreach ($override in @($Config.Overrides)) { + if ($null -eq $override.When) { continue } + if ($AdcsObject.($override.When.Property) -eq $override.When.Value) { + Write-Verbose "Override matched on $($override.When.Property) = $($override.When.Value)" + return $override + } + } + + return $Config +} diff --git a/Public/Find-LS2VulnerableTemplate.ps1 b/Public/Find-LS2VulnerableTemplate.ps1 index afcc1c5..b5b6d34 100644 --- a/Public/Find-LS2VulnerableTemplate.ps1 +++ b/Public/Find-LS2VulnerableTemplate.ps1 @@ -374,11 +374,15 @@ $forestName = Get-ForestNameFromDN -DistinguishedName $template.distinguishedName - $issueText = ($config.IssueTemplate -join '') ` + # Resolve any technique Overrides (e.g., CA-shaped schema v1 templates get + # alternate text — supersession is unreliable for CA templates) + $issueConfig = Get-IssueTextOverride -Config $config -AdcsObject $template + + $issueText = ($issueConfig.IssueTemplate -join '') ` -replace '\$\(TemplateName\)', $templateName - $fixScript = ($config.FixTemplate -join "`n") - $revertScript = ($config.RevertTemplate -join "`n") + $fixScript = ($issueConfig.FixTemplate -join "`n") + $revertScript = ($issueConfig.RevertTemplate -join "`n") $issue = [LS2Issue]@{ Technique = $Technique diff --git a/Tests/Private/Set/Set-IsCATemplate.Tests.ps1 b/Tests/Private/Set/Set-IsCATemplate.Tests.ps1 new file mode 100644 index 0000000..88aa120 --- /dev/null +++ b/Tests/Private/Set/Set-IsCATemplate.Tests.ps1 @@ -0,0 +1,74 @@ +BeforeDiscovery { + $ModuleRoot = Split-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Parent + $ls2Manifest = if ($env:LS2_MODULE_ROOT) { Join-Path $env:LS2_MODULE_ROOT 'Locksmith2.psd1' } else { Join-Path $ModuleRoot 'Locksmith2.psd1' } + Import-Module $ls2Manifest -Force -ErrorAction Stop +} +BeforeAll { + $ModuleRoot = Split-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Parent + $ls2Manifest = if ($env:LS2_MODULE_ROOT) { Join-Path $env:LS2_MODULE_ROOT 'Locksmith2.psd1' } else { Join-Path $ModuleRoot 'Locksmith2.psd1' } + Import-Module $ls2Manifest -Force -ErrorAction Stop + Import-Module (Join-Path $ModuleRoot 'Tests\Shared\TestHelpers.psm1') -Force -ErrorAction Stop +} + +Describe 'Set-IsCATemplate' -Tag 'Unit' { + InModuleScope 'Locksmith2' { + BeforeEach { + $script:IssueStore = @{}; $script:PrincipalStore = @{}; $script:AdcsObjectStore = @{} + $script:DomainStore = @{}; $script:SafePrincipals = @(); $script:DangerousPrincipals = @() + $script:StandardOwners = @(); $script:DangerousAces = $null; $script:InitializingStores = $false + $script:RootDSE = $null; $script:Server = $null; $script:Forest = $null; $script:Credential = $null + } + + Context 'Non-template objects' { + It 'should not set IsCATemplate on non-template objects' { + $ca = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKIEnrollmentService' + cn = 'MyCA' + } + + $result = $ca | Set-IsCATemplate + + $result.IsCATemplate | Should -BeFalse + } + } + + Context 'CA-shaped templates' { + It 'should set IsCATemplate=$true when pKIDefaultKeySpec is 2 (AT_SIGNATURE)' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + cn = 'SubCA' + pKIDefaultKeySpec = 2 + } + + $result = $template | Set-IsCATemplate + + $result.IsCATemplate | Should -BeTrue + } + } + + Context 'End-entity templates' { + It 'should set IsCATemplate=$false when pKIDefaultKeySpec is 1 (AT_KEYEXCHANGE)' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + cn = 'WebServer' + pKIDefaultKeySpec = 1 + } + + $result = $template | Set-IsCATemplate + + $result.IsCATemplate | Should -BeFalse + } + + It 'should set IsCATemplate=$false when pKIDefaultKeySpec is null' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + cn = 'WebServer' + } + + $result = $template | Set-IsCATemplate + + $result.IsCATemplate | Should -BeFalse + } + } + } +} diff --git a/Tests/Private/Utility/Get-IssueTextOverride.Tests.ps1 b/Tests/Private/Utility/Get-IssueTextOverride.Tests.ps1 new file mode 100644 index 0000000..c3c4837 --- /dev/null +++ b/Tests/Private/Utility/Get-IssueTextOverride.Tests.ps1 @@ -0,0 +1,102 @@ +BeforeDiscovery { + $ModuleRoot = Split-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Parent + $ls2Manifest = if ($env:LS2_MODULE_ROOT) { Join-Path $env:LS2_MODULE_ROOT 'Locksmith2.psd1' } else { Join-Path $ModuleRoot 'Locksmith2.psd1' } + Import-Module $ls2Manifest -Force -ErrorAction Stop +} +BeforeAll { + $ModuleRoot = Split-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) -Parent + $ls2Manifest = if ($env:LS2_MODULE_ROOT) { Join-Path $env:LS2_MODULE_ROOT 'Locksmith2.psd1' } else { Join-Path $ModuleRoot 'Locksmith2.psd1' } + Import-Module $ls2Manifest -Force -ErrorAction Stop + Import-Module (Join-Path $ModuleRoot 'Tests\Shared\TestHelpers.psm1') -Force -ErrorAction Stop +} + +Describe 'Get-IssueTextOverride' -Tag 'Unit' { + InModuleScope 'Locksmith2' { + BeforeAll { + $script:BaseConfig = @{ + Technique = 'SchemaV1' + IssueTemplate = 'base issue' + FixTemplate = 'base fix' + RevertTemplate = 'base revert' + Overrides = @( + @{ + When = @{ Property = 'IsCATemplate'; Value = $true } + IssueTemplate = 'override issue' + FixTemplate = 'override fix' + RevertTemplate = 'override revert' + } + ) + } + } + + Context 'No Overrides key on config' { + It 'should return the base config when Overrides is absent' { + $config = @{ IssueTemplate = 'base issue' } + $template = New-MockLS2AdcsObject + + $result = Get-IssueTextOverride -Config $config -AdcsObject $template + + $result.IssueTemplate | Should -Be 'base issue' + } + } + + Context 'Empty Overrides array' { + It 'should return the base config when Overrides is empty' { + $config = @{ IssueTemplate = 'base issue'; Overrides = @() } + $template = New-MockLS2AdcsObject + + $result = Get-IssueTextOverride -Config $config -AdcsObject $template + + $result.IssueTemplate | Should -Be 'base issue' + } + } + + Context 'When condition matches' { + It 'should return the override text when the property equals the value' { + $template = New-MockLS2AdcsObject -Properties @{ IsCATemplate = $true } + + $result = Get-IssueTextOverride -Config $script:BaseConfig -AdcsObject $template + + $result.IssueTemplate | Should -Be 'override issue' + $result.FixTemplate | Should -Be 'override fix' + $result.RevertTemplate | Should -Be 'override revert' + } + } + + Context 'When condition does not match' { + It 'should return the base config when the property differs' { + $template = New-MockLS2AdcsObject -Properties @{ IsCATemplate = $false } + + $result = Get-IssueTextOverride -Config $script:BaseConfig -AdcsObject $template + + $result.IssueTemplate | Should -Be 'base issue' + } + + It 'should return the base config when the property is null' { + $template = New-MockLS2AdcsObject + $template.IsCATemplate = $false + + $result = Get-IssueTextOverride -Config $script:BaseConfig -AdcsObject $template + + $result.FixTemplate | Should -Be 'base fix' + } + } + + Context 'Multiple overrides' { + It 'should return the first matching override' { + $config = @{ + IssueTemplate = 'base' + Overrides = @( + @{ When = @{ Property = 'IsCATemplate'; Value = $true }; IssueTemplate = 'first' } + @{ When = @{ Property = 'IsCATemplate'; Value = $true }; IssueTemplate = 'second' } + ) + } + $template = New-MockLS2AdcsObject -Properties @{ IsCATemplate = $true } + + $result = Get-IssueTextOverride -Config $config -AdcsObject $template + + $result.IssueTemplate | Should -Be 'first' + } + } + } +} diff --git a/Tests/Public/Find-LS2VulnerableTemplate.Tests.ps1 b/Tests/Public/Find-LS2VulnerableTemplate.Tests.ps1 index 6cef218..1d2d15e 100644 --- a/Tests/Public/Find-LS2VulnerableTemplate.Tests.ps1 +++ b/Tests/Public/Find-LS2VulnerableTemplate.Tests.ps1 @@ -486,6 +486,58 @@ InModuleScope 'Locksmith2' { $result.Count | Should -Be 0 } + + It 'should recommend supersession for end-entity schema v1 templates' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + TemplateSchemaVersion = 1 + Enabled = $true + AuthenticationEKUExist = $false + IsCATemplate = $false + distinguishedName = 'CN=EndEntityV1,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com' + Name = 'EndEntityV1' + } + $script:AdcsObjectStore = @{ $template.distinguishedName = $template } + + $result = @(Find-LS2VulnerableTemplate -Technique 'SchemaV1') + + $result[0].Fix | Should -Match 'supersede' + } + + It 'should not recommend supersession for CA-shaped schema v1 templates' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + TemplateSchemaVersion = 1 + Enabled = $true + AuthenticationEKUExist = $false + IsCATemplate = $true + distinguishedName = 'CN=SubCAV1,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com' + Name = 'SubCAV1' + } + $script:AdcsObjectStore = @{ $template.distinguishedName = $template } + + $result = @(Find-LS2VulnerableTemplate -Technique 'SchemaV1') + + # CA-shaped templates must not contain supersession instructions + $result[0].Fix | Should -Not -Match 'configure the old template to be superseded' + } + + It 'should mention planning and testing in the issue text for CA-shaped schema v1 templates' { + $template = New-MockLS2AdcsObject -Properties @{ + SchemaClassName = 'pKICertificateTemplate' + TemplateSchemaVersion = 1 + Enabled = $true + AuthenticationEKUExist = $false + IsCATemplate = $true + distinguishedName = 'CN=SubCAV1,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=contoso,DC=com' + Name = 'SubCAV1' + } + $script:AdcsObjectStore = @{ $template.distinguishedName = $template } + + $result = @(Find-LS2VulnerableTemplate -Technique 'SchemaV1') + + $result[0].Issue | Should -Match 'planning and testing' + } } } } diff --git a/Tests/Shared/TestHelpers.psm1 b/Tests/Shared/TestHelpers.psm1 index 692745f..dcf0083 100644 --- a/Tests/Shared/TestHelpers.psm1 +++ b/Tests/Shared/TestHelpers.psm1 @@ -109,6 +109,7 @@ function New-MockLS2AdcsObject { $obj.AuditingIncomplete = $false $obj.DisableExtensionList = @() $obj.SecurityExtensionDisabled = $null + $obj.IsCATemplate = $false $obj.HasLinkedGroupOIDPolicy = $null $obj.LinkedGroupOIDPolicies = @() From 7944261b88048bb5ab00689fb940ab63f19af93b Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Tue, 18 Aug 2026 18:03:37 -0400 Subject: [PATCH 2/2] fix: standardize comment formatting in ESCDefinitions.ps1 --- Private/Data/ESCDefinitions.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Private/Data/ESCDefinitions.ps1 b/Private/Data/ESCDefinitions.ps1 index 1918317..71b2754 100644 --- a/Private/Data/ESCDefinitions.ps1 +++ b/Private/Data/ESCDefinitions.ps1 @@ -257,7 +257,7 @@ $script:ESCDefinitions = data { " - https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53" ) - # Fix script template (quick mitigation — Manager Approval) + # Fix script template (quick mitigation - Manager Approval) FixTemplate = @( "# Quick mitigation: Enable Manager Approval to require approval before certificate issuance" "`$Object = '`$(DistinguishedName)'" @@ -713,7 +713,7 @@ $script:ESCDefinitions = data { } ESC15 = @{ - # ESC15: Enabled schema v1 template with auth EKU — bypasses strong certificate mapping + # ESC15: Enabled schema v1 template with auth EKU - bypasses strong certificate mapping # (szOID_NTDS_CA_SECURITY_EXT is absent in schema v1 certificates) Technique = 'ESC15' @@ -788,7 +788,7 @@ $script:ESCDefinitions = data { } SchemaV1 = @{ - # SchemaV1: Enabled schema v1 template without client auth EKU — informational hygiene finding + # SchemaV1: Enabled schema v1 template without client auth EKU - informational hygiene finding Technique = 'SchemaV1' Conditions = @( @@ -833,13 +833,13 @@ $script:ESCDefinitions = data { "include the CA security extension (szOID_NTDS_CA_SECURITY_EXT), reducing their compatibility " "with strong certificate mapping requirements.`n`n" "Because this template issues CA certificates, it cannot be safely superseded like an " - "end-entity template — supersession has not been observed to work reliably for CA templates " + "end-entity template - supersession has not been observed to work reliably for CA templates " "in live environments. Replacing SubCA certificates requires planning and testing rather " "than simple supersession. Leave this template in place until a tested replacement plan exists." ) FixTemplate = @( "# No automated fix. This is a CA-shaped schema v1 template." - "# Do NOT supersede this template — supersession is unreliable for CA templates." + "# Do NOT supersede this template - supersession is unreliable for CA templates." "# Replacing SubCA certificates requires planning and testing:" "# 1. Inventory all CAs and subordinate CAs issued from this template." "# 2. Plan CA certificate renewal/replacement against a schema v2+ CA template."