From 149e0a56e8f4425454109cdacd7954e09e94d601 Mon Sep 17 00:00:00 2001 From: Jake Hildreth Date: Mon, 8 Sep 2025 18:44:03 -0400 Subject: [PATCH 1/4] 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 ccf646fe..0232edd1 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 222c598a..93bd53ff 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 ba99bb7a..1e5c45b6 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 0f02ecdf..cf8a4af8 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 471e6acc..a49e2ffe 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 633120a8..fce534f7 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 3e366049..868de4c3 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 2d3c2b17..3c25c9bc 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 4f872ced..224f4966 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 2/4] 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 0232edd1..e295ee31 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 3/4] 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 cf8a4af8..a891c651 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 4/4] 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 93bd53ff..20c578be 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)