Skip to content

Commit f6009ed

Browse files
authored
Merge pull request #191 from TrimarcJake/get-published-templates
2 parents 592e399 + 1a3081b commit f6009ed

9 files changed

+357
-32
lines changed

Diff for: Invoke-Locksmith.ps1

+177-14
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,11 @@ Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0}
280280
"@
281281
Technique = 'ESC1'
282282
}
283+
284+
if ( $Mode -in @(1, 3, 4) ) {
285+
Update-ESC1Remediation -Issue $Issue
286+
}
287+
283288
$Issue
284289
}
285290
}
@@ -2648,7 +2653,7 @@ function Set-AdditionalCAProperty {
26482653
$Cache = [System.Net.CredentialCache]::New()
26492654
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
26502655
$Request.Credentials = $Cache
2651-
$Request.Timeout = 3000
2656+
$Request.Timeout = 1000
26522657
$Request.GetResponse() | Out-Null
26532658
$CAEnrollmentEndpoint += @{
26542659
'URL' = $FullURL
@@ -2657,9 +2662,13 @@ function Set-AdditionalCAProperty {
26572662
}
26582663
catch {
26592664
try {
2665+
$Auth = 'NTLM'
26602666
$FullURL = "https$URL"
26612667
$Request = [System.Net.WebRequest]::Create($FullURL)
2662-
2668+
$Cache = [System.Net.CredentialCache]::New()
2669+
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
2670+
$Request.Credentials = $Cache
2671+
$Request.Timeout = 1000
26632672
$Request.GetResponse() | Out-Null
26642673
$CAEnrollmentEndpoint += @{
26652674
'URL' = $FullURL
@@ -2672,8 +2681,9 @@ function Set-AdditionalCAProperty {
26722681
$FullURL = "https$URL"
26732682
$Request = [System.Net.WebRequest]::Create($FullURL)
26742683
$Cache = [System.Net.CredentialCache]::New()
2675-
$Cache.Add([System.Uri]::new($FullURL), 'Negotiate', [System.Net.CredentialCache]::DefaultNetworkCredentials)
2684+
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
26762685
$Request.Credentials = $Cache
2686+
$Request.Timeout = 1000
26772687
$Request.GetResponse() | Out-Null
26782688
$CAEnrollmentEndpoint += @{
26792689
'URL' = $FullURL
@@ -2780,6 +2790,52 @@ function Set-AdditionalCAProperty {
27802790
}
27812791
}
27822792

2793+
function Set-AdditionalTemplateProperty {
2794+
<#
2795+
.SYNOPSIS
2796+
Sets additional properties on a template object.
2797+
2798+
.DESCRIPTION
2799+
This script sets additional properties on a template object.
2800+
It takes an array of AD CS Objects as input, which includes the templates to be processed and CA objects that
2801+
detail which templates are published.
2802+
The script filters the AD CS Objects based on the objectClass property and performs the necessary operations
2803+
to set the additional properties.
2804+
2805+
.PARAMETER ADCSObjects
2806+
Specifies the array of AD CS Objects to be processed. This parameter is mandatory and supports pipeline input.
2807+
2808+
.PARAMETER Credential
2809+
Specifies the PSCredential object to be used for authentication when accessing the CA objects.
2810+
If not provided, the script will use the current user's credentials.
2811+
2812+
.EXAMPLE
2813+
$ADCSObjects = Get-ADCSObject -Targets (Get-Target)
2814+
Set-AdditionalTemplateProperty -ADCSObjects $ADCSObjects -ForestGC 'dc1.ad.dotdot.horse:3268'
2815+
#>
2816+
2817+
[CmdletBinding(SupportsShouldProcess)]
2818+
param (
2819+
[parameter(Mandatory, ValueFromPipeline)]
2820+
[array]$ADCSObjects
2821+
)
2822+
2823+
$ADCSObjects | Where-Object objectClass -Match 'pKICertificateTemplate' -PipelineVariable template | ForEach-Object {
2824+
Write-Host "[?] Checking if template `"$($template.Name)`" is published on any Certification Authority." -ForegroundColor Blue
2825+
$Published = $false
2826+
$PublishedOn = @()
2827+
foreach ($ca in ($ADCSObjects | Where-Object objectClass -EQ 'pKIEnrollmentService')) {
2828+
if ($ca.certificateTemplates -contains $template.Name) {
2829+
$Published = $true
2830+
$PublishedOn += $ca.Name
2831+
}
2832+
2833+
$template | Add-Member -NotePropertyName Published -NotePropertyValue $Published -Force
2834+
$template | Add-Member -NotePropertyName PublishedOn -NotePropertyValue $PublishedOn -Force
2835+
}
2836+
}
2837+
}
2838+
27832839
function Set-Severity {
27842840
[OutputType([string])]
27852841
[CmdletBinding()]
@@ -3113,6 +3169,109 @@ function Test-IsRSATInstalled {
31133169
$false
31143170
}
31153171
}
3172+
function Update-ESC1Remediation {
3173+
<#
3174+
.SYNOPSIS
3175+
This function asks the user a set of questions to provide the most appropriate remediation for ESC1 issues.
3176+
3177+
.DESCRIPTION
3178+
This function takes a single ESC1 issue as input then asks a series of questions to determine the correct
3179+
remediation.
3180+
3181+
Questions:
3182+
TODO: Is this template published?
3183+
1. Does the identified principal need to enroll in this template? [Yes/No/Unsure]
3184+
2. Is this certificate widely used and/or frequently requested? [Yes/No/Unsure]
3185+
3186+
Depending on answers to these questions, the Issue and Fix attributes on the Issue object are updated.
3187+
3188+
.PARAMETER Issue
3189+
A pscustomobject that includes all pertinent information about the ESC1 issue.
3190+
3191+
.OUTPUTS
3192+
This function updates ESC1 remediations customized to the user's needs.
3193+
3194+
.EXAMPLE
3195+
$Targets = Get-Target
3196+
$ADCSObjects = Get-ADCSObject -Targets $Targets
3197+
$SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-9$|-526$|-527$|S-1-5-10'
3198+
$ESC1Issues = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers
3199+
foreach ($issue in $ESC1Issues) { Update-ESC1Remediation -Issue $Issue }
3200+
#>
3201+
[CmdletBinding()]
3202+
param(
3203+
$Issue
3204+
)
3205+
3206+
$Header = "`n[!] ESC1 Issue detected in $($Issue.Name)"
3207+
Write-Host $Header -ForegroundColor Yellow
3208+
Write-Host $('-' * $Header.Length) -ForegroundColor Yellow
3209+
Write-Host "$($Issue.IdentityReference) can provide a Subject Alternative Name (SAN) while enrolling in this"
3210+
Write-Host "template. Manager approval is not required for a certificate to be issued.`n"
3211+
Write-Host 'To provide the most appropriate remediation for this issue, Locksmith will now ask you a few questions.'
3212+
3213+
$Enroll = ''
3214+
do {
3215+
$Enroll = Read-Host "`nDoes $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]"
3216+
} while ( ($Enroll -ne 'y') -and ($Enroll -ne 'n') -and ($Enroll -ne 'unsure'))
3217+
3218+
if ($Enroll -eq 'y') {
3219+
$Frequent = ''
3220+
do {
3221+
$Frequent = Read-Host "`nIs the $($Issue.Name) certificate frequently requested? [y/n/unsure]"
3222+
} while ( ($Frequent -ne 'y') -and ($Frequent -ne 'n') -and ($Frequent -ne 'unsure'))
3223+
3224+
if ($Frequent -ne 'n') {
3225+
$Issue.Fix = @"
3226+
# Locksmith cannot currently determine the best remediation course.
3227+
# Remediation Options:
3228+
# 1. If $($Issue.IdentityReference) is a group, remove its Enroll/AutoEnroll rights and grant those rights
3229+
# to a smaller group or a single user/service account.
3230+
3231+
# 2. Remove the ability to submit a SAN (aka disable "Supply in the request").
3232+
`$Object = `'$($_.DistinguishedName)`'
3233+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0}
3234+
3235+
# 3. Enable Manager Approval
3236+
`$Object = `'$($_.DistinguishedName)`'
3237+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2}
3238+
"@
3239+
3240+
$Issue.Revert = @"
3241+
# 1. Replace Enroll/AutoEnroll rights from the smaller group/single user/service account and grant those rights
3242+
# back to $($Issue.IdentityReference).
3243+
3244+
# 2. Restore the ability to submit a SAN.
3245+
`$Object = `'$($_.DistinguishedName)`'
3246+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1}
3247+
3248+
# 3. Disable Manager Approval
3249+
`$Object = `'$($_.DistinguishedName)`'
3250+
Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0}
3251+
"@
3252+
}
3253+
}
3254+
elseif ($Enroll -eq 'n') {
3255+
$Issue.Fix = @"
3256+
# 1. Open the Certification Templates Console: certtmpl.msc
3257+
# 2. Double-click the $($Issue.Name) template to open its Properties page.
3258+
# 3. Select the Security tab.
3259+
# 4. Select the entry for $($Issue.IdentityReference).
3260+
# 5. Uncheck the "Enroll" and/or "Autoenroll" boxes.
3261+
# 6. Click OK.
3262+
"@
3263+
3264+
$Issue.Revert = @"
3265+
# 1. Open the Certification Templates Console: certtmpl.msc
3266+
# 2. Double-click the $($Issue.Name) template to open its Properties page.
3267+
# 3. Select the Security tab.
3268+
# 4. Select the entry for $($Issue.IdentityReference).
3269+
# 5. Check the "Enroll" and/or "Autoenroll" boxes depending on your specific needs.
3270+
# 6. Click OK.
3271+
"@
3272+
}
3273+
}
3274+
31163275
function Update-ESC4Remediation {
31173276
<#
31183277
.SYNOPSIS
@@ -3121,8 +3280,8 @@ function Update-ESC4Remediation {
31213280
.DESCRIPTION
31223281
This function takes a single ESC4 issue as input. It then prompts the user if the principal with the ESC4 rights
31233282
administers the template in question.
3124-
If the principal is an admin of the template, the Issue attribute to indicate this configuration is expected, and
3125-
the Fix attribute for the issue is updated to indicate no remediation is needed.
3283+
If the principal is an admin of the template, the Issue attribute is updated to indicate this configuration is
3284+
expected, and the Fix attribute for the issue is updated to indicate no remediation is needed.
31263285
If the the principal is not an admin of the template AND the rights assigned is GenericAll, Locksmith will ask
31273286
if Enroll or AutoEnroll rights are needed.
31283287
Depending on the answers to the listed questions, the Fix attribute is updated accordingly.
@@ -3134,14 +3293,14 @@ function Update-ESC4Remediation {
31343293
This function updates ESC4 remediations customized to the user's needs.
31353294
31363295
.EXAMPLE
3137-
$Target = Get-Target
3138-
$ADCSObjects = Get-ADCSObject -Target $Target
3296+
$Targets = Get-Target
3297+
$ADCSObjects = Get-ADCSObject -Targets $Targets
31393298
$DangerousRights = @('GenericAll', 'WriteProperty', 'WriteOwner', 'WriteDacl')
31403299
$SafeOwners = '-512$|-519$|-544$|-18$|-517$|-500$'
31413300
$SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-9$|-526$|-527$|S-1-5-10'
31423301
$SafeObjectTypes = '0e10c968-78fb-11d2-90d4-00c04f79dc55|a05b8cc2-17bc-4802-a710-e7c15ab866a2'
31433302
$ESC4Issues = Find-ESC4 -ADCSObjects $ADCSObjects -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeUsers $SafeUsers -SafeObjectTypes $SafeObjectTypes
3144-
foreach ($issue in $ESCIssues) { Update-ESC4Remediation -Issue $Issue }
3303+
foreach ($issue in $ESC4Issues) { Update-ESC4Remediation -Issue $Issue }
31453304
#>
31463305
[CmdletBinding()]
31473306
param(
@@ -3376,7 +3535,7 @@ function Invoke-Locksmith {
33763535
[System.Management.Automation.PSCredential]$Credential
33773536
)
33783537

3379-
$Version = '2024.11.23'
3538+
$Version = '2024.12.5'
33803539
$LogoPart1 = @"
33813540
_ _____ _______ _ _ _______ _______ _____ _______ _ _
33823541
| | | | |____/ |______ | | | | | |_____|
@@ -3508,16 +3667,18 @@ function Invoke-Locksmith {
35083667
if ($Credential) {
35093668
$ADCSObjects = Get-ADCSObject -Targets $Targets -Credential $Credential
35103669
Set-AdditionalCAProperty -ADCSObjects $ADCSObjects -Credential $Credential -ForestGC $ForestGC
3511-
$ADCSObjects += Get-CAHostObject -ADCSObjects $ADCSObjects -Credential $Credential -ForestGC $ForestGC
35123670
$CAHosts = Get-CAHostObject -ADCSObjects $ADCSObjects -Credential $Credential -ForestGC $ForestGC
3671+
$ADCSObjects += $CAHosts
35133672
}
35143673
else {
35153674
$ADCSObjects = Get-ADCSObject -Targets $Targets
35163675
Set-AdditionalCAProperty -ADCSObjects $ADCSObjects -ForestGC $ForestGC
3517-
$ADCSObjects += Get-CAHostObject -ADCSObjects $ADCSObjects -ForestGC $ForestGC
35183676
$CAHosts = Get-CAHostObject -ADCSObjects $ADCSObjects -ForestGC $ForestGC
3677+
$ADCSObjects += $CAHosts
35193678
}
35203679

3680+
Set-AdditionalTemplateProperty -ADCSObjects $ADCSObjects
3681+
35213682
# Add SIDs of CA Hosts to $SafeUsers
35223683
$CAHosts | ForEach-Object { $SafeUsers += '|' + $_.objectSid }
35233684

@@ -3574,10 +3735,12 @@ function Invoke-Locksmith {
35743735
Write-Host @"
35753736
[!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues
35763737
identified in the environment. For more details including:
3738+
35773739
- DistinguishedName of impacted object(s)
3578-
- Remediation code
3579-
- Revert code (in case remediation breaks something!)
3580-
run Locksmith in Mode 1:
3740+
- Remediation guidance and/or code
3741+
- Revert guidance and/or code (in case remediation breaks something!)
3742+
3743+
Run Locksmith in Mode 1!
35813744
35823745
# Module version
35833746
Invoke-Locksmith -Mode 1

Diff for: Locksmith.psd1

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Description = 'A small tool to find and fix common misconfigurations in Active Directory Certificate Services.'
88
FunctionsToExport = @('*')
99
GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a'
10-
ModuleVersion = '2024.11.23'
10+
ModuleVersion = '2024.12.5'
1111
PowerShellVersion = '5.1'
1212
PrivateData = @{
1313
PSData = @{

Diff for: Private/Find-ESC1.ps1

+8-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
[Parameter(Mandatory)]
3636
[array]$SafeUsers,
3737
[Parameter(Mandatory)]
38-
$ClientAuthEKUs
38+
$ClientAuthEKUs,
39+
[Parameter(Mandatory)]
40+
[int]$Mode
3941
)
4042
$ADCSObjects | Where-Object {
4143
($_.objectClass -eq 'pKICertificateTemplate') -and
@@ -83,6 +85,11 @@ Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0}
8385
"@
8486
Technique = 'ESC1'
8587
}
88+
89+
if ( $Mode -in @(1, 3, 4) ) {
90+
Update-ESC1Remediation -Issue $Issue
91+
}
92+
8693
$Issue
8794
}
8895
}

Diff for: Private/Invoke-Scans.ps1

+3-3
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ function Invoke-Scans {
6868
}
6969
ESC1 {
7070
Write-Host 'Identifying AD CS templates with dangerous ESC1 configurations...'
71-
[array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus
71+
[array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode
7272
}
7373
ESC2 {
7474
Write-Host 'Identifying AD CS templates with dangerous ESC2 configurations...'
@@ -81,7 +81,7 @@ function Invoke-Scans {
8181
}
8282
ESC4 {
8383
Write-Host 'Identifying AD CS templates with poor access control (ESC4)...'
84-
[array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes
84+
[array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -Mode $Mode
8585
}
8686
ESC5 {
8787
Write-Host 'Identifying AD CS objects with poor access control (ESC5)...'
@@ -115,7 +115,7 @@ function Invoke-Scans {
115115
Write-Host 'Identifying auditing issues...'
116116
[array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects
117117
Write-Host 'Identifying AD CS templates with dangerous ESC1 configurations...'
118-
[array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus
118+
[array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode
119119
Write-Host 'Identifying AD CS templates with dangerous ESC2 configurations...'
120120
[array]$ESC2 = Find-ESC2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers
121121
Write-Host 'Identifying AD CS templates with dangerous ESC3 configurations...'

Diff for: Private/Set-AdditionalCAProperty.ps1

+8-3
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,21 @@
7979
$Cache = [System.Net.CredentialCache]::New()
8080
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
8181
$Request.Credentials = $Cache
82-
$Request.Timeout = 3000
82+
$Request.Timeout = 1000
8383
$Request.GetResponse() | Out-Null
8484
$CAEnrollmentEndpoint += @{
8585
'URL' = $FullURL
8686
'Auth' = $Auth
8787
}
8888
} catch {
8989
try {
90+
$Auth = 'NTLM'
9091
$FullURL = "https$URL"
9192
$Request = [System.Net.WebRequest]::Create($FullURL)
92-
93+
$Cache = [System.Net.CredentialCache]::New()
94+
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
95+
$Request.Credentials = $Cache
96+
$Request.Timeout = 1000
9397
$Request.GetResponse() | Out-Null
9498
$CAEnrollmentEndpoint += @{
9599
'URL' = $FullURL
@@ -101,8 +105,9 @@
101105
$FullURL = "https$URL"
102106
$Request = [System.Net.WebRequest]::Create($FullURL)
103107
$Cache = [System.Net.CredentialCache]::New()
104-
$Cache.Add([System.Uri]::new($FullURL), 'Negotiate', [System.Net.CredentialCache]::DefaultNetworkCredentials)
108+
$Cache.Add([System.Uri]::new($FullURL), $Auth, [System.Net.CredentialCache]::DefaultNetworkCredentials)
105109
$Request.Credentials = $Cache
110+
$Request.Timeout = 1000
106111
$Request.GetResponse() | Out-Null
107112
$CAEnrollmentEndpoint += @{
108113
'URL' = $FullURL

0 commit comments

Comments
 (0)