Skip to content

Commit c934def

Browse files
committed
Update module metadata, add ESC17 vulnerability detection, and enhance risk assessment
- Updated copyright year in Locksmith.psd1 to 2026 and incremented module version to 2026.1.4.1411. - Modified Format-Result.ps1 to change ESC1 description and added ESC17 vulnerability. - Enhanced Invoke-Scans.ps1 to include ESC17 in scan options and processing logic. - Updated Set-RiskRating.ps1 to account for ESC17 in risk scoring. - Extended Invoke-Locksmith.ps1 to handle server authentication EKUs for ESC17. - Implemented Find-ESC17.ps1 to identify AD CS templates with dangerous configurations related to server authentication.
1 parent 6f1b845 commit c934def

8 files changed

Lines changed: 536 additions & 158 deletions

File tree

Build/Build-Module.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ $CopyrightYear = if ($Calver) { $CalVer.Split('.')[0] } else { (Get-Date -Format
2626
Build-Module -ModuleName 'Locksmith' {
2727
# Usual defaults as per standard module
2828
$Manifest = [ordered] @{
29-
ModuleVersion = if ($Calver) { $CalVer } else { (Get-Date -Format yyyy.M.d.Hmm -AsUTC) }
29+
ModuleVersion = if ($Calver) { $CalVer } else { (Get-Date -Format yyyy.M.d.Hmm) }
3030
CompatiblePSEditions = @('Desktop', 'Core')
3131
GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a'
3232
Author = 'Jake Hildreth'
@@ -132,7 +132,7 @@ Build-Module -ModuleName 'Locksmith' {
132132

133133
# The scans to run. Defaults to 'All'.
134134
[Parameter()]
135-
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')]
135+
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'ESC17', 'All', 'PromptMe')]
136136
[array]$Scans = 'All',
137137

138138
[Parameter()]

Invoke-Locksmith.ps1

Lines changed: 394 additions & 147 deletions
Large diffs are not rendered by default.

Locksmith.psd1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
Author = 'Jake Hildreth'
44
CmdletsToExport = @()
55
CompatiblePSEditions = @('Desktop', 'Core')
6-
Copyright = '(c) 2022 - 2025. All rights reserved.'
6+
Copyright = '(c) 2022 - 2026. All rights reserved.'
77
Description = 'A small tool to find and fix common misconfigurations in Active Directory Certificate Services.'
88
FunctionsToExport = 'Invoke-Locksmith'
99
GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a'
1010
HelpInfoURI = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/en-US/'
11-
ModuleVersion = '2025.10.29.1143'
11+
ModuleVersion = '2026.1.4.1411'
1212
PowerShellVersion = '5.1'
1313
PrivateData = @{
1414
PSData = @{
1515
ExternalModuleDependencies = @('ActiveDirectory', 'ServerManager', 'Microsoft.PowerShell.Utility', 'Microsoft.PowerShell.LocalAccounts', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Security', 'CimCmdlets', 'Dism')
1616
IconUri = 'https://raw.githubusercontent.com/jakehildreth/Locksmith/main/Images/locksmith.ico'
1717
ProjectUri = 'https://github.com/jakehildreth/Locksmith'
18+
RequireLicenseAcceptance = $false
1819
Tags = @('Locksmith', 'ActiveDirectory', 'ADCS', 'CA', 'Certificate', 'CertificateAuthority', 'CertificateServices', 'PKI', 'X509', 'Windows')
1920
}
2021
}

Private/Find-ESC17.ps1

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
function Find-ESC17 {
2+
<#
3+
.SYNOPSIS
4+
This script finds AD CS (Active Directory Certificate Services) objects that have the ESC1 vulnerability.
5+
6+
.DESCRIPTION
7+
The script takes an array of ADCS objects as input and filters them based on the specified conditions.
8+
For each matching object, it creates a custom object with properties representing various information about
9+
the object, such as Forest, Name, DistinguishedName, IdentityReference, ActiveDirectoryRights, Issue, Fix, Revert, and Technique.
10+
11+
.PARAMETER ADCSObjects
12+
Specifies the array of ADCS objects to be processed. This parameter is mandatory.
13+
14+
.PARAMETER SafeUsers
15+
Specifies the list of SIDs of safe users who are allowed to have specific rights on the objects. This parameter is mandatory.
16+
17+
.PARAMETER ServerAuthEKUs
18+
A list of EKUs that can be used for server authentication.
19+
20+
.OUTPUTS
21+
The script outputs an array of custom objects representing the matching ADCS objects and their associated information.
22+
23+
.EXAMPLE
24+
$Targets = Get-Target
25+
$ADCSObjects = Get-ADCSObject -Targets $Targets
26+
$SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10'
27+
$ServerAuthEKUs = '1\.3\.6\.1\.5\.5\.7\.3\.1'
28+
$Results = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ServerAuthEKUs
29+
$Results
30+
#>
31+
[CmdletBinding()]
32+
param(
33+
[Parameter(Mandatory)]
34+
[Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects,
35+
[Parameter(Mandatory)]
36+
[string]$SafeUsers,
37+
[Parameter(Mandatory)]
38+
$ServerAuthEKUs,
39+
[Parameter(Mandatory)]
40+
[int]$Mode,
41+
[Parameter(Mandatory)]
42+
[string]$UnsafeUsers,
43+
[switch]$SkipRisk
44+
)
45+
$ADCSObjects | Where-Object {
46+
($_.objectClass -eq 'pKICertificateTemplate') -and
47+
($_.pkiExtendedKeyUsage -match $ServerAuthEKUs) -and
48+
($_.'msPKI-Certificate-Name-Flag' -band 1) -and
49+
!($_.'msPKI-Enrollment-Flag' -band 2) -and
50+
( ($_.'msPKI-RA-Signature' -eq 0) -or ($null -eq $_.'msPKI-RA-Signature') )
51+
} | ForEach-Object {
52+
foreach ($entry in $_.nTSecurityDescriptor.Access) {
53+
$Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference)
54+
if ($Principal -match '^(S-1|O:)') {
55+
$SID = $Principal
56+
} else {
57+
$SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value
58+
}
59+
if (
60+
($SID -notmatch $SafeUsers) -and
61+
( ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -and
62+
( $entry.ObjectType -match '0e10c968-78fb-11d2-90d4-00c04f79dc55|00000000-0000-0000-0000-000000000000' ) ) -or
63+
($entry.ActiveDirectoryRights -match 'GenericAll') )
64+
) {
65+
$Issue = [pscustomobject]@{
66+
Forest = $_.CanonicalName.split('/')[0]
67+
Name = $_.Name
68+
DistinguishedName = $_.DistinguishedName
69+
IdentityReference = $entry.IdentityReference
70+
IdentityReferenceSID = $SID
71+
ActiveDirectoryRights = $entry.ActiveDirectoryRights
72+
Enabled = $_.Enabled
73+
EnabledOn = $_.EnabledOn
74+
Issue = @"
75+
$($entry.IdentityReference) can provide a Subject Alternative Name (SAN) while
76+
enrolling in this Server Authentication template, and enrollment does not require
77+
Manager Approval.
78+
79+
The resultant certificate can be used by an attacker to impersonate servers
80+
and perform Machine-in-the-Middle Attacks
81+
82+
More info:
83+
- https://trustedsec.com/blog/wsus-is-sus-ntlm-relay-attacks-in-plain-sight
84+
- https://blog.digitrace.de/2026/01/using-adcs-to-attack-https-enabled-wsus-clients/
85+
86+
"@
87+
Fix = @"
88+
# Enable Manager Approval
89+
`$Object = '$($_.DistinguishedName)'
90+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2}
91+
"@
92+
Revert = @"
93+
# Disable Manager Approval
94+
`$Object = '$($_.DistinguishedName)'
95+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0}
96+
"@
97+
Technique = 'ESC17'
98+
}
99+
100+
if ($SkipRisk -eq $false) {
101+
Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers
102+
}
103+
104+
# if ( $Mode -in @(1, 3, 4) ) {
105+
# Update-ESC1Remediation -Issue $Issue
106+
# }
107+
108+
$Issue
109+
}
110+
}
111+
}
112+
}

Private/Format-Result.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function Format-Result {
3232

3333
$IssueTable = @{
3434
DETECT = 'Auditing Not Fully Enabled'
35-
ESC1 = 'ESC1 - Vulnerable Certificate Template - Authentication'
35+
ESC1 = 'ESC1 - Vulnerable Certificate Template - Client Authentication'
3636
ESC2 = 'ESC2 - Vulnerable Certificate Template - Subordinate CA/Any Purpose'
3737
ESC3 = 'ESC3 - Vulnerable Certificate Template - Enrollment Agent'
3838
ESC4 = 'ESC4 - Vulnerable Access Control - Certificate Template'
@@ -45,6 +45,7 @@ function Format-Result {
4545
ESC13 = 'ESC13 - Vulnerable Certificate Template - Group-Linked'
4646
'ESC15/EKUwu' = 'ESC15 - Vulnerable Certificate Template - Schema V1'
4747
ESC16 = 'ESC16 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled on CA'
48+
ESC17 = 'ESC17 - Vulnerable Certificate Template - Server Authentication'
4849
}
4950

5051
$RiskTable = @{
@@ -73,7 +74,7 @@ function Format-Result {
7374
Format-Table Technique, @{l = 'CA Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Issue -Wrap |
7475
Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive
7576
}
76-
{ $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu') } {
77+
{ $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu', 'ESC17') } {
7778
$Issue |
7879
Format-Table Technique, @{l = 'Template Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Enabled, Issue -Wrap |
7980
Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive

Private/Invoke-Scans.ps1

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function Invoke-Scans {
66
.PARAMETER Scans
77
Specifies the type of scans to perform. Multiple scan options can be provided as an array. The default value is 'All'.
88
The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC9', 'ESC11',
9-
'ESC13', 'ESC15, 'EKUwu', 'ESC16', 'All', 'PromptMe'.
9+
'ESC13', 'ESC15, 'EKUwu', 'ESC16', 'ESC17', 'All', 'PromptMe'.
1010
1111
.NOTES
1212
- The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3C1,
@@ -37,6 +37,8 @@ function Invoke-Scans {
3737
[Parameter(Mandatory)]
3838
[string]$ClientAuthEkus,
3939
[Parameter(Mandatory)]
40+
[string]$ServerAuthEkus,
41+
[Parameter(Mandatory)]
4042
[string]$DangerousRights,
4143
[Parameter(Mandatory)]
4244
[string]$EnrollmentAgentEKU,
@@ -48,7 +50,7 @@ function Invoke-Scans {
4850
[string]$SafeUsers,
4951
[Parameter(Mandatory)]
5052
[string]$SafeOwners,
51-
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')]
53+
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'ESC17', 'All', 'PromptMe')]
5254
[array]$Scans = 'All',
5355
[Parameter(Mandatory)]
5456
[string]$UnsafeUsers,
@@ -133,6 +135,10 @@ function Invoke-Scans {
133135
Write-Host 'Identifying Issuing CAs with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...'
134136
[array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
135137
}
138+
ESC17 {
139+
Write-Host 'Identifying AD CS templates with dangerous ESC17 configurations...'
140+
[array]$ESC17 = Find-ESC17 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ServerAuthEKUs $ServerAuthEKUs -Mode $Mode -UnsafeUsers $UnsafeUsers
141+
}
136142
All {
137143
Write-Host 'Identifying auditing issues...'
138144
[array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects
@@ -163,10 +169,12 @@ function Invoke-Scans {
163169
[array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers
164170
Write-Host 'Identifying Certificate Authorities with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...'
165171
[array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
172+
Write-Host 'Identifying AD CS templates with dangerous ESC17 configurations...'
173+
[array]$ESC17 = Find-ESC17 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ServerAuthEKUs $ServerAuthEkus -Mode $Mode -UnsafeUsers $UnsafeUsers
166174
}
167175
}
168176

169-
[array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC9 + $ESC11 + $ESC13 + $ESC15 + $ESC16
177+
[array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC9 + $ESC11 + $ESC13 + $ESC15 + $ESC16 + $ESC17
170178

171179
# If these are all empty = no issues found, exit
172180
if ($AllIssues.Count -lt 1) {
@@ -191,5 +199,6 @@ function Invoke-Scans {
191199
ESC13 = $ESC13
192200
ESC15 = $ESC15
193201
ESC16 = $ESC16
202+
ESC17 = $ESC17
194203
}
195204
}

Private/Set-RiskRating.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function Set-RiskRating {
5555
$RiskScoring = @()
5656

5757
# CA issues don't rely on a principal and have a base risk of Medium.
58-
if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) {
58+
if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11','ESC17')) {
5959
$RiskValue += 3
6060
$RiskScoring += 'Base Score: 3'
6161

@@ -104,7 +104,7 @@ function Set-RiskRating {
104104
}
105105

106106
# Template and object issues rely on a principal and have complex scoring.
107-
if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) {
107+
if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11','ESC17')) {
108108
$RiskScoring += 'Base Score: 0'
109109

110110
# Templates are more dangerous when enabled, but objects cannot be enabled/disabled.

Public/Invoke-Locksmith.ps1

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ function Invoke-Locksmith {
101101
'ESC15',
102102
'EKUwu',
103103
'ESC16',
104+
'ESC17',
104105
'All',
105106
'PromptMe'
106107
)]
@@ -155,6 +156,9 @@ function Invoke-Locksmith {
155156
# Extended Key Usages for client authentication. A requirement for ESC1, ESC3 Condition 2, and ESC13
156157
$ClientAuthEKUs = '1\.3\.6\.1\.5\.5\.7\.3\.2|1\.3\.6\.1\.5\.2\.3\.4|1\.3\.6\.1\.4\.1\.311\.20\.2\.2|2\.5\.29\.37\.0'
157158

159+
# Extended Key Usages for server authentication. A requirement for ESC17
160+
$ServerAuthEKUs = '1\.3\.6\.1\.5\.5\.7\.3\.1'
161+
158162
# GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object.
159163
# WriteProperty may or may not permit full control depending the specific property and AD object type.
160164
$DangerousRights = 'GenericAll|Write'
@@ -264,6 +268,7 @@ function Invoke-Locksmith {
264268
$ScansParameters = @{
265269
ADCSObjects = $ADCSObjects
266270
ClientAuthEkus = $ClientAuthEKUs
271+
ServerAuthEKUs = $ServerAuthEKUs
267272
DangerousRights = $DangerousRights
268273
EnrollmentAgentEKU = $EnrollmentAgentEKU
269274
Mode = $Mode
@@ -291,6 +296,7 @@ function Invoke-Locksmith {
291296
$ESC13 = $Results['ESC13']
292297
$ESC15 = $Results['ESC15']
293298
$ESC16 = $Results['ESC16']
299+
$ESC17 = $Results['ESC17']
294300

295301
# If these are all empty = no issues found, exit
296302
if ($null -eq $Results) {
@@ -316,6 +322,7 @@ function Invoke-Locksmith {
316322
Format-Result -Issue $ESC13 -Mode 0
317323
Format-Result -Issue $ESC15 -Mode 0
318324
Format-Result -Issue $ESC16 -Mode 0
325+
Format-Result -Issue $ESC17 -Mode 0
319326
Write-Host @"
320327
[!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues
321328
identified in the environment. For more details including:
@@ -350,6 +357,7 @@ Invoke-Locksmith -Mode 1
350357
Format-Result -Issue $ESC13 -Mode 1
351358
Format-Result -Issue $ESC15 -Mode 1
352359
Format-Result -Issue $ESC16 -Mode 1
360+
Format-Result -Issue $ESC17 -Mode 1
353361
}
354362
2 {
355363
$Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV"

0 commit comments

Comments
 (0)