From 601123bae4f87b588630cac7cfd0d98c317e4683 Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 25 Aug 2025 15:29:56 -0400 Subject: [PATCH 01/10] feat: improved risk ratings for template + ESC5 issues --- Private/Set-RiskRating.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Private/Set-RiskRating.ps1 b/Private/Set-RiskRating.ps1 index c5e7d54..8c93809 100644 --- a/Private/Set-RiskRating.ps1 +++ b/Private/Set-RiskRating.ps1 @@ -364,6 +364,10 @@ function Set-RiskRating { $Principals += $OtherIssue.IdentityReference.Value $OtherIssueRisk += 1 } + else { + $Principals += $OtherIssue.IdentityReference.Value + $OtherIssueRisk += 0.1 + } $CheckedESC5Templates.$($OtherIssue.Name) = $Principals } # forech ($OtherIssue) if ($OtherIssueRisk -ge 2) { From dbbe65270cf29dcf6775f6e2b1c30a72857f8d19 Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 25 Aug 2025 15:30:48 -0400 Subject: [PATCH 02/10] chore: gardening. improved outro message, less complex dangerous rights --- Public/Invoke-Locksmith.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Public/Invoke-Locksmith.ps1 b/Public/Invoke-Locksmith.ps1 index 92cc1de..cc6c054 100644 --- a/Public/Invoke-Locksmith.ps1 +++ b/Public/Invoke-Locksmith.ps1 @@ -157,7 +157,7 @@ function Invoke-Locksmith { # GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object. # WriteProperty may or may not permit full control depending the specific property and AD object type. - $DangerousRights = 'GenericAll|WriteDacl|WriteOwner|WriteProperty' + $DangerousRights = 'GenericAll|Write' # Extended Key Usage for client authentication. A requirement for ESC3. $EnrollmentAgentEKU = '1\.3\.6\.1\.4\.1\.311\.20\.2\.1' @@ -320,11 +320,13 @@ function Invoke-Locksmith { [!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues identified in the environment. For more details including: - - DistinguishedName of impacted object(s) - - Remediation guidance and/or code + - Detailed Risk Rating + - General remediation guidance and/or code for all issues + - Custom remediation guidance and/or code for some issues! - Revert guidance and/or code (in case remediation breaks something!) + - Distinguished Name of impacted object(s) -Run Locksmith in Mode 1! +Try Mode 1! # Module version Invoke-Locksmith -Mode 1 @@ -385,5 +387,6 @@ Invoke-Locksmith -Mode 1 } } Write-Host 'Thank you for using ' -NoNewline - Write-Host "Locksmith <3`n" -ForegroundColor Magenta + Write-Host 'Locksmith <3 ' -ForegroundColor Magenta -NoNewline + Write-Host "(https://github.com/jakehildreth/Locksmith)`n" } From 7cb25fc4b5ea34408d8a14a3566136f199d2c8cf Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 25 Aug 2025 15:36:22 -0400 Subject: [PATCH 03/10] chore: fresh build --- Invoke-Locksmith.ps1 | 177 ++++++++++++++++++++++++------------------- Locksmith.psd1 | 2 +- Locksmith.psm1 | 8 +- 3 files changed, 102 insertions(+), 85 deletions(-) diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index 64e6476..946910d 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -2053,20 +2053,20 @@ function Get-CAHostObject { if ($Credential) { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { if ($_.CAHostDistinguishedName) { - Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential + Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential } else { - Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" + Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" } } } else { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { if ($_.CAHostDistinguishedName) { - Get-ADObject -Identity $_.CAHostDistinguishedName -Properties * -Server $ForestGC + Get-ADObject -Identity $_.CAHostDistinguishedName -Properties * -Server $ForestGC } else { - Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" + Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" } } } @@ -2232,7 +2232,7 @@ function Install-RSATADPowerShell { else { Write-Warning -Message "The ActiveDirectory PowerShell module is required for Locksmith, but is not installed. Please launch an elevated PowerShell session to have this module installed for you automatically." # The goal here is to exit the script without closing the PowerShell window. Need to test. - Return + return } } function Invoke-Remediation { @@ -2964,7 +2964,7 @@ function New-Dictionary { ReferenceUrls = @('https://github.com/jakehildreth/Locksmith', 'https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/designing-and-implementing-a-pki-part-i-design-and-planning/ba-p/396953') } ) - Return $Dictionary + return $Dictionary } function New-OutputPath { @@ -3039,7 +3039,7 @@ function Set-AdditionalCAProperty { begin { if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy') ) { if ($PSVersionTable.PSEdition -eq 'Desktop') { - $code = @" + $code = @' using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { @@ -3047,12 +3047,12 @@ function Set-AdditionalCAProperty { return true; } } -"@ +'@ Add-Type -TypeDefinition $code -Language CSharp [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } else { - Add-Type @" + Add-Type @' using System.Net; using System.Security.Cryptography.X509Certificates; using System.Net.Security; @@ -3061,7 +3061,7 @@ function Set-AdditionalCAProperty { return true; } } -"@ +'@ # Set the ServerCertificateValidationCallback [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [TrustAllCertsPolicy]::TrustAllCerts } @@ -3072,7 +3072,7 @@ function Set-AdditionalCAProperty { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { $CAEnrollmentEndpoint = @() #[array]$CAEnrollmentEndpoint = $_.'msPKI-Enrollment-Servers' | Select-String 'http.*' | ForEach-Object { $_.Matches[0].Value } - foreach ($directory in @("certsrv/", "$($_.Name)_CES_Kerberos/service.svc", "$($_.Name)_CES_Kerberos/service.svc/CES", "ADPolicyProvider_CEP_Kerberos/service.svc", "certsrv/mscep/")) { + foreach ($directory in @('certsrv/', "$($_.Name)_CES_Kerberos/service.svc", "$($_.Name)_CES_Kerberos/service.svc/CES", 'ADPolicyProvider_CEP_Kerberos/service.svc', 'certsrv/mscep/')) { $URL = "://$($_.dNSHostName)/$directory" try { $Auth = 'NTLM' @@ -3119,6 +3119,7 @@ function Set-AdditionalCAProperty { } } catch { + Write-Debug "There may have been an error or something nothing found. $_" } } } @@ -3134,10 +3135,10 @@ function Set-AdditionalCAProperty { $CAHostFQDN = (Get-ADObject -Filter { (Name -eq $CAHostName) -and (objectclass -eq 'computer') } -Properties DnsHostname -Server $ForestGC).DnsHostname } $ping = if ($CAHostFQDN) { - Test-Connection -ComputerName $CAHostFQDN -Count 1 -Quiet + Test-Connection -ComputerName $CAHostFQDN -Count 1 -Quiet } else { - Write-Warning "Unable to resolve $($_.Name) Fully Qualified Domain Name (FQDN)" + Write-Warning "Unable to resolve $($_.Name) Fully Qualified Domain Name (FQDN)" } if ($ping) { try { @@ -3303,18 +3304,20 @@ function Set-AdditionalTemplateProperty { [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects ) - $ADCSObjects | Where-Object objectClass -Match 'pKICertificateTemplate' -PipelineVariable template | ForEach-Object { - # Write-Host "[?] Checking if template `"$($template.Name)`" is Enabled on any Certification Authority." -ForegroundColor Blue - $Enabled = $false - $EnabledOn = @() - foreach ($ca in ($ADCSObjects | Where-Object objectClass -EQ 'pKIEnrollmentService')) { - if ($ca.certificateTemplates -contains $template.Name) { - $Enabled = $true - $EnabledOn += $ca.Name - } + process { + $ADCSObjects | Where-Object objectClass -Match 'pKICertificateTemplate' -PipelineVariable template | ForEach-Object { + # Write-Host "[?] Checking if template `"$($template.Name)`" is Enabled on any Certification Authority." -ForegroundColor Blue + $Enabled = $false + $EnabledOn = @() + foreach ($ca in ($ADCSObjects | Where-Object objectClass -EQ 'pKIEnrollmentService')) { + if ($ca.certificateTemplates -contains $template.Name) { + $Enabled = $true + $EnabledOn += $ca.Name + } - $template | Add-Member -NotePropertyName Enabled -NotePropertyValue $Enabled -Force - $template | Add-Member -NotePropertyName EnabledOn -NotePropertyValue $EnabledOn -Force + $template | Add-Member -NotePropertyName Enabled -NotePropertyValue $Enabled -Force + $template | Add-Member -NotePropertyName EnabledOn -NotePropertyValue $EnabledOn -Force + } } } } @@ -3706,6 +3709,10 @@ function Set-RiskRating { $Principals += $OtherIssue.IdentityReference.Value $OtherIssueRisk += 1 } + else { + $Principals += $OtherIssue.IdentityReference.Value + $OtherIssueRisk += 0.1 + } $CheckedESC5Templates.$($OtherIssue.Name) = $Principals } # forech ($OtherIssue) if ($OtherIssueRisk -ge 2) { @@ -3727,23 +3734,23 @@ function Set-RiskRating { switch ($Issue.objectClass) { # Being able to modify Root CA Objects is very bad. 'certificationAuthority' { - $RiskValue += 2; $RiskScoring += 'Root Certification Authority bject: +2' + $RiskValue += 2; $RiskScoring += 'Root Certification Authority bject: +2' } # Being able to modify Issuing CA Objects is also very bad. 'pKIEnrollmentService' { - $RiskValue += 2; $RiskScoring += 'Issuing Certification Authority Object: +2' + $RiskValue += 2; $RiskScoring += 'Issuing Certification Authority Object: +2' } # Being able to modify CA Hosts? Yeah... very bad. 'computer' { - $RiskValue += 2; $RiskScoring += 'Certification Authority Host Computer: +2' + $RiskValue += 2; $RiskScoring += 'Certification Authority Host Computer: +2' } # Being able to modify OIDs could result in ESC13 vulns. 'msPKI-Enterprise-Oid' { - $RiskValue += 1; $RiskScoring += 'OID: +1' + $RiskValue += 1; $RiskScoring += 'OID: +1' } # Being able to modify PKS containers is bad. 'container' { - $RiskValue += 1; $RiskScoring += 'Container: +1' + $RiskValue += 1; $RiskScoring += 'Container: +1' } } } @@ -3764,19 +3771,19 @@ function Set-RiskRating { # Convert Value to Name $RiskName = switch ($RiskValue) { { $_ -le 1 } { - 'Informational' + 'Informational' } 2 { - 'Low' + 'Low' } 3 { - 'Medium' + 'Medium' } 4 { - 'High' + 'High' } { $_ -ge 5 } { - 'Critical' + 'Critical' } } @@ -3837,9 +3844,9 @@ function Test-IsADAdmin { #> if ( # Need to test to make sure this checks domain groups and not local groups, particularly for 'Administrators' (reference SID instead of name?). - ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Domain Admins") -or - ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators") -or - ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Enterprise Admins") + ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Domain Admins") -or + ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators") -or + ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Enterprise Admins") ) { return $true } @@ -3887,7 +3894,7 @@ function Test-IsLocalAccountSession { $CurrentSID = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value $LocalSIDs = (Get-LocalUser).SID.Value if ($CurrentSID -in $LocalSIDs) { - Return $true + return $true } } @@ -3932,41 +3939,48 @@ function Test-IsMemberOfProtectedUsers { $User ) - Import-Module ActiveDirectory - - # Use the currently logged in user if none is specified - # Get the user from Active Directory - if (-not($User)) { - # These two are different types. Fixed by referencing $CheckUser.SID later, but should fix here by using one type. - $CurrentUser = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name).Split('\')[-1] - $CheckUser = Get-ADUser $CurrentUser -Properties primaryGroupID - } - else { - $CheckUser = Get-ADUser $User -Properties primaryGroupID + begin { + Import-Module ActiveDirectory } - # Get the Protected Users group by SID instead of by its name to ensure compatibility with any locale or language. - $DomainSID = (Get-ADDomain).DomainSID.Value - $ProtectedUsersSID = "$DomainSID-525" + process { + # Use the currently logged in user if none is specified + # Get the user from Active Directory + if (-not($User)) { + # These two are different types. Fixed by referencing $CheckUser.SID later, but should fix here by using one type. + $CurrentUser = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name).Split('\')[-1] + $CheckUser = Get-ADUser $CurrentUser -Properties primaryGroupID + } + else { + $CheckUser = Get-ADUser $User -Properties primaryGroupID + } - # Get members of the Protected Users group for the current domain. Recuse in case groups are nested in it. - $ProtectedUsers = Get-ADGroupMember -Identity $ProtectedUsersSID -Recursive | Select-Object -Unique + # Get the Protected Users group by SID instead of by its name to ensure compatibility with any locale or language. + $DomainSID = (Get-ADDomain).DomainSID.Value + $ProtectedUsersSID = "$DomainSID-525" - # Check if the current user is in the 'Protected Users' group - if ($ProtectedUsers.SID.Value -contains $CheckUser.SID) { - Write-Verbose "$($CheckUser.Name) ($($CheckUser.DistinguishedName)) is a member of the Protected Users group." - $true - } - else { - # Check if the user's PGID (primary group ID) is set to the Protected Users group RID (525). - if ( $CheckUser.primaryGroupID -eq '525' ) { + # Get members of the Protected Users group for the current domain. Recuse in case groups are nested in it. + $ProtectedUsers = Get-ADGroupMember -Identity $ProtectedUsersSID -Recursive | Select-Object -Unique + + # Check if the current user is in the 'Protected Users' group + if ($ProtectedUsers.SID.Value -contains $CheckUser.SID) { + Write-Verbose "$($CheckUser.Name) ($($CheckUser.DistinguishedName)) is a member of the Protected Users group." $true } else { - Write-Verbose "$($CheckUser.Name) ($($CheckUser.DistinguishedName)) is not a member of the Protected Users group." - $false + # Check if the user's PGID (primary group ID) is set to the Protected Users group RID (525). + if ( $CheckUser.primaryGroupID -eq '525' ) { + $true + } + else { + Write-Verbose "$($CheckUser.Name) ($($CheckUser.DistinguishedName)) is not a member of the Protected Users group." + $false + } } } + + end { + } } function Test-IsRecentVersion { @@ -4335,7 +4349,7 @@ Set-Acl -Path `$Path -AclObject `$ACL "@ } 4 { - break + break } 5 { $Issue.Fix = @" @@ -4587,7 +4601,7 @@ TODO #> -Function Write-HostColorized { +function Write-HostColorized { <# .SYNOPSIS Colors portions of the default host output that match given patterns. @@ -4706,10 +4720,10 @@ Function Write-HostColorized { # We precompile them for better performance with many input objects. [System.Text.RegularExpressions.RegexOptions] $reOpts = if ($CaseSensitive) { - 'Compiled, ExplicitCapture' + 'Compiled, ExplicitCapture' } else { - 'Compiled, ExplicitCapture, IgnoreCase' + 'Compiled, ExplicitCapture, IgnoreCase' } # Transform the dictionary: @@ -4731,20 +4745,20 @@ Function Write-HostColorized { } $colorArgs = @{ } if ($fg) { - $colorArgs['ForegroundColor'] = [ConsoleColor] $fg + $colorArgs['ForegroundColor'] = [ConsoleColor] $fg } if ($bg) { - $colorArgs['BackgroundColor'] = [ConsoleColor] $bg + $colorArgs['BackgroundColor'] = [ConsoleColor] $bg } # Consolidate the patterns into a single pattern with alternation ('|'), # escape the patterns if -SimpleMatch was passsed. $re = New-Object regex -Args ` $(if ($SimpleMatch) { - ($entry.Key | ForEach-Object { [regex]::Escape($_) }) -join '|' + ($entry.Key | ForEach-Object { [regex]::Escape($_) }) -join '|' } else { - ($entry.Key | ForEach-Object { '({0})' -f $_ }) -join '|' + ($entry.Key | ForEach-Object { '({0})' -f $_ }) -join '|' }), $reOpts @@ -4753,7 +4767,7 @@ Function Write-HostColorized { } } catch { - throw + throw } # Construct the arguments to pass to Out-String. @@ -4776,7 +4790,7 @@ Function Write-HostColorized { foreach ($m in $entry.Key.Matches($_)) { @{ Index = $m.Index; Text = $m.Value; ColorArgs = $entry.Value } if ($WholeLine) { - break patternLoop + break patternLoop } } } @@ -4955,7 +4969,7 @@ function Invoke-Locksmith { [System.Management.Automation.PSCredential]$Credential ) - $Version = '2025.5.26' + $Version = '2025.8.25' $LogoPart1 = @' _ _____ _______ _ _ _______ _______ _____ _______ _ _ | | | | |____/ |______ | | | | | |_____| @@ -4997,7 +5011,7 @@ function Invoke-Locksmith { # GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object. # WriteProperty may or may not permit full control depending the specific property and AD object type. - $DangerousRights = 'GenericAll|WriteDacl|WriteOwner|WriteProperty' + $DangerousRights = 'GenericAll|Write' # Extended Key Usage for client authentication. A requirement for ESC3. $EnrollmentAgentEKU = '1\.3\.6\.1\.4\.1\.311\.20\.2\.1' @@ -5162,11 +5176,13 @@ function Invoke-Locksmith { [!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues identified in the environment. For more details including: - - DistinguishedName of impacted object(s) - - Remediation guidance and/or code + - Detailed Risk Rating + - General remediation guidance and/or code for all issues + - Custom remediation guidance and/or code for some issues! - Revert guidance and/or code (in case remediation breaks something!) + - Distinguished Name of impacted object(s) -Run Locksmith in Mode 1! +Try Mode 1! # Module version Invoke-Locksmith -Mode 1 @@ -5229,7 +5245,8 @@ Invoke-Locksmith -Mode 1 } } Write-Host 'Thank you for using ' -NoNewline - Write-Host "Locksmith <3`n" -ForegroundColor Magenta + Write-Host 'Locksmith <3 ' -ForegroundColor Magenta -NoNewline + Write-Host "(https://github.com/jakehildreth/Locksmith)`n" } diff --git a/Locksmith.psd1 b/Locksmith.psd1 index b8ae79d..12295a8 100644 --- a/Locksmith.psd1 +++ b/Locksmith.psd1 @@ -8,7 +8,7 @@ FunctionsToExport = 'Invoke-Locksmith' GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a' HelpInfoURI = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/en-US/' - ModuleVersion = '2025.5.26' + ModuleVersion = '2025.8.25' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ diff --git a/Locksmith.psm1 b/Locksmith.psm1 index 0869fc0..af7557e 100644 --- a/Locksmith.psm1 +++ b/Locksmith.psm1 @@ -53,7 +53,7 @@ $Assembly = @( } ) $FoundErrors = @( - Foreach ($Import in @($Assembly)) { + foreach ($Import in @($Assembly)) { try { Write-Verbose -Message $Import.FullName Add-Type -Path $Import.Fullname -ErrorAction Stop @@ -77,10 +77,10 @@ $FoundErrors = @( } } #Dot source the files - Foreach ($Import in @($Classes + $Enums + $Private + $Public)) { - Try { + foreach ($Import in @($Classes + $Enums + $Private + $Public)) { + try { . $Import.Fullname - } Catch { + } catch { Write-Error -Message "Failed to import functions from $($import.Fullname): $_" $true } From ae0a35c35e14af9f97da437d1b3b1be52afe8ed4 Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Sun, 7 Sep 2025 21:24:24 -0400 Subject: [PATCH 04/10] fix: new logic should resolve ESC1 false positives. --- Invoke-Locksmith.ps1 | 8 ++++++-- Locksmith.psd1 | 2 +- Private/Find-ESC1.ps1 | 6 +++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index 946910d..3334ea7 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -263,7 +263,11 @@ function Find-ESC1 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and ($entry.ObjectType -eq '0e10c968-78fb-11d2-90d4-00c04f79dc55') ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -4969,7 +4973,7 @@ function Invoke-Locksmith { [System.Management.Automation.PSCredential]$Credential ) - $Version = '2025.8.25' + $Version = '2025.9.7' $LogoPart1 = @' _ _____ _______ _ _ _______ _______ _____ _______ _ _ | | | | |____/ |______ | | | | | |_____| diff --git a/Locksmith.psd1 b/Locksmith.psd1 index 12295a8..3c1d47e 100644 --- a/Locksmith.psd1 +++ b/Locksmith.psd1 @@ -8,7 +8,7 @@ FunctionsToExport = 'Invoke-Locksmith' GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a' HelpInfoURI = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/en-US/' - ModuleVersion = '2025.8.25' + ModuleVersion = '2025.9.7' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ diff --git a/Private/Find-ESC1.ps1 b/Private/Find-ESC1.ps1 index abb6fd0..e8e9831 100644 --- a/Private/Find-ESC1.ps1 +++ b/Private/Find-ESC1.ps1 @@ -56,7 +56,11 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and ($entry.ObjectType -eq '0e10c968-78fb-11d2-90d4-00c04f79dc55') ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name From 1c8e193222a158fc2e00e2ef764b49af1583554d Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 8 Sep 2025 06:00:28 -0400 Subject: [PATCH 05/10] fix: remove false negative when AllExtendedRights is granted on an ESC1 template --- Invoke-Locksmith.ps1 | 5 +++-- Locksmith.psd1 | 2 +- Private/Find-ESC1.ps1 | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index 3334ea7..ccf646f 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -265,7 +265,8 @@ function Find-ESC1 { } if ( ($SID -notmatch $SafeUsers) -and - ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and ($entry.ObjectType -eq '0e10c968-78fb-11d2-90d4-00c04f79dc55') ) -or + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ @@ -4973,7 +4974,7 @@ function Invoke-Locksmith { [System.Management.Automation.PSCredential]$Credential ) - $Version = '2025.9.7' + $Version = '2025.9.8' $LogoPart1 = @' _ _____ _______ _ _ _______ _______ _____ _______ _ _ | | | | |____/ |______ | | | | | |_____| diff --git a/Locksmith.psd1 b/Locksmith.psd1 index 3c1d47e..9222068 100644 --- a/Locksmith.psd1 +++ b/Locksmith.psd1 @@ -8,7 +8,7 @@ FunctionsToExport = 'Invoke-Locksmith' GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a' HelpInfoURI = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/en-US/' - ModuleVersion = '2025.9.7' + ModuleVersion = '2025.9.8' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ diff --git a/Private/Find-ESC1.ps1 b/Private/Find-ESC1.ps1 index e8e9831..8903cbd 100644 --- a/Private/Find-ESC1.ps1 +++ b/Private/Find-ESC1.ps1 @@ -58,7 +58,8 @@ } if ( ($SID -notmatch $SafeUsers) -and - ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and ($entry.ObjectType -eq '0e10c968-78fb-11d2-90d4-00c04f79dc55') ) -or + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ From 149e0a56e8f4425454109cdacd7954e09e94d601 Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 8 Sep 2025 18:44:03 -0400 Subject: [PATCH 06/10] fix: applied updated ESC1 detection logic to other template-based ESCs --- Invoke-Locksmith.ps1 | 51 ++++++++++++++++++++++------ Private/Find-ESC13.ps1 | 9 +++-- Private/Find-ESC15.ps1 | 7 +++- Private/Find-ESC16.ps1 | 2 +- Private/Find-ESC2.ps1 | 7 +++- Private/Find-ESC3C1.ps1 | 7 +++- Private/Find-ESC3C2.ps1 | 7 +++- Private/Find-ESC9.ps1 | 7 +++- Private/Set-AdditionalCAProperty.ps1 | 7 ++-- 9 files changed, 81 insertions(+), 23 deletions(-) diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index ccf646f..0232edd 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -463,7 +463,12 @@ function Find-ESC13 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -556,7 +561,12 @@ function Find-ESC15 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -651,7 +661,7 @@ function Find-ESC16 { if ($_.DisableExtensionList -eq 'Yes') { $Issue.Issue = @" The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When -this extension is disabled, every certificate issued by this CA will be unable to to reliably map a certificate to a +this extension is disabled, every certificate issued from this template will be unable to to reliably map a certificate to a user or computer account's SID for authentication. More info: @@ -734,7 +744,12 @@ function Find-ESC2 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -838,7 +853,12 @@ function Find-ESC3C1 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -931,7 +951,12 @@ function Find-ESC3C2 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -1813,7 +1838,12 @@ function Find-ESC9 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name @@ -3076,7 +3106,6 @@ function Set-AdditionalCAProperty { process { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { $CAEnrollmentEndpoint = @() - #[array]$CAEnrollmentEndpoint = $_.'msPKI-Enrollment-Servers' | Select-String 'http.*' | ForEach-Object { $_.Matches[0].Value } foreach ($directory in @('certsrv/', "$($_.Name)_CES_Kerberos/service.svc", "$($_.Name)_CES_Kerberos/service.svc/CES", 'ADPolicyProvider_CEP_Kerberos/service.svc', 'certsrv/mscep/')) { $URL = "://$($_.dNSHostName)/$directory" try { @@ -3086,7 +3115,7 @@ function Set-AdditionalCAProperty { $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -3101,7 +3130,7 @@ function Set-AdditionalCAProperty { $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -3116,7 +3145,7 @@ function Set-AdditionalCAProperty { $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL diff --git a/Private/Find-ESC13.ps1 b/Private/Find-ESC13.ps1 index 222c598..93bd53f 100644 --- a/Private/Find-ESC13.ps1 +++ b/Private/Find-ESC13.ps1 @@ -47,7 +47,7 @@ function Find-ESC13 { } | ForEach-Object { foreach ($policy in $_.'msPKI-Certificate-Policy') { if ($ADCSObjects.'msPKI-Cert-Template-OID' -contains $policy) { - $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -eq $policy + $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -EQ $policy if ($OidToCheck.'msDS-OIDToGroupLink') { foreach ($entry in $_.nTSecurityDescriptor.Access) { $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) @@ -56,7 +56,12 @@ function Find-ESC13 { } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Find-ESC15.ps1 b/Private/Find-ESC15.ps1 index ba99bb7..1e5c45b 100644 --- a/Private/Find-ESC15.ps1 +++ b/Private/Find-ESC15.ps1 @@ -44,7 +44,12 @@ function Find-ESC15 { } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Find-ESC16.ps1 b/Private/Find-ESC16.ps1 index 0f02ecd..cf8a4af 100644 --- a/Private/Find-ESC16.ps1 +++ b/Private/Find-ESC16.ps1 @@ -40,7 +40,7 @@ if ($_.DisableExtensionList -eq 'Yes') { $Issue.Issue = @" The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When -this extension is disabled, every certificate issued by this CA will be unable to to reliably map a certificate to a +this extension is disabled, every certificate issued from this template will be unable to to reliably map a certificate to a user or computer account's SID for authentication. More info: diff --git a/Private/Find-ESC2.ps1 b/Private/Find-ESC2.ps1 index 471e6ac..a49e2ff 100644 --- a/Private/Find-ESC2.ps1 +++ b/Private/Find-ESC2.ps1 @@ -46,7 +46,12 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Find-ESC3C1.ps1 b/Private/Find-ESC3C1.ps1 index 633120a..fce534f 100644 --- a/Private/Find-ESC3C1.ps1 +++ b/Private/Find-ESC3C1.ps1 @@ -46,7 +46,12 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Find-ESC3C2.ps1 b/Private/Find-ESC3C2.ps1 index 3e36604..868de4c 100644 --- a/Private/Find-ESC3C2.ps1 +++ b/Private/Find-ESC3C2.ps1 @@ -47,7 +47,12 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Find-ESC9.ps1 b/Private/Find-ESC9.ps1 index 2d3c2b1..3c25c9b 100644 --- a/Private/Find-ESC9.ps1 +++ b/Private/Find-ESC9.ps1 @@ -56,7 +56,12 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + if ( + ($SID -notmatch $SafeUsers) -and + ( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and + ( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or + ($entry.ActiveDirectoryRights -match 'GenericAll') ) + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name diff --git a/Private/Set-AdditionalCAProperty.ps1 b/Private/Set-AdditionalCAProperty.ps1 index 4f872ce..224f496 100644 --- a/Private/Set-AdditionalCAProperty.ps1 +++ b/Private/Set-AdditionalCAProperty.ps1 @@ -71,7 +71,6 @@ process { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { $CAEnrollmentEndpoint = @() - #[array]$CAEnrollmentEndpoint = $_.'msPKI-Enrollment-Servers' | Select-String 'http.*' | ForEach-Object { $_.Matches[0].Value } foreach ($directory in @('certsrv/', "$($_.Name)_CES_Kerberos/service.svc", "$($_.Name)_CES_Kerberos/service.svc/CES", 'ADPolicyProvider_CEP_Kerberos/service.svc', 'certsrv/mscep/')) { $URL = "://$($_.dNSHostName)/$directory" try { @@ -81,7 +80,7 @@ $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -95,7 +94,7 @@ $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -109,7 +108,7 @@ $Cache = [System.Net.CredentialCache]::New() $Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials) $Request.Credentials = $Cache - $Request.Timeout = 1000 + $Request.Timeout = 100 $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL From 02cde02d8f3f73bb62e45251100da4b9039b801b Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:49:48 -0400 Subject: [PATCH 07/10] Update Invoke-Locksmith.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Invoke-Locksmith.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index 0232edd..e295ee3 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -661,7 +661,7 @@ function Find-ESC16 { if ($_.DisableExtensionList -eq 'Yes') { $Issue.Issue = @" The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When -this extension is disabled, every certificate issued from this template will be unable to to reliably map a certificate to a +this extension is disabled, every certificate issued from this template will be unable to reliably map a certificate to a user or computer account's SID for authentication. More info: From 5a63da4c9083a2079983f486feaeca19821e4620 Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:50:02 -0400 Subject: [PATCH 08/10] Update Private/Find-ESC16.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Private/Find-ESC16.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Private/Find-ESC16.ps1 b/Private/Find-ESC16.ps1 index cf8a4af..a891c65 100644 --- a/Private/Find-ESC16.ps1 +++ b/Private/Find-ESC16.ps1 @@ -40,7 +40,7 @@ if ($_.DisableExtensionList -eq 'Yes') { $Issue.Issue = @" The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When -this extension is disabled, every certificate issued from this template will be unable to to reliably map a certificate to a +this extension is disabled, every certificate issued from this template will be unable to reliably map a certificate to a user or computer account's SID for authentication. More info: From c4dc429d63614b6b2a7f4d0b50b43e266932293f Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:50:18 -0400 Subject: [PATCH 09/10] Update Private/Find-ESC13.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Private/Find-ESC13.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Private/Find-ESC13.ps1 b/Private/Find-ESC13.ps1 index 93bd53f..20c578b 100644 --- a/Private/Find-ESC13.ps1 +++ b/Private/Find-ESC13.ps1 @@ -47,7 +47,7 @@ function Find-ESC13 { } | ForEach-Object { foreach ($policy in $_.'msPKI-Certificate-Policy') { if ($ADCSObjects.'msPKI-Cert-Template-OID' -contains $policy) { - $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -EQ $policy + $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -eq $policy if ($OidToCheck.'msDS-OIDToGroupLink') { foreach ($entry in $_.nTSecurityDescriptor.Access) { $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) From 3fa75b635ed20e185c8edb2dad411358239859d1 Mon Sep 17 00:00:00 2001 From: Jake Hildreth <93942157+jakehildreth@users.noreply.github.com> Date: Thu, 11 Sep 2025 04:57:34 -0400 Subject: [PATCH 10/10] Update Private/Find-ESC16.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Private/Find-ESC16.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Private/Find-ESC16.ps1 b/Private/Find-ESC16.ps1 index a891c65..b70f42e 100644 --- a/Private/Find-ESC16.ps1 +++ b/Private/Find-ESC16.ps1 @@ -40,7 +40,7 @@ if ($_.DisableExtensionList -eq 'Yes') { $Issue.Issue = @" The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When -this extension is disabled, every certificate issued from this template will be unable to reliably map a certificate to a +this extension is disabled, every certificate issued by this CA will be unable to reliably map a certificate to a user or computer account's SID for authentication. More info: