Skip to content

Commit dd5a51b

Browse files
authored
Merge pull request #247 from jakehildreth/add-esc16
Added ESC16 detections 🕵️
2 parents 7425baf + a7d240c commit dd5a51b

11 files changed

Lines changed: 245 additions & 31 deletions

Build/Build-Module.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Build-Module -ModuleName 'Locksmith' {
129129

130130
# The scans to run. Defaults to 'All'.
131131
[Parameter()]
132-
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')]
132+
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')]
133133
[array]$Scans = 'All'
134134
)
135135
}

Invoke-Locksmith.ps1

Lines changed: 124 additions & 15 deletions
Large diffs are not rendered by default.

Private/Find-ESC16.ps1

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
function Find-ESC16 {
2+
<#
3+
.SYNOPSIS
4+
This script finds Active Directory Certificate Services (AD CS) Certification Authorities (CA) that have the ESC16 vulnerability.
5+
6+
.DESCRIPTION
7+
The script takes an array of ADCS objects as input and filters them based on objects that have the objectClass
8+
'pKIEnrollmentService' and the szOID_NTDS_CA_SECURITY_EXT disabled. For each matching object, it creates a custom object with
9+
properties representing various information about the object, such as Forest, Name, DistinguishedName, Technique,
10+
Issue, Fix, and Revert.
11+
12+
.PARAMETER ADCSObjects
13+
Specifies the array of AD CS objects to be processed. This parameter is mandatory.
14+
15+
.OUTPUTS
16+
The script outputs an array of custom objects representing the matching ADCS objects and their associated information.
17+
#>
18+
[CmdletBinding()]
19+
param(
20+
[Parameter(Mandatory)]
21+
[Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects,
22+
[Parameter(Mandatory)]
23+
[string]$UnsafeUsers,
24+
[switch]$SkipRisk
25+
)
26+
process {
27+
$ADCSObjects | Where-Object {
28+
($_.objectClass -eq 'pKIEnrollmentService') -and
29+
($_.DisableExtensionList -ne 'No')
30+
} | ForEach-Object {
31+
$Issue = [pscustomobject]@{
32+
Forest = $_.CanonicalName.split('/')[0]
33+
Name = $_.Name
34+
DistinguishedName = $_.DistinguishedName
35+
Issue = $_.DisableExtensionList
36+
Fix = 'N/A'
37+
Revert = 'N/A'
38+
Technique = 'ESC16'
39+
}
40+
if ($_.DisableExtensionList -eq 'Yes') {
41+
$Issue.Issue = @"
42+
The Certification Authority (CA) $($_.CAFullName) has the szOID_NTDS_CA_SECURITY_EXT security extension disabled. When
43+
this extension is disabled, every certificate issued by this CA will be unable to to reliably map a certificate to a
44+
user or computer account's SID for authentication.
45+
46+
More info:
47+
- https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc16-security-extension-disabled-on-ca-globally
48+
49+
"@
50+
$Issue.Fix = @"
51+
# Enable the flag
52+
# TODO
53+
54+
# Restart the Certificate Authority service
55+
Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock {
56+
Get-Service -Name certsvc | Restart-Service -Force
57+
}
58+
"@
59+
$Issue.Revert = @"
60+
# Disable the flag
61+
TODO
62+
63+
# Restart the Certificate Authority service
64+
Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock {
65+
Get-Service -Name certsvc | Restart-Service -Force
66+
}
67+
"@
68+
}
69+
if ($SkipRisk -eq $false) {
70+
Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers
71+
}
72+
$Issue
73+
}
74+
}
75+
}

Private/Find-ESC7.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
process {
3737
$ADCSObjects | Where-Object {
3838
($_.objectClass -eq 'pKIEnrollmentService') -and
39-
( ($_.CAAdministrator) -or ($_.CertificateManager) )
39+
( ($_.CAAdministrator -notmatch 'Failure|CA Unavailable') -or ($_.CertificateManager) )
4040
} | ForEach-Object {
4141
$UnsafeCAAdministrators = Write-Output $_.CAAdministrator -PipelineVariable admin | ForEach-Object {
4242
$SID = Convert-IdentityReferenceToSid -Object $admin

Private/Format-Result.ps1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function Format-Result {
4343
ESC11 = 'ESC11 - IF_ENFORCEENCRYPTICERTREQUEST Flag Disabled'
4444
ESC13 = 'ESC13 - Vulnerable Certificate Template - Group-Linked'
4545
'ESC15/EKUwu' = 'ESC15 - Vulnerable Certificate Template - Schema V1'
46+
ESC16 = 'ESC16 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled'
4647
}
4748

4849
$RiskTable = @{
@@ -66,7 +67,7 @@ function Format-Result {
6667
if ($Mode -eq 0) {
6768
# TODO Refactor this
6869
switch ($UniqueIssue) {
69-
{ $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11') } {
70+
{ $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } {
7071
$Issue |
7172
Format-Table Technique, @{l = 'CA Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Issue -Wrap |
7273
Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive
@@ -84,7 +85,7 @@ function Format-Result {
8485
}
8586
} elseif ($Mode -eq 1) {
8687
switch ($UniqueIssue) {
87-
{ $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11') } {
88+
{ $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } {
8889
$Issue |
8990
Format-List Technique, @{l = 'CA Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, DistinguishedName, Issue, Fix, @{l = 'Risk Score'; e = { $_.RiskValue } }, @{l = 'Risk Score Detail'; e = { $_.RiskScoring -join "`n" } } |
9091
Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive

Private/Invoke-Scans.ps1

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ 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', 'ESC11',
9-
'ESC13', 'ESC15, 'EKUwu', 'All', 'PromptMe'.
9+
'ESC13', 'ESC15, 'EKUwu', 'ESC16', 'All', 'PromptMe'.
1010
1111
.NOTES
1212
- The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3C1,
13-
Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13, Find-ESC15
13+
Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13, Find-ESC15, Find-ESC16
1414
- The script uses Out-GridView or Out-ConsoleGridView for interactive selection when the 'PromptMe' scan option is chosen.
1515
- The script returns a hash table containing the results of the scans.
1616
@@ -48,7 +48,7 @@ function Invoke-Scans {
4848
[string]$SafeUsers,
4949
[Parameter(Mandatory)]
5050
[string]$SafeOwners,
51-
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')]
51+
[ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')]
5252
[array]$Scans = 'All',
5353
[Parameter(Mandatory)]
5454
[string]$UnsafeUsers,
@@ -102,7 +102,7 @@ function Invoke-Scans {
102102
[array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
103103
}
104104
ESC7 {
105-
Write-Host 'Identifying Issuing CAs with ESC7...'
105+
Write-Host 'Identifying Issuing CAs with Non-Standard Admins (ESC7)...'
106106
[array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers
107107
}
108108
ESC8 {
@@ -125,6 +125,10 @@ function Invoke-Scans {
125125
Write-Host 'Identifying AD CS templates with dangerous ESC15/EKUwu configurations...'
126126
[array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers
127127
}
128+
ESC16 {
129+
Write-Host 'Identifying Issuing CAs with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...'
130+
[array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
131+
}
128132
All {
129133
Write-Host 'Identifying auditing issues...'
130134
[array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects
@@ -141,7 +145,7 @@ function Invoke-Scans {
141145
[array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -UnsafeUsers $UnsafeUsers
142146
Write-Host 'Identifying Certificate Authorities with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...'
143147
[array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
144-
Write-Host 'Identifying Certificate Authorities with ESC7...'
148+
Write-Host 'Identifying Certificate Authorities with Non-Standard Admins (ESC7)...'
145149
[array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers
146150
Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...'
147151
[array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
@@ -151,11 +155,12 @@ function Invoke-Scans {
151155
[array]$ESC13 = Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -UnsafeUsers $UnsafeUsers
152156
Write-Host 'Identifying AD CS templates with dangerous ESC15 configurations...'
153157
[array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers
154-
Write-Host
158+
Write-Host 'Identifying Certificate Authorities with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...'
159+
[array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers
155160
}
156161
}
157162

158-
[array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC11 + $ESC13 + $ESC15
163+
[array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC11 + $ESC13 + $ESC15 + $ESC16
159164

160165
# If these are all empty = no issues found, exit
161166
if ($AllIssues.Count -lt 1) {
@@ -178,5 +183,6 @@ function Invoke-Scans {
178183
ESC11 = $ESC11
179184
ESC13 = $ESC13
180185
ESC15 = $ESC15
186+
ESC16 = $ESC16
181187
}
182188
}

Private/New-Dictionary.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function New-Dictionary {
9494
Category = 'Escalation Path'
9595
Subcategory = 'Vulnerable Certificate Authority Access Control'
9696
Summary = ''
97-
FindIt = { Write-Output 'We have not created Find-ESC7 yet.' }
97+
FindIt = { Find-ESC7 }
9898
FixIt = { Write-Output 'Add code to fix the vulnerable configuration.' }
9999
ReferenceUrls = 'https://posts.specterops.io/certified-pre-owned-d95910965cd2#:~:text=Vulnerable%20Certificate%20Authority%20Access%20Control%20%E2%80%94%20ESC7'
100100
},

Private/Set-AdditionalCAProperty.ps1

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,22 @@
168168
$CAAdministrator = 'Failure'
169169
$CertificateManager = 'Failure'
170170
}
171+
try {
172+
if ($Credential) {
173+
$CertutilDisableExtensionList = Invoke-Command -ComputerName $CAHostFQDN -Credential $Credential -ScriptBlock { certutil -config $using:CAFullName -getreg policy\DisableExtensionList }
174+
} else {
175+
$CertutilDisableExtensionList = certutil -config $CAFullName -getreg policy\DisableExtensionList
176+
}
177+
} catch {
178+
$CertutilDisableExtensionList = 'Failure'
179+
}
171180
} else {
172181
$AuditFilter = 'CA Unavailable'
173182
$SANFlag = 'CA Unavailable'
174183
$InterfaceFlag = 'CA Unavailable'
175184
$CAAdministrator = 'CA Unavailable'
176185
$CertificateManager = 'CA Unavailable'
186+
$DisableExtensionList = 'CA Unavailable'
177187
}
178188
if ($CertutilAudit) {
179189
try {
@@ -216,6 +226,14 @@
216226
}
217227
}
218228
}
229+
if ($CertutilDisableExtensionList) {
230+
[string]$DisableExtensionList = $CertutilDisableExtensionList | Select-String '1\.3\.6\.1\.4\.1\.311\.25\.2'
231+
if ($DisableExtensionList) {
232+
$DisableExtensionList = 'Yes'
233+
} else {
234+
$DisableExtensionList = 'No'
235+
}
236+
}
219237
Add-Member -InputObject $_ -MemberType NoteProperty -Name AuditFilter -Value $AuditFilter -Force
220238
Add-Member -InputObject $_ -MemberType NoteProperty -Name CAEnrollmentEndpoint -Value $CAEnrollmentEndpoint -Force
221239
Add-Member -InputObject $_ -MemberType NoteProperty -Name CAFullName -Value $CAFullName -Force
@@ -225,6 +243,7 @@
225243
Add-Member -InputObject $_ -MemberType NoteProperty -Name InterfaceFlag -Value $InterfaceFlag -Force
226244
Add-Member -InputObject $_ -MemberType NoteProperty -Name CAAdministrator -Value $CAAdministrator -Force
227245
Add-Member -InputObject $_ -MemberType NoteProperty -Name CertificateManager -Value $CertificateManager -Force
246+
Add-Member -InputObject $_ -MemberType NoteProperty -Name DisableExtensionList -Value $DisableExtensionList -Force
228247
}
229248
}
230249
}

Private/Set-RiskRating.ps1

Lines changed: 3 additions & 3 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')) {
58+
if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) {
5959
$RiskValue += 3
6060
$RiskScoring += 'Base Score: 3'
6161

@@ -64,12 +64,12 @@ function Set-RiskRating {
6464
$RiskScoring += 'HTTP Enrollment: +2'
6565
}
6666

67-
# TODO Check NtAuthCertificates for CA thumbnail. If found, +2, else -1
67+
# TODO Check NtAuthCertificates for CA thumbprint. If found, +2, else -1
6868
# TODO Check if NTLMv1 is allowed.
6969
}
7070

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

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

Public/Invoke-Locksmith.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ function Invoke-Locksmith {
9999
'ESC13',
100100
'ESC15',
101101
'EKUwu',
102+
'ESC16',
102103
'All',
103104
'PromptMe'
104105
)]
@@ -287,6 +288,7 @@ function Invoke-Locksmith {
287288
$ESC11 = $Results['ESC11']
288289
$ESC13 = $Results['ESC13']
289290
$ESC15 = $Results['ESC15']
291+
$ESC16 = $Results['ESC16']
290292

291293
# If these are all empty = no issues found, exit
292294
if ($null -eq $Results) {
@@ -310,6 +312,7 @@ function Invoke-Locksmith {
310312
Format-Result -Issue $ESC11 -Mode 0
311313
Format-Result -Issue $ESC13 -Mode 0
312314
Format-Result -Issue $ESC15 -Mode 0
315+
Format-Result -Issue $ESC16 -Mode 0
313316
Write-Host @"
314317
[!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues
315318
identified in the environment. For more details including:
@@ -340,6 +343,7 @@ Invoke-Locksmith -Mode 1
340343
Format-Result -Issue $ESC11 -Mode 1
341344
Format-Result -Issue $ESC13 -Mode 1
342345
Format-Result -Issue $ESC15 -Mode 1
346+
Format-Result -Issue $ESC16 -Mode 1
343347
}
344348
2 {
345349
$Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV"

0 commit comments

Comments
 (0)