diff --git a/Build/Build-Module.ps1 b/Build/Build-Module.ps1 index 527ef38..6ea0a70 100644 --- a/Build/Build-Module.ps1 +++ b/Build/Build-Module.ps1 @@ -129,7 +129,7 @@ Build-Module -ModuleName 'Locksmith' { # The scans to run. Defaults to 'All'. [Parameter()] - [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')] [array]$Scans = 'All' ) } diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index 375745d..dbd8022 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -7,7 +7,7 @@ param ( # The scans to run. Defaults to 'All'. [Parameter()] - [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')] [array]$Scans = 'All' ) function Convert-IdentityReferenceToSid { @@ -299,12 +299,14 @@ Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} Technique = 'ESC1' } - if ( $Mode -in @(1, 3, 4) ) { - Update-ESC1Remediation -Issue $Issue - } if ($SkipRisk -eq $false) { Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC1Remediation -Issue $Issue + } + $Issue } } @@ -602,6 +604,82 @@ Invoke-WebRequest -Uri https://gist.githubusercontent.com/jakehildreth/13c7d615a } } +function Find-ESC16 { + <# + .SYNOPSIS + This script finds Active Directory Certificate Services (AD CS) Certification Authorities (CA) that have the ESC16 vulnerability. + + .DESCRIPTION + The script takes an array of ADCS objects as input and filters them based on objects that have the objectClass + 'pKIEnrollmentService' and the szOID_NTDS_CA_SECURITY_EXT disabled. For each matching object, it creates a custom object with + properties representing various information about the object, such as Forest, Name, DistinguishedName, Technique, + Issue, Fix, and Revert. + + .PARAMETER ADCSObjects + Specifies the array of AD CS objects to be processed. This parameter is mandatory. + + .OUTPUTS + The script outputs an array of custom objects representing the matching ADCS objects and their associated information. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [string]$UnsafeUsers, + [switch]$SkipRisk + ) + process { + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKIEnrollmentService') -and + ($_.DisableExtensionList -ne 'No') + } | ForEach-Object { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + Issue = $_.DisableExtensionList + Fix = 'N/A' + Revert = 'N/A' + Technique = '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 +user or computer account's SID for authentication. + +More info: + - https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc16-security-extension-disabled-on-ca-globally + +"@ + $Issue.Fix = @" +# Enable the flag +# TODO + +# Restart the Certificate Authority service +Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock { + Get-Service -Name certsvc | Restart-Service -Force +} +"@ + $Issue.Revert = @" +# Disable the flag +TODO + +# Restart the Certificate Authority service +Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock { + Get-Service -Name certsvc | Restart-Service -Force +} +"@ + } + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + $Issue + } + } +} + function Find-ESC2 { <# .SYNOPSIS @@ -769,7 +847,7 @@ function Find-ESC3C1 { $($entry.IdentityReference) can use this template to request an Enrollment Agent certificate without Manager Approval. -The resulting certificate can be used to enroll in any template that requires +The resulting certificate can be used to enroll in any template that allows an Enrollment Agent to submit the request. More info: @@ -1068,12 +1146,14 @@ Set-Acl -Path 'AD:$($_.DistinguishedName)' -AclObject `$ACL Technique = 'ESC4' } - if ( $Mode -in @(1, 3, 4) ) { - Update-ESC4Remediation -Issue $Issue - } if ($SkipRisk -eq $false) { Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC4Remediation -Issue $Issue + } + $Issue } } @@ -1406,6 +1486,7 @@ function Find-ESC6 { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name + CAFullname = $CAFullName DistinguishedName = $_.DistinguishedName Issue = $_.SANFlag Fix = 'N/A' @@ -1456,6 +1537,121 @@ Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock { } } +function Find-ESC7 { + <# + .SYNOPSIS + This script finds Active Directory Certificate Services (AD CS) Certificate Authorities (CA) that have the ESC7 vulnerability. + + .DESCRIPTION + The script takes an array of AD CS objects as input and filters them based on objects that have the objectClass + 'pKIEnrollmentService'. If the CA objects have non-standard/unsafe principals as administrators or managers, an issue is created. + + .PARAMETER ADCSObjects + Specifies the array of AD CS objects to be processed. This parameter is mandatory. + + .PARAMETER UnsafeUsers + Principals that should never be granted control of a CA. + + .PARAMETER SafeUsers + Principals that are generally recognized as safe to control a CA. + + .PARAMETER SkipRisk + Switch used when processing second-order risks. + + .OUTPUTS + The script outputs an array of custom objects representing the matching AD CS objects and their associated information. + + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [string]$UnsafeUsers, + [Parameter(Mandatory)] + [string]$SafeUsers, + [switch]$SkipRisk + ) + process { + Write-Output $ADCSObjects -PipelineVariable object | Where-Object { + ($object.objectClass -eq 'pKIEnrollmentService') -and $object.CAHostDistinguishedName -and + ( ($object.CAAdministrator) -or ($object.CertificateManager) ) + } | ForEach-Object { + Write-Output $object.CAAdministrator -PipelineVariable admin | ForEach-Object { + $SID = Convert-IdentityReferenceToSid -Object $admin + if ($SID -notmatch $SafeUsers) { + $Issue = [pscustomobject]@{ + Forest = $object.CanonicalName.split('/')[0] + Name = $object.Name + DistinguishedName = $object.DistinguishedName + IdentityReference = $admin + IdentityReferenceSID = $SID + Right = 'CA Administrator' + Issue = @" +$admin has been granted CA Administrator rights on this Certification Authority (CA). + +$admin has full control over this CA. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + Fix = "Revoke CA Administrator rights from ${admin}." + Revert = "Restore CA Administrator rights to ${admin}." + Technique = 'ESC7' + } + + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC7Remediation -Issue $Issue + } + + $Issue + } + } + + Write-Output $object.CertificateManager -PipelineVariable admin | ForEach-Object { + $SID = Convert-IdentityReferenceToSid -Object $admin + if ($SID -notmatch $SafeUsers) { + $Issue = [pscustomobject]@{ + Forest = $object.CanonicalName.split('/')[0] + Name = $object.Name + DistinguishedName = $object.DistinguishedName + IdentityReference = $admin + IdentityReferenceSID = $SID + Right = 'Certificate Manager' + Issue = @" +$admin has been granted Certificate Manager rights on this Certification Authority (CA). + +$admin can approve pending certificate requests on this CA. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + Fix = "Revoke Certificate Manager rights from ${admin}." + Revert = "Restore Certificate Manager rights to ${admin}." + Technique = 'ESC7' + } + + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC7Remediation -Issue $Issue + } + + $Issue + } + } + } + } +} + function Find-ESC8 { <# .SYNOPSIS @@ -1553,119 +1749,115 @@ Disable NTLM authentication (if possible.) } } -<# - This is a working POC. I need to test both checks and possibly blend pieces of them. - Then I need to fold this function into the Locksmith workflow. -#> - function Find-ESC9 { <# .SYNOPSIS - Checks for ESC9 (No Security Extension) Vulnerability + This function finds Active Directory Certificate Services (AD CS) objects that have the ESC9 vulnerability. .DESCRIPTION - This function checks for certificate templates that contain the flag CT_FLAG_NO_SECURITY_EXTENSION (0x80000), - which will likely make them vulnerable to ESC9. Another factor to check for ESC9 is the registry values on AD - domain controllers that can help harden certificate based authentication for Kerberos and SChannel. + The script takes an array of ADCS objects as input and filters them based on the specified conditions. + For each matching object, it creates a custom object with properties representing various information about + the object, such as Forest, Name, DistinguishedName, IdentityReference, ActiveDirectoryRights, Issue, Fix, Revert, and Technique. - .NOTES - An ESC9 condition exists when: - - - the new msPKI-Enrollment-Flag value on a certificate contains the flag CT_FLAG_NO_SECURITY_EXTENSION (0x80000) - - AND an insecure registry value is set on domain controllers: - - - the StrongCertificateBindingEnforcement registry value for Kerberos is not set to 2 (the default is 1) on domain controllers - at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc - - OR the CertificateMappingMethods registry value for SCHANNEL contains the UPN flag on domain controllers at - HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel - - When the CT_FLAG_NO_SECURITY_EXTENSION (0x80000) flag is set on a certificate template, the new szOID_NTDS_CA_SECURITY_EXT - security extension will not be embedded in issued certificates. This security extension was added by Microsoft's - patch KB5014754 ("Certificate-based authentication changes on Windows domain controllers") on May 10, 2022. - - The patch applies to all servers that run Active Directory Certificate Services and Windows domain controllers that - service certificate-based authentication. - https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16 - - Based on research from - https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7, - https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16, - and on a very long conversation with Bing Chat. - - Additional notes from Cortana -- Bing when I pressed her to tell me whether both conditions were required for ESC9 or only one of them: - A certificate template can still be vulnerable to ESC9 even if the msPKI-Enrollment-Flag does not include - CT_FLAG_NO_SECURITY_EXTENSION. This is because the vulnerability primarily arises from the ability of a - requester to specify the subjectAltName in a Certificate Signing Request (CSR). If a requester can specify - the subjectAltName in a CSR, they can request a certificate as anyone, including a domain admin user. - Therefore, if a certificate template allows requesters to specify a subjectAltName and - StrongCertificateBindingEnforcement is not set to 2, it could potentially be vulnerable to ESC9. However, - the presence of CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag is a clear indicator of a template - being vulnerable to ESC9. -#> + .PARAMETER ADCSObjects + Specifies the array of ADCS objects to be processed. This parameter is mandatory. + + .PARAMETER SafeUsers + Specifies the list of SIDs of safe users who are allowed to have specific rights on the objects. This parameter is mandatory. + .PARAMETER UnsafeUsers + Specifies the list of SIDs of safe users who should never have specific rights on the objects. This parameter is mandatory. + + .PARAMETER ClientAuthEKUs + A list of EKUs that can be used for client authentication. + + .OUTPUTS + The script outputs an array of custom objects representing the matching ADCS objects and their associated information. + + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $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' + $Results = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs + $Results + #> [CmdletBinding()] param( [Parameter(Mandatory)] [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, [Parameter(Mandatory)] + [string]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs, + [int]$Mode = 0, + [Parameter(Mandatory)] [string]$UnsafeUsers, [switch]$SkipRisk ) - - # Import the required module - Import-Module ActiveDirectory - - # Get the configuration naming context - $configNC = (Get-ADRootDSE).configurationNamingContext - - # Define the path to the Certificate Templates container - $path = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC" - - # Get all certificate templates - $templates = Get-ADObject -Filter * -SearchBase $path -Properties msPKI-Enrollment-Flag, msPKI-Certificate-Name-Flag - - foreach ($template in $templates) { - # Check if msPKI-Enrollment-Flag contains the CT_FLAG_NO_SECURITY_EXTENSION (0x80000) flag - if ($template.'msPKI-Enrollment-Flag' -band 0x80000) { - # Check if msPKI-Certificate-Name-Flag contains the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME (0x2) flag - if ($template.'msPKI-Certificate-Name-Flag' -band 0x2) { - # Output the template name - Write-Output "Template Name: $($template.Name), Vulnerable to ESC9" + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKICertificateTemplate') -and + ($_.pkiExtendedKeyUsage -match $ClientAuthEKUs) -and + ($_.'msPKI-Enrollment-Flag' -band 0x80000) + } | ForEach-Object { + foreach ($entry in $_.nTSecurityDescriptor.Access) { + $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) + if ($Principal -match '^(S-1|O:)') { + $SID = $Principal } - } - } + else { + $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value + } + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + IdentityReference = $entry.IdentityReference + IdentityReferenceSID = $SID + ActiveDirectoryRights = $entry.ActiveDirectoryRights + Enabled = $_.Enabled + EnabledOn = $_.EnabledOn + Issue = @" +This Client Authentication template has the szOID_NTDS_CA_SECURITY_EXT security +extension disabled. Certificates issued from this template will not enforce +strong certificate binding. Depending on the current Certificate Binding +Enforcement level ESC6 status, it may be possible to request and receive +certificates that rely on weak (aka attacker-controllable) binding methods. + +An attacker can abuse this weakness by: +1. Getting access to a user or computer account. +2. Modifying the user's userPrincipalName attribute (or the computer's dNSHostName attribute) to match a higher-privileged account. +3. Requesting a client authentication certificate from a template that w/ szOID_NTDS_CA_SECURITY_EXT disabled. +4. Using the client authentication certificiate to authenticate as the higher-privileged account. +5. Profiting. - # AND / OR / ALSO +More info: + - ESC9 description: https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc9-no-security-extension-on-certificate-template + - Strong Mapping/Enforcement Mode: https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16 - Import-Module ActiveDirectory +"@ + Fix = @" +# Enable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} +"@ + Revert = @" +# Disable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} +"@ + Technique = 'ESC9' + } - $templates = Get-ADObject -Filter { ObjectClass -eq 'pKICertificateTemplate' } -Properties * - foreach ($template in $templates) { - $name = $template.Name - - $subjectNameFlag = $template.'msPKI-Cert-Template-OID' - $subjectType = $template.'msPKI-Certificate-Application-Policy' - $enrollmentFlag = $template.'msPKI-Enrollment-Flag' - $certificateNameFlag = $template.'msPKI-Certificate-Name-Flag' - - # Check if the template is vulnerable to ESC9 - if ($subjectNameFlag -eq 'Supply in the request' -and - ($subjectType -eq 'User' -or $subjectType -eq 'Computer') -and - # 0x200 means a certificate needs to include a template name certificate extension - # 0x220 instructs the client to perform auto-enrollment for the specified template - ($enrollmentFlag -eq 0x200 -or $enrollmentFlag -eq 0x220) -and - # 0x2 instructs the client to supply subject information in the certificate request (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT). - # This means that any user who is allowed to enroll in a certificate with this setting can request a certificate as any - # user in the network, including a privileged user. - # 0x3 instructs the client to supply both the subject and subject alternate name information in the certificate request - ($certificateNameFlag -eq 0x2 -or $certificateNameFlag -eq 0x3)) { - - # Print the template name and the vulnerability - Write-Output "$name is vulnerable to ESC9" - } - else { - # Print the template name and the status - Write-Output "$name is not vulnerable to ESC9" + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC9Remediation -Issue $Issue + } + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + $Issue + } } } } @@ -1693,7 +1885,7 @@ function Format-Result { Formats the issue result in list format. .NOTES - Author: Spencer Alessi + Authors: Spencer Alessi & Jake Hildreth #> [CmdletBinding()] param( @@ -1710,10 +1902,13 @@ function Format-Result { ESC4 = 'ESC4 - Vulnerable Access Control - Certificate Template' ESC5 = 'ESC5 - Vulnerable Access Control - PKI Object' ESC6 = 'ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2 Flag Enabled' + ESC7 = 'ESC7 - Non-standard PKI Admins' ESC8 = 'ESC8 - HTTP/S Enrollment Enabled' + ESC9 = 'ESC9 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled on Template' ESC11 = 'ESC11 - IF_ENFORCEENCRYPTICERTREQUEST Flag Disabled' ESC13 = 'ESC13 - Vulnerable Certificate Template - Group-Linked' 'ESC15/EKUwu' = 'ESC15 - Vulnerable Certificate Template - Schema V1' + ESC16 = 'ESC16 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled on CA' } $RiskTable = @{ @@ -1737,12 +1932,12 @@ function Format-Result { if ($Mode -eq 0) { # TODO Refactor this switch ($UniqueIssue) { - { $_ -in @('DETECT', 'ESC6', 'ESC8', 'ESC11') } { + { $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } { $Issue | Format-Table Technique, @{l = 'CA Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Issue -Wrap | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive } - { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC13', 'ESC15/EKUwu') } { + { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu') } { $Issue | Format-Table Technique, @{l = 'Template Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Enabled, Issue -Wrap | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive @@ -1756,12 +1951,12 @@ function Format-Result { } elseif ($Mode -eq 1) { switch ($UniqueIssue) { - { $_ -in @('DETECT', 'ESC6', 'ESC8', 'ESC11') } { + { $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } { $Issue | 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" } } | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive } - { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC13', 'ESC15/EKUwu') } { + { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu') } { $Issue | Format-List Technique, @{l = 'Template Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, DistinguishedName, Enabled, EnabledOn, Issue, Fix, @{l = 'Risk Score'; e = { $_.RiskValue } }, @{l = 'Risk Score Detail'; e = { $_.RiskScoring -join "`n" } } | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive @@ -1857,12 +2052,22 @@ function Get-CAHostObject { process { if ($Credential) { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { - Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential + if ($_.CAHostDistinguishedName) { + Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential + } + else { + Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" + } } } else { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { - Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC + if ($_.CAHostDistinguishedName) { + Get-ADObject -Identity $_.CAHostDistinguishedName -Properties * -Server $ForestGC + } + else { + Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" + } } } } @@ -2405,12 +2610,12 @@ function Invoke-Scans { .PARAMETER Scans Specifies the type of scans to perform. Multiple scan options can be provided as an array. The default value is 'All'. - The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', - 'ESC13', 'ESC15, 'EKUwu', 'All', 'PromptMe'. + The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC9', 'ESC11', + 'ESC13', 'ESC15, 'EKUwu', 'ESC16', 'All', 'PromptMe'. .NOTES - The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3C1, - Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13, Find-ESC15 + Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC9, Find-ESC11, Find-ESC13, Find-ESC15, Find-ESC16 - The script uses Out-GridView or Out-ConsoleGridView for interactive selection when the 'PromptMe' scan option is chosen. - The script returns a hash table containing the results of the scans. @@ -2448,7 +2653,7 @@ function Invoke-Scans { [string]$SafeUsers, [Parameter(Mandatory)] [string]$SafeOwners, - [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')] [array]$Scans = 'All', [Parameter(Mandatory)] [string]$UnsafeUsers, @@ -2503,10 +2708,18 @@ function Invoke-Scans { Write-Host 'Identifying Issuing CAs with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } + ESC7 { + Write-Host 'Identifying Issuing CAs with Non-Standard Admins (ESC7)...' + [array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers + } ESC8 { Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } + ESC9 { + Write-Host 'Identifying AD CS templates with szOID_NTDS_CA_SECURITY_EXT disabled (ESC9)...' + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode -UnsafeUsers $UnsafeUsers + } ESC11 { Write-Host 'Identifying Issuing CAs with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers @@ -2523,6 +2736,10 @@ function Invoke-Scans { Write-Host 'Identifying AD CS templates with dangerous ESC15/EKUwu configurations...' [array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers } + ESC16 { + Write-Host 'Identifying Issuing CAs with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...' + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + } All { Write-Host 'Identifying auditing issues...' [array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects @@ -2539,23 +2756,28 @@ function Invoke-Scans { [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -UnsafeUsers $UnsafeUsers Write-Host 'Identifying Certificate Authorities with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + Write-Host 'Identifying Certificate Authorities with Non-Standard Admins (ESC7)...' + [array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + Write-Host 'Identifying AD CS templates with szOID_NTDS_CA_SECURITY_EXT disabled (ESC9)...' + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode -UnsafeUsers $UnsafeUsers Write-Host 'Identifying Certificate Authorities with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC13 configurations...' [array]$ESC13 = Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -UnsafeUsers $UnsafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC15 configurations...' [array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers - Write-Host + Write-Host 'Identifying Certificate Authorities with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...' + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } } - [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + $ESC11 + $ESC13 + $ESC15 + [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC9 + $ESC11 + $ESC13 + $ESC15 + $ESC16 # If these are all empty = no issues found, exit if ($AllIssues.Count -lt 1) { - Write-Host "`n$(Get-Date) : No ADCS issues were found." -ForegroundColor Green + Write-Host "`n$(Get-Date) : No ADCS issues were found. :)" -ForegroundColor Green break } @@ -2569,10 +2791,13 @@ function Invoke-Scans { ESC4 = $ESC4 ESC5 = $ESC5 ESC6 = $ESC6 + ESC7 = $ESC7 ESC8 = $ESC8 + ESC9 = $ESC9 ESC11 = $ESC11 ESC13 = $ESC13 ESC15 = $ESC15 + ESC16 = $ESC16 } } @@ -2672,7 +2897,7 @@ function New-Dictionary { Category = 'Escalation Path' Subcategory = 'Vulnerable Certificate Authority Access Control' Summary = '' - FindIt = { Write-Output 'We have not created Find-ESC7 yet.' } + FindIt = { Find-ESC7 } FixIt = { Write-Output 'Add code to fix the vulnerable configuration.' } ReferenceUrls = 'https://posts.specterops.io/certified-pre-owned-d95910965cd2#:~:text=Vulnerable%20Certificate%20Authority%20Access%20Control%20%E2%80%94%20ESC7' }, @@ -2799,6 +3024,8 @@ function Set-AdditionalCAProperty { Date: July 15, 2022 #> + # TODO REfactor to move the creation of each property into its own function + [CmdletBinding(SupportsShouldProcess)] param ( [parameter( @@ -2946,11 +3173,37 @@ function Set-AdditionalCAProperty { catch { $InterfaceFlag = 'Failure' } + try { + if ($Credential) { + $CertutilSecurity = Invoke-Command -ComputerName $CAHostFQDN -Credential $Credential -ScriptBlock { certutil -config $using:CAFullName -getreg CA\Security } + } + else { + $CertutilSecurity = certutil -config $CAFullName -getreg CA\Security + } + } + catch { + $CAAdministrator = 'Failure' + $CertificateManager = 'Failure' + } + try { + if ($Credential) { + $CertutilDisableExtensionList = Invoke-Command -ComputerName $CAHostFQDN -Credential $Credential -ScriptBlock { certutil -config $using:CAFullName -getreg policy\DisableExtensionList } + } + else { + $CertutilDisableExtensionList = certutil -config $CAFullName -getreg policy\DisableExtensionList + } + } + catch { + $CertutilDisableExtensionList = 'Failure' + } } else { $AuditFilter = 'CA Unavailable' $SANFlag = 'CA Unavailable' $InterfaceFlag = 'CA Unavailable' + $CAAdministrator = 'CA Unavailable' + $CertificateManager = 'CA Unavailable' + $DisableExtensionList = 'CA Unavailable' } if ($CertutilAudit) { try { @@ -2985,6 +3238,27 @@ function Set-AdditionalCAProperty { $InterfaceFlag = 'No' } } + if ($CertutilSecurity) { + [string[]]$CAAdministrator = $CertutilSecurity | ForEach-Object { + if ($_ -match '^.*Allow.*CA Administrator.*.*\t(.*)$') { + $matches[1].ToString() + } + } + [string[]]$CertificateManager = $CertutilSecurity | ForEach-Object { + if ($_ -match '^.*Allow.*Certificate Manager.*\t(.*)$') { + $matches[1].ToString() + } + } + } + if ($CertutilDisableExtensionList) { + [string]$DisableExtensionList = $CertutilDisableExtensionList | Select-String '1\.3\.6\.1\.4\.1\.311\.25\.2' + if ($DisableExtensionList) { + $DisableExtensionList = 'Yes' + } + else { + $DisableExtensionList = 'No' + } + } Add-Member -InputObject $_ -MemberType NoteProperty -Name AuditFilter -Value $AuditFilter -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name CAEnrollmentEndpoint -Value $CAEnrollmentEndpoint -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name CAFullName -Value $CAFullName -Force @@ -2992,6 +3266,9 @@ function Set-AdditionalCAProperty { Add-Member -InputObject $_ -MemberType NoteProperty -Name CAHostDistinguishedName -Value $CAHostDistinguishedName -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name SANFlag -Value $SANFlag -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name InterfaceFlag -Value $InterfaceFlag -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name CAAdministrator -Value $CAAdministrator -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name CertificateManager -Value $CertificateManager -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name DisableExtensionList -Value $DisableExtensionList -Force } } } @@ -3099,7 +3376,7 @@ function Set-RiskRating { $RiskScoring = @() # CA issues don't rely on a principal and have a base risk of Medium. - if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC8', 'ESC11')) { + if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) { $RiskValue += 3 $RiskScoring += 'Base Score: 3' @@ -3108,12 +3385,49 @@ function Set-RiskRating { $RiskScoring += 'HTTP Enrollment: +2' } - # TODO Check NtAuthCertificates for CA thumbnail. If found, +2, else -1 + if ($Issue.Technique -eq 'ESC7') { + # If an Issue can be tied to a principal, the principal's objectClass impacts the Issue's risk + $SID = $Issue.IdentityReferenceSID.ToString() + $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass + + if ($Issue.IdentityReferenceSID -match $UnsafeUsers) { + # Authenticated Users, Domain Users, Domain Computers etc. are very risky + $RiskValue += 2 + $RiskScoring += 'Very Large Group: +2' + } + elseif ($IdentityReferenceObjectClass -eq 'group') { + # Groups are riskier than individual principals + $RiskValue += 1 + $RiskScoring += 'Group: +1' + } + elseif ($Issue.IdentityReferenceSID -notmatch $UnsafeUsers -and + $Issue.IdentityReferenceSID -notmatch $SafeUsers -and + $IdentityReferenceObjectClass -notlike '*ManagedServiceAccount') { + $RiskValue += 1 + $RiskScoring += 'Unprivileged Principal: +1' + } + } + + # Modifiers that rely on the existence of other ESCs + if ($Issue.Technique -eq 'ESC6') { + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -UnsafeUsers $UnsafeUsers + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + $ESC9and16 = $ESC9 + $ESC16 + if ($ESC9and16) { + $RiskValue += 2 + $RiskScoring += "Additional risky configurations exist which make this issue more severe: +2" + foreach ($otherIssue in $ESC9and16) { + $RiskScoring += " - $($otherIssue.Technique) exists on $($otherIssue.Name)" + } + } # end if ($ESC9and16) + } + + # TODO Check NtAuthCertificates for CA thumbprint. If found, +2, else -1 # TODO Check if NTLMv1 is allowed. } # Template and object issues rely on a principal and have complex scoring. - if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC8', 'ESC11')) { + if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) { $RiskScoring += 'Base Score: 0' # Templates are more dangerous when enabled, but objects cannot be enabled/disabled. @@ -3128,16 +3442,17 @@ function Set-RiskRating { } } - # The principal's objectClass impacts the Issue's risk - $SID = $Issue.IdentityReferenceSID.ToString() - $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass - # ESC1 and ESC4 templates are more dangerous than other templates because they can result in immediate compromise. if ($Issue.Technique -in @('ESC1', 'ESC4')) { $RiskValue += 1 - $RiskScoring += 'ESC1/4: +1' + $RiskScoring += "$($Issue.Technique) +1" } + # If an Issue can be tied to a principal, the principal's objectClass impacts the Issue's risk + $SID = $Issue.IdentityReferenceSID.ToString() + $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass + + if ($Issue.IdentityReferenceSID -match $UnsafeUsers) { # Authenticated Users, Domain Users, Domain Computers etc. are very risky $RiskValue += 2 @@ -3148,9 +3463,15 @@ function Set-RiskRating { $RiskValue += 1 $RiskScoring += 'Group: +1' } + elseif ($Issue.IdentityReferenceSID -notmatch $UnsafeUsers -and + $Issue.IdentityReferenceSID -notmatch $SafeUsers -and + $IdentityReferenceObjectClass -notlike '*ManagedServiceAccount') { + $RiskValue += 1 + $RiskScoring += 'Unprivileged Principal: +1' + } - # Safe users and managed service accounts are inherently safer than other principals - except in ESC3 Condition 2! - if ($Issue.Technique -eq 'ESC3' -and $Issue.Condition -eq 2) { + # Safe users and managed service accounts are inherently safer than other principals - except in ESC3 Condition 2 and ESC9! + if (($Issue.Technique -eq 'ESC9') -or ($Issue.Technique -eq 'ESC3' -and $Issue.Condition -eq 2)) { if ($Issue.IdentityReferenceSID -match $SafeUsers) { # Safe Users are admins. Authenticating as an admin is bad. $RiskValue += 2 @@ -3354,8 +3675,8 @@ function Set-RiskRating { $RiskValue += $OtherTemplateRisk } - # Disabled ESC1, ESC2, ESC3, ESC4, and ESC15 templates are more dangerous if there's an ESC5 on one or more CA objects - if ($Issue.Technique -match 'ESC1|ESC2|ESC3|ESC4' -and $Issue.Enabled -eq $false ) { + # Disabled ESC1, ESC2, ESC3, ESC4, ESC9, and ESC15 templates are more dangerous if there's an ESC5 on one or more CA objects + if ($Issue.Technique -match 'ESC1|ESC2|ESC3|ESC4|ESC9' -and $Issue.Enabled -eq $false ) { $ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers -DangerousRights $DangerousRights -SafeOwners '-519$' -SafeObjectTypes $SafeObjectTypes -SkipRisk | Where-Object { $_.objectClass -eq 'pKIEnrollmentService' } $ESC5Names = @(($ESC5 | Select-Object -Property Name -Unique).Name) @@ -3428,6 +3749,18 @@ function Set-RiskRating { } } + # ESC9/ESC16 are much more dangerous when ESC6 exists + if ($Issue.Technique -match 'ESC9|ESC16') { + $ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SkipRisk + if ($ESC6) { + $RiskValue += 2 + $RiskScoring += "One or more CAs have an ESC6: +2" + foreach ($ca in $ESC6.CAFullName) { + $RiskScoring += " - ESC6 exists on $ca" + } + } # end if ($ESC6) + } + # Convert Value to Name $RiskName = switch ($RiskValue) { { $_ -le 1 } { @@ -3797,13 +4130,13 @@ function Update-ESC1Remediation { $Enroll = '' do { - $Enroll = Read-Host "`nDoes $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" + $Enroll = Read-Host "`n[?] Does $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" } while ( ($Enroll -ne 'y') -and ($Enroll -ne 'n') -and ($Enroll -ne 'unsure')) if ($Enroll -eq 'y') { $Frequent = '' do { - $Frequent = Read-Host "`nIs the $($Issue.Name) certificate frequently requested? [y/n/unsure]" + $Frequent = Read-Host "`n[?] Is the $($Issue.Name) certificate frequently requested? [y/n/unsure]" } while ( ($Frequent -ne 'y') -and ($Frequent -ne 'n') -and ($Frequent -ne 'unsure')) if ($Frequent -ne 'n') { @@ -3869,8 +4202,10 @@ function Update-ESC4Remediation { .DESCRIPTION This function takes a single ESC4 issue as input. It then prompts the user if the principal with the ESC4 rights administers the template in question. - If the principal is an admin of the template, the Issue attribute is updated to indicate this configuration is - expected, and the Fix attribute for the issue is updated to indicate no remediation is needed. + If the principal is an admin of the template: + - the Issue attribute is updated to indicate this configuration is expected + - the Fix attribute for the issue is updated to indicate no remediation is needed + - the Risk Value, Risk Name, and Risk Scoring Details are updated to indicate no risk If the the principal is not an admin of the template AND the rights assigned is GenericAll, Locksmith will ask if Enroll or AutoEnroll rights are needed. Depending on the answers to the listed questions, the Fix attribute is updated accordingly. @@ -3905,12 +4240,20 @@ function Update-ESC4Remediation { $Admin = '' do { - $Admin = Read-Host "`nDoes $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) template? [y/n]" + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) template? [y/n]" } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) if ($Admin -eq 'y') { - $Issue.Issue = "$($Issue.IdentityReference) has $($Issue.ActiveDirectoryRights) rights on this template, but this is expected." + $Issue.Issue = @" +$($Issue.IdentityReference) has $($Issue.ActiveDirectoryRights) rights on this template, but this is expected. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 +"@ $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) administers this template" -Force } elseif ($Issue.Issue -match 'GenericAll') { $RightsToRestore = 0 @@ -4020,6 +4363,214 @@ Set-Acl -Path `$Path -AclObject `$ACL } # end elseif ($Issue.Issue -match 'GenericAll') } +function Update-ESC7Remediation { + <# + .SYNOPSIS + This function asks the user a set of questions to provide the most appropriate remediation for ESC7 issues. + + .DESCRIPTION + This function takes a single ESC7 issue as input. It then prompts the user if the principal with the ESC7 rights + administers the Certification Authority (CA) in question. + If the principal is an admin of the CA: + - the Issue attribute is updated to indicate this configuration is expected + - the Fix attribute for the issue is updated to indicate no remediation is needed + - the Risk Value and Risk Scoring are set to + If the the principal is not an admin of the CA, + Depending on the answers to the listed questions, the Fix attribute is updated accordingly. + + .PARAMETER Issue + A pscustomobject that includes all pertinent information about the ESC7 issue. + + .OUTPUTS + This function updates ESC7 remediations customized to the user's needs. + + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $DangerousRights = @('GenericAll', 'WriteProperty', 'WriteOwner', 'WriteDacl') + $SafeOwners = '-512$|-519$|-544$|-18$|-517$|-500$' + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $SafeObjectTypes = '0e10c968-78fb-11d2-90d4-00c04f79dc55|a05b8cc2-17bc-4802-a710-e7c15ab866a2' + $ESC7Issues = Find-ESC7 -ADCSObjects $ADCSObjects -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeUsers $SafeUsers -SafeObjectTypes $SafeObjectTypes -Mode 1 + foreach ($issue in $ESC7Issues) { Update-ESC7Remediation -Issue $Issue } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Issue + ) + + if ($Issue.Right -eq 'CA Administrator') { + $Header = "`n[!] ESC7 Issue detected on $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host "$($Issue.IdentityReference) has CA Administrator rights on this Certification Authority (CA).`n" + Write-Host 'To provide the most appropriate remediation for this issue, Locksmith will now ask you a few questions.' + + $Admin = '' + do { + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) CA? [y/n]" + } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) + + if ($Admin -eq 'y') { + $Issue.Issue = @" +$($Issue.IdentityReference) has CA Administrator rights on this CA, but this is expected. + +Note: +These rights grant $($Issue.IdentityReference) control of the forest. +This principal should be considered a Tier 0/control plane object and protected as such. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) administers this CA" -Force + } + } + + if ($Issue.Right -eq 'Certificate Manager') { + $Header = "`n[!] ESC7 Issue detected on $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host "$($Issue.IdentityReference) has Certificate Manager rights on this Certification Authority (CA).`n" + Write-Host 'To provide the most appropriate remediation for this issue, Locksmith will now ask you a few questions.' + + $Admin = '' + do { + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) need to approve pending certificate requests on the $($Issue.Name) CA? [y/n]" + } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) + + if ($Admin -eq 'y') { + $Issue.Issue = @" +$($Issue.IdentityReference) has Certificate Manager rights on this CA, but this is expected. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) approves pending certificate requests on this CA" -Force + } + } +} + +function Update-ESC9Remediation { + <# + .SYNOPSIS + This function asks the user a set of questions to provide the most appropriate remediation for ESC9 issues. + + .DESCRIPTION + This function takes a single ESC9 issue as input then asks a series of questions to determine the correct + remediation. + + Questions: + 1. Does the identified principal need to enroll in this template? [Yes/No/Unsure] + 2. Is this certificate widely used and/or frequently requested? [Yes/No/Unsure] + + Depending on answers to these questions, the Issue and Fix attributes on the Issue object are updated. + + TODO: More questions: + Should the identified principal be able to request certs that include a SAN or SANs? + + .PARAMETER Issue + A pscustomobject that includes all pertinent information about the ESC1 issue. + + .OUTPUTS + This function updates ESC9 remediations customized to the user's needs. + + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $ESC9Issues = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers + foreach ($issue in $ESC9Issues) { Update-ESC9Remediation -Issue $Issue } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Issue + ) + + $Header = "`n[!] ESC9 Issue detected in $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host @" +The $($Issue.Name) template has the szOID_NTDS_CA_SECURITY_EXT security extension +disabled. Certificates issued from this template will not enforce strong +certificate binding. Manager approval is not required for a certificate to be issued. +To provide the most appropriate remediation for this issue, Locksmith will now +ask you a few questions. +"@ + + $Enroll = '' + do { + $Enroll = Read-Host "`n[?] Does $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" + } while ( ($Enroll -ne 'y') -and ($Enroll -ne 'n') -and ($Enroll -ne 'unsure')) + + if ($Enroll -eq 'y') { + $Frequent = '' + do { + $Frequent = Read-Host "`n[?] Is the $($Issue.Name) certificate frequently requested? [y/n/unsure]" + } while ( ($Frequent -ne 'y') -and ($Frequent -ne 'n') -and ($Frequent -ne 'unsure')) + + if ($Frequent -ne 'n') { + $Issue.Fix = @" +# Locksmith cannot currently determine the best remediation course. +# Remediation Options: +# 1. If $($Issue.IdentityReference) is a group, remove its Enroll/AutoEnroll rights and grant those rights +# to a smaller group or a single user/service account. + +# 2. Enable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} + +# 3. Enable the szOID_NTDS_CA_SECURITY_EXT security extension. +TODO +"@ + + $Issue.Revert = @" +# 1. Replace Enroll/AutoEnroll rights from the smaller group/single user/service account and grant those rights +# back to $($Issue.IdentityReference). + +# 2. Disable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} + +# 3. Disable the szOID_NTDS_CA_SECURITY_EXT security extension. +TODO +"@ + } + } + elseif ($Enroll -eq 'n') { + $Issue.Fix = @" +<# + 1. Open the Certification Templates Console: certtmpl.msc + 2. Double-click the $($Issue.Name) template to open its Properties page. + 3. Select the Security tab. + 4. Select the entry for $($Issue.IdentityReference). + 5. Uncheck the "Enroll" and/or "Autoenroll" boxes. + 6. Click OK. +#> +"@ + + $Issue.Revert = @" +<# + 1. Open the Certification Templates Console: certtmpl.msc + 2. Double-click the $($Issue.Name) template to open its Properties page. + 3. Select the Security tab. + 4. Select the entry for $($Issue.IdentityReference). + 5. Check the "Enroll" and/or "Autoenroll" boxes depending on your specific needs. + 6. Click OK. +#> +"@ + } # end if ($Enroll -eq 'y')/elseif ($Enroll -eq 'n') +} + <# Prerequisites: PowerShell version 2 or above. License: MIT @@ -4381,11 +4932,14 @@ function Invoke-Locksmith { 'ESC4', 'ESC5', 'ESC6', + 'ESC7', 'ESC8', + 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', + 'ESC16', 'All', 'PromptMe' )] @@ -4401,7 +4955,7 @@ function Invoke-Locksmith { [System.Management.Automation.PSCredential]$Credential ) - $Version = '2025.4.20' + $Version = '2025.5.26' $LogoPart1 = @' _ _____ _______ _ _ _______ _______ _____ _______ _ _ | | | | |____/ |______ | | | | | |_____| @@ -4572,10 +5126,13 @@ function Invoke-Locksmith { $ESC4 = $Results['ESC4'] $ESC5 = $Results['ESC5'] $ESC6 = $Results['ESC6'] + $ESC7 = $Results['ESC7'] $ESC8 = $Results['ESC8'] + $ESC9 = $Results['ESC9'] $ESC11 = $Results['ESC11'] $ESC13 = $Results['ESC13'] $ESC15 = $Results['ESC15'] + $ESC16 = $Results['ESC16'] # If these are all empty = no issues found, exit if ($null -eq $Results) { @@ -4594,10 +5151,13 @@ function Invoke-Locksmith { Format-Result -Issue $ESC4 -Mode 0 Format-Result -Issue $ESC5 -Mode 0 Format-Result -Issue $ESC6 -Mode 0 + Format-Result -Issue $ESC7 -Mode 0 Format-Result -Issue $ESC8 -Mode 0 + Format-Result -Issue $ESC9 -Mode 0 Format-Result -Issue $ESC11 -Mode 0 Format-Result -Issue $ESC13 -Mode 0 Format-Result -Issue $ESC15 -Mode 0 + Format-Result -Issue $ESC16 -Mode 0 Write-Host @" [!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues identified in the environment. For more details including: @@ -4623,10 +5183,13 @@ Invoke-Locksmith -Mode 1 Format-Result -Issue $ESC4 -Mode 1 Format-Result -Issue $ESC5 -Mode 1 Format-Result -Issue $ESC6 -Mode 1 + Format-Result -Issue $ESC7 -Mode 1 Format-Result -Issue $ESC8 -Mode 1 + Format-Result -Issue $ESC9 -Mode 1 Format-Result -Issue $ESC11 -Mode 1 Format-Result -Issue $ESC13 -Mode 1 Format-Result -Issue $ESC15 -Mode 1 + Format-Result -Issue $ESC16 -Mode 1 } 2 { $Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV" diff --git a/Locksmith.psd1 b/Locksmith.psd1 index d6c714d..b8ae79d 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.4.20' + ModuleVersion = '2025.5.26' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ diff --git a/Private/Find-ESC1.ps1 b/Private/Find-ESC1.ps1 index d54c1c9..abb6fd0 100644 --- a/Private/Find-ESC1.ps1 +++ b/Private/Find-ESC1.ps1 @@ -92,12 +92,14 @@ Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} Technique = 'ESC1' } - if ( $Mode -in @(1, 3, 4) ) { - Update-ESC1Remediation -Issue $Issue - } if ($SkipRisk -eq $false) { Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC1Remediation -Issue $Issue + } + $Issue } } diff --git a/Private/Find-ESC16.ps1 b/Private/Find-ESC16.ps1 new file mode 100644 index 0000000..0f02ecd --- /dev/null +++ b/Private/Find-ESC16.ps1 @@ -0,0 +1,75 @@ +function Find-ESC16 { + <# + .SYNOPSIS + This script finds Active Directory Certificate Services (AD CS) Certification Authorities (CA) that have the ESC16 vulnerability. + + .DESCRIPTION + The script takes an array of ADCS objects as input and filters them based on objects that have the objectClass + 'pKIEnrollmentService' and the szOID_NTDS_CA_SECURITY_EXT disabled. For each matching object, it creates a custom object with + properties representing various information about the object, such as Forest, Name, DistinguishedName, Technique, + Issue, Fix, and Revert. + + .PARAMETER ADCSObjects + Specifies the array of AD CS objects to be processed. This parameter is mandatory. + + .OUTPUTS + The script outputs an array of custom objects representing the matching ADCS objects and their associated information. + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [string]$UnsafeUsers, + [switch]$SkipRisk + ) + process { + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKIEnrollmentService') -and + ($_.DisableExtensionList -ne 'No') + } | ForEach-Object { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + Issue = $_.DisableExtensionList + Fix = 'N/A' + Revert = 'N/A' + Technique = '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 +user or computer account's SID for authentication. + +More info: + - https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc16-security-extension-disabled-on-ca-globally + +"@ + $Issue.Fix = @" +# Enable the flag +# TODO + +# Restart the Certificate Authority service +Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock { + Get-Service -Name certsvc | Restart-Service -Force +} +"@ + $Issue.Revert = @" +# Disable the flag +TODO + +# Restart the Certificate Authority service +Invoke-Command -ComputerName '$($_.dNSHostName)' -ScriptBlock { + Get-Service -Name certsvc | Restart-Service -Force +} +"@ + } + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + $Issue + } + } +} diff --git a/Private/Find-ESC3C1.ps1 b/Private/Find-ESC3C1.ps1 index 514fcc2..633120a 100644 --- a/Private/Find-ESC3C1.ps1 +++ b/Private/Find-ESC3C1.ps1 @@ -60,7 +60,7 @@ $($entry.IdentityReference) can use this template to request an Enrollment Agent certificate without Manager Approval. -The resulting certificate can be used to enroll in any template that requires +The resulting certificate can be used to enroll in any template that allows an Enrollment Agent to submit the request. More info: diff --git a/Private/Find-ESC4.ps1 b/Private/Find-ESC4.ps1 index 0aff874..0d13347 100644 --- a/Private/Find-ESC4.ps1 +++ b/Private/Find-ESC4.ps1 @@ -173,12 +173,14 @@ Set-Acl -Path 'AD:$($_.DistinguishedName)' -AclObject `$ACL Technique = 'ESC4' } - if ( $Mode -in @(1, 3, 4) ) { - Update-ESC4Remediation -Issue $Issue - } if ($SkipRisk -eq $false) { Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC4Remediation -Issue $Issue + } + $Issue } } diff --git a/Private/Find-ESC6.ps1 b/Private/Find-ESC6.ps1 index a0ed1b7..906a6e6 100644 --- a/Private/Find-ESC6.ps1 +++ b/Private/Find-ESC6.ps1 @@ -37,6 +37,7 @@ $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name + CAFullname = $CAFullName DistinguishedName = $_.DistinguishedName Issue = $_.SANFlag Fix = 'N/A' diff --git a/Private/Find-ESC7.ps1 b/Private/Find-ESC7.ps1 new file mode 100644 index 0000000..2976147 --- /dev/null +++ b/Private/Find-ESC7.ps1 @@ -0,0 +1,114 @@ +function Find-ESC7 { + <# + .SYNOPSIS + This script finds Active Directory Certificate Services (AD CS) Certificate Authorities (CA) that have the ESC7 vulnerability. + + .DESCRIPTION + The script takes an array of AD CS objects as input and filters them based on objects that have the objectClass + 'pKIEnrollmentService'. If the CA objects have non-standard/unsafe principals as administrators or managers, an issue is created. + + .PARAMETER ADCSObjects + Specifies the array of AD CS objects to be processed. This parameter is mandatory. + + .PARAMETER UnsafeUsers + Principals that should never be granted control of a CA. + + .PARAMETER SafeUsers + Principals that are generally recognized as safe to control a CA. + + .PARAMETER SkipRisk + Switch used when processing second-order risks. + + .OUTPUTS + The script outputs an array of custom objects representing the matching AD CS objects and their associated information. + + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [string]$UnsafeUsers, + [Parameter(Mandatory)] + [string]$SafeUsers, + [switch]$SkipRisk + ) + process { + Write-Output $ADCSObjects -PipelineVariable object | Where-Object { + ($object.objectClass -eq 'pKIEnrollmentService') -and $object.CAHostDistinguishedName -and + ( ($object.CAAdministrator) -or ($object.CertificateManager) ) + } | ForEach-Object { + Write-Output $object.CAAdministrator -PipelineVariable admin | ForEach-Object { + $SID = Convert-IdentityReferenceToSid -Object $admin + if ($SID -notmatch $SafeUsers) { + $Issue = [pscustomobject]@{ + Forest = $object.CanonicalName.split('/')[0] + Name = $object.Name + DistinguishedName = $object.DistinguishedName + IdentityReference = $admin + IdentityReferenceSID = $SID + Right = 'CA Administrator' + Issue = @" +$admin has been granted CA Administrator rights on this Certification Authority (CA). + +$admin has full control over this CA. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + Fix = "Revoke CA Administrator rights from ${admin}." + Revert = "Restore CA Administrator rights to ${admin}." + Technique = 'ESC7' + } + + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC7Remediation -Issue $Issue + } + + $Issue + } + } + + Write-Output $object.CertificateManager -PipelineVariable admin | ForEach-Object { + $SID = Convert-IdentityReferenceToSid -Object $admin + if ($SID -notmatch $SafeUsers) { + $Issue = [pscustomobject]@{ + Forest = $object.CanonicalName.split('/')[0] + Name = $object.Name + DistinguishedName = $object.DistinguishedName + IdentityReference = $admin + IdentityReferenceSID = $SID + Right = 'Certificate Manager' + Issue = @" +$admin has been granted Certificate Manager rights on this Certification Authority (CA). + +$admin can approve pending certificate requests on this CA. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + Fix = "Revoke Certificate Manager rights from ${admin}." + Revert = "Restore Certificate Manager rights to ${admin}." + Technique = 'ESC7' + } + + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC7Remediation -Issue $Issue + } + + $Issue + } + } + } + } +} diff --git a/Private/Find-ESC9.ps1 b/Private/Find-ESC9.ps1 index 354e85e..2d3c2b1 100644 --- a/Private/Find-ESC9.ps1 +++ b/Private/Find-ESC9.ps1 @@ -1,116 +1,111 @@ -<# - This is a working POC. I need to test both checks and possibly blend pieces of them. - Then I need to fold this function into the Locksmith workflow. -#> - -function Find-ESC9 { +function Find-ESC9 { <# .SYNOPSIS - Checks for ESC9 (No Security Extension) Vulnerability + This function finds Active Directory Certificate Services (AD CS) objects that have the ESC9 vulnerability. .DESCRIPTION - This function checks for certificate templates that contain the flag CT_FLAG_NO_SECURITY_EXTENSION (0x80000), - which will likely make them vulnerable to ESC9. Another factor to check for ESC9 is the registry values on AD - domain controllers that can help harden certificate based authentication for Kerberos and SChannel. - - .NOTES - An ESC9 condition exists when: - - - the new msPKI-Enrollment-Flag value on a certificate contains the flag CT_FLAG_NO_SECURITY_EXTENSION (0x80000) - - AND an insecure registry value is set on domain controllers: + The script takes an array of ADCS objects as input and filters them based on the specified conditions. + For each matching object, it creates a custom object with properties representing various information about + the object, such as Forest, Name, DistinguishedName, IdentityReference, ActiveDirectoryRights, Issue, Fix, Revert, and Technique. - - the StrongCertificateBindingEnforcement registry value for Kerberos is not set to 2 (the default is 1) on domain controllers - at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc - - OR the CertificateMappingMethods registry value for SCHANNEL contains the UPN flag on domain controllers at - HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel + .PARAMETER ADCSObjects + Specifies the array of ADCS objects to be processed. This parameter is mandatory. - When the CT_FLAG_NO_SECURITY_EXTENSION (0x80000) flag is set on a certificate template, the new szOID_NTDS_CA_SECURITY_EXT - security extension will not be embedded in issued certificates. This security extension was added by Microsoft's - patch KB5014754 ("Certificate-based authentication changes on Windows domain controllers") on May 10, 2022. + .PARAMETER SafeUsers + Specifies the list of SIDs of safe users who are allowed to have specific rights on the objects. This parameter is mandatory. - The patch applies to all servers that run Active Directory Certificate Services and Windows domain controllers that - service certificate-based authentication. - https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16 + .PARAMETER UnsafeUsers + Specifies the list of SIDs of safe users who should never have specific rights on the objects. This parameter is mandatory. - Based on research from - https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7, - https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16, - and on a very long conversation with Bing Chat. + .PARAMETER ClientAuthEKUs + A list of EKUs that can be used for client authentication. - Additional notes from Cortana -- Bing when I pressed her to tell me whether both conditions were required for ESC9 or only one of them: - A certificate template can still be vulnerable to ESC9 even if the msPKI-Enrollment-Flag does not include - CT_FLAG_NO_SECURITY_EXTENSION. This is because the vulnerability primarily arises from the ability of a - requester to specify the subjectAltName in a Certificate Signing Request (CSR). If a requester can specify - the subjectAltName in a CSR, they can request a certificate as anyone, including a domain admin user. - Therefore, if a certificate template allows requesters to specify a subjectAltName and - StrongCertificateBindingEnforcement is not set to 2, it could potentially be vulnerable to ESC9. However, - the presence of CT_FLAG_NO_SECURITY_EXTENSION in msPKI-Enrollment-Flag is a clear indicator of a template - being vulnerable to ESC9. -#> + .OUTPUTS + The script outputs an array of custom objects representing the matching ADCS objects and their associated information. + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $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' + $Results = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs + $Results + #> [CmdletBinding()] param( [Parameter(Mandatory)] [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, [Parameter(Mandatory)] + [string]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs, + [int]$Mode = 0, + [Parameter(Mandatory)] [string]$UnsafeUsers, [switch]$SkipRisk ) - - # Import the required module - Import-Module ActiveDirectory - - # Get the configuration naming context - $configNC = (Get-ADRootDSE).configurationNamingContext - - # Define the path to the Certificate Templates container - $path = "CN=Certificate Templates,CN=Public Key Services,CN=Services,$configNC" - - # Get all certificate templates - $templates = Get-ADObject -Filter * -SearchBase $path -Properties msPKI-Enrollment-Flag, msPKI-Certificate-Name-Flag - - foreach ($template in $templates) { - # Check if msPKI-Enrollment-Flag contains the CT_FLAG_NO_SECURITY_EXTENSION (0x80000) flag - if ($template.'msPKI-Enrollment-Flag' -band 0x80000) { - # Check if msPKI-Certificate-Name-Flag contains the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME (0x2) flag - if ($template.'msPKI-Certificate-Name-Flag' -band 0x2) { - # Output the template name - Write-Output "Template Name: $($template.Name), Vulnerable to ESC9" + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKICertificateTemplate') -and + ($_.pkiExtendedKeyUsage -match $ClientAuthEKUs) -and + ($_.'msPKI-Enrollment-Flag' -band 0x80000) + } | ForEach-Object { + foreach ($entry in $_.nTSecurityDescriptor.Access) { + $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) + if ($Principal -match '^(S-1|O:)') { + $SID = $Principal + } else { + $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value + } + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + IdentityReference = $entry.IdentityReference + IdentityReferenceSID = $SID + ActiveDirectoryRights = $entry.ActiveDirectoryRights + Enabled = $_.Enabled + EnabledOn = $_.EnabledOn + Issue = @" +This Client Authentication template has the szOID_NTDS_CA_SECURITY_EXT security +extension disabled. Certificates issued from this template will not enforce +strong certificate binding. Depending on the current Certificate Binding +Enforcement level ESC6 status, it may be possible to request and receive +certificates that rely on weak (aka attacker-controllable) binding methods. + +An attacker can abuse this weakness by: +1. Getting access to a user or computer account. +2. Modifying the user's userPrincipalName attribute (or the computer's dNSHostName attribute) to match a higher-privileged account. +3. Requesting a client authentication certificate from a template that w/ szOID_NTDS_CA_SECURITY_EXT disabled. +4. Using the client authentication certificiate to authenticate as the higher-privileged account. +5. Profiting. + +More info: + - ESC9 description: https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc9-no-security-extension-on-certificate-template + - Strong Mapping/Enforcement Mode: https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16 + +"@ + Fix = @" +# Enable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} +"@ + Revert = @" +# Disable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} +"@ + Technique = 'ESC9' + } + + if ( $Mode -in @(1, 3, 4) ) { + Update-ESC9Remediation -Issue $Issue + } + if ($SkipRisk -eq $false) { + Set-RiskRating -ADCSObjects $ADCSObjects -Issue $Issue -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers + } + $Issue } } } - - # AND / OR / ALSO - - Import-Module ActiveDirectory - - $templates = Get-ADObject -Filter { ObjectClass -eq 'pKICertificateTemplate' } -Properties * - foreach ($template in $templates) { - $name = $template.Name - - $subjectNameFlag = $template.'msPKI-Cert-Template-OID' - $subjectType = $template.'msPKI-Certificate-Application-Policy' - $enrollmentFlag = $template.'msPKI-Enrollment-Flag' - $certificateNameFlag = $template.'msPKI-Certificate-Name-Flag' - - # Check if the template is vulnerable to ESC9 - if ($subjectNameFlag -eq 'Supply in the request' -and - ($subjectType -eq 'User' -or $subjectType -eq 'Computer') -and - # 0x200 means a certificate needs to include a template name certificate extension - # 0x220 instructs the client to perform auto-enrollment for the specified template - ($enrollmentFlag -eq 0x200 -or $enrollmentFlag -eq 0x220) -and - # 0x2 instructs the client to supply subject information in the certificate request (CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT). - # This means that any user who is allowed to enroll in a certificate with this setting can request a certificate as any - # user in the network, including a privileged user. - # 0x3 instructs the client to supply both the subject and subject alternate name information in the certificate request - ($certificateNameFlag -eq 0x2 -or $certificateNameFlag -eq 0x3)) { - - # Print the template name and the vulnerability - Write-Output "$name is vulnerable to ESC9" - } else { - # Print the template name and the status - Write-Output "$name is not vulnerable to ESC9" - } - } - } diff --git a/Private/Format-Result.ps1 b/Private/Format-Result.ps1 index b4b6d8b..65e321d 100644 --- a/Private/Format-Result.ps1 +++ b/Private/Format-Result.ps1 @@ -21,7 +21,7 @@ function Format-Result { Formats the issue result in list format. .NOTES - Author: Spencer Alessi + Authors: Spencer Alessi & Jake Hildreth #> [CmdletBinding()] param( @@ -38,10 +38,13 @@ function Format-Result { ESC4 = 'ESC4 - Vulnerable Access Control - Certificate Template' ESC5 = 'ESC5 - Vulnerable Access Control - PKI Object' ESC6 = 'ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2 Flag Enabled' + ESC7 = 'ESC7 - Non-standard PKI Admins' ESC8 = 'ESC8 - HTTP/S Enrollment Enabled' + ESC9 = 'ESC9 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled on Template' ESC11 = 'ESC11 - IF_ENFORCEENCRYPTICERTREQUEST Flag Disabled' ESC13 = 'ESC13 - Vulnerable Certificate Template - Group-Linked' 'ESC15/EKUwu' = 'ESC15 - Vulnerable Certificate Template - Schema V1' + ESC16 = 'ESC16 - szOID_NTDS_CA_SECURITY_EXT Extension Disabled on CA' } $RiskTable = @{ @@ -65,12 +68,12 @@ function Format-Result { if ($Mode -eq 0) { # TODO Refactor this switch ($UniqueIssue) { - { $_ -in @('DETECT', 'ESC6', 'ESC8', 'ESC11') } { + { $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } { $Issue | Format-Table Technique, @{l = 'CA Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Issue -Wrap | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive } - { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC13', 'ESC15/EKUwu') } { + { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu') } { $Issue | Format-Table Technique, @{l = 'Template Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, Enabled, Issue -Wrap | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive @@ -83,12 +86,12 @@ function Format-Result { } } elseif ($Mode -eq 1) { switch ($UniqueIssue) { - { $_ -in @('DETECT', 'ESC6', 'ESC8', 'ESC11') } { + { $_ -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16') } { $Issue | 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" } } | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive } - { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC13', 'ESC15/EKUwu') } { + { $_ -in @('ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC9', 'ESC13', 'ESC15/EKUwu') } { $Issue | Format-List Technique, @{l = 'Template Name'; e = { $_.Name } }, @{l = 'Risk'; e = { $_.RiskName } }, DistinguishedName, Enabled, EnabledOn, Issue, Fix, @{l = 'Risk Score'; e = { $_.RiskValue } }, @{l = 'Risk Score Detail'; e = { $_.RiskScoring -join "`n" } } | Write-HostColorized -PatternColorMap $RiskTable -CaseSensitive diff --git a/Private/Get-CAHostObject.ps1 b/Private/Get-CAHostObject.ps1 index 43026d8..b435074 100644 --- a/Private/Get-CAHostObject.ps1 +++ b/Private/Get-CAHostObject.ps1 @@ -40,11 +40,11 @@ process { if ($Credential) { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { - Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential + if ($_.CAHostDistinguishedName) { Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC -Credential $Credential } else { Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" } } } else { $ADCSObjects | Where-Object objectClass -Match 'pKIEnrollmentService' | ForEach-Object { - Get-ADObject $_.CAHostDistinguishedName -Properties * -Server $ForestGC + if ($_.CAHostDistinguishedName) { Get-ADObject -Identity $_.CAHostDistinguishedName -Properties * -Server $ForestGC } else { Write-Warning "Get-CAHostObject: Unable to get information from $($_.DisplayName)" } } } } diff --git a/Private/Invoke-Scans.ps1 b/Private/Invoke-Scans.ps1 index 4dfcfd8..bd0aa26 100644 --- a/Private/Invoke-Scans.ps1 +++ b/Private/Invoke-Scans.ps1 @@ -5,12 +5,12 @@ function Invoke-Scans { .PARAMETER Scans Specifies the type of scans to perform. Multiple scan options can be provided as an array. The default value is 'All'. - The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', - 'ESC13', 'ESC15, 'EKUwu', 'All', 'PromptMe'. + The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC9', 'ESC11', + 'ESC13', 'ESC15, 'EKUwu', 'ESC16', 'All', 'PromptMe'. .NOTES - The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3C1, - Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13, Find-ESC15 + Find-ESC3C2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC9, Find-ESC11, Find-ESC13, Find-ESC15, Find-ESC16 - The script uses Out-GridView or Out-ConsoleGridView for interactive selection when the 'PromptMe' scan option is chosen. - The script returns a hash table containing the results of the scans. @@ -48,7 +48,7 @@ function Invoke-Scans { [string]$SafeUsers, [Parameter(Mandatory)] [string]$SafeOwners, - [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'All', 'PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC7', 'ESC8', 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', 'ESC16', 'All', 'PromptMe')] [array]$Scans = 'All', [Parameter(Mandatory)] [string]$UnsafeUsers, @@ -101,10 +101,18 @@ function Invoke-Scans { Write-Host 'Identifying Issuing CAs with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } + ESC7 { + Write-Host 'Identifying Issuing CAs with Non-Standard Admins (ESC7)...' + [array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers + } ESC8 { Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } + ESC9 { + Write-Host 'Identifying AD CS templates with szOID_NTDS_CA_SECURITY_EXT disabled (ESC9)...' + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode -UnsafeUsers $UnsafeUsers + } ESC11 { Write-Host 'Identifying Issuing CAs with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers @@ -121,6 +129,10 @@ function Invoke-Scans { Write-Host 'Identifying AD CS templates with dangerous ESC15/EKUwu configurations...' [array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers } + ESC16 { + Write-Host 'Identifying Issuing CAs with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...' + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + } All { Write-Host 'Identifying auditing issues...' [array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects @@ -137,23 +149,28 @@ function Invoke-Scans { [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -UnsafeUsers $UnsafeUsers Write-Host 'Identifying Certificate Authorities with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + Write-Host 'Identifying Certificate Authorities with Non-Standard Admins (ESC7)...' + [array]$ESC7 = Find-ESC7 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SafeUsers $SafeUsers Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + Write-Host 'Identifying AD CS templates with szOID_NTDS_CA_SECURITY_EXT disabled (ESC9)...' + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -Mode $Mode -UnsafeUsers $UnsafeUsers Write-Host 'Identifying Certificate Authorities with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC13 configurations...' [array]$ESC13 = Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -UnsafeUsers $UnsafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC15 configurations...' [array]$ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers - Write-Host + Write-Host 'Identifying Certificate Authorities with szOID_NTDS_CA_SECURITY_EXT disabled (ESC16)...' + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers } } - [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + $ESC11 + $ESC13 + $ESC15 + [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC7 + $ESC8 + $ESC9 + $ESC11 + $ESC13 + $ESC15 + $ESC16 # If these are all empty = no issues found, exit if ($AllIssues.Count -lt 1) { - Write-Host "`n$(Get-Date) : No ADCS issues were found." -ForegroundColor Green + Write-Host "`n$(Get-Date) : No ADCS issues were found. :)" -ForegroundColor Green break } @@ -167,9 +184,12 @@ function Invoke-Scans { ESC4 = $ESC4 ESC5 = $ESC5 ESC6 = $ESC6 + ESC7 = $ESC7 ESC8 = $ESC8 + ESC9 = $ESC9 ESC11 = $ESC11 ESC13 = $ESC13 ESC15 = $ESC15 + ESC16 = $ESC16 } } diff --git a/Private/New-Dictionary.ps1 b/Private/New-Dictionary.ps1 index 4a8bd9c..6c2ca39 100644 --- a/Private/New-Dictionary.ps1 +++ b/Private/New-Dictionary.ps1 @@ -94,7 +94,7 @@ function New-Dictionary { Category = 'Escalation Path' Subcategory = 'Vulnerable Certificate Authority Access Control' Summary = '' - FindIt = { Write-Output 'We have not created Find-ESC7 yet.' } + FindIt = { Find-ESC7 } FixIt = { Write-Output 'Add code to fix the vulnerable configuration.' } ReferenceUrls = 'https://posts.specterops.io/certified-pre-owned-d95910965cd2#:~:text=Vulnerable%20Certificate%20Authority%20Access%20Control%20%E2%80%94%20ESC7' }, diff --git a/Private/Set-AdditionalCAProperty.ps1 b/Private/Set-AdditionalCAProperty.ps1 index ca66874..16975cc 100644 --- a/Private/Set-AdditionalCAProperty.ps1 +++ b/Private/Set-AdditionalCAProperty.ps1 @@ -25,6 +25,8 @@ Date: July 15, 2022 #> + # TODO REfactor to move the creation of each property into its own function + [CmdletBinding(SupportsShouldProcess)] param ( [parameter( @@ -156,10 +158,32 @@ } catch { $InterfaceFlag = 'Failure' } + try { + if ($Credential) { + $CertutilSecurity = Invoke-Command -ComputerName $CAHostFQDN -Credential $Credential -ScriptBlock { certutil -config $using:CAFullName -getreg CA\Security } + } else { + $CertutilSecurity = certutil -config $CAFullName -getreg CA\Security + } + } catch { + $CAAdministrator = 'Failure' + $CertificateManager = 'Failure' + } + try { + if ($Credential) { + $CertutilDisableExtensionList = Invoke-Command -ComputerName $CAHostFQDN -Credential $Credential -ScriptBlock { certutil -config $using:CAFullName -getreg policy\DisableExtensionList } + } else { + $CertutilDisableExtensionList = certutil -config $CAFullName -getreg policy\DisableExtensionList + } + } catch { + $CertutilDisableExtensionList = 'Failure' + } } else { $AuditFilter = 'CA Unavailable' $SANFlag = 'CA Unavailable' $InterfaceFlag = 'CA Unavailable' + $CAAdministrator = 'CA Unavailable' + $CertificateManager = 'CA Unavailable' + $DisableExtensionList = 'CA Unavailable' } if ($CertutilAudit) { try { @@ -190,6 +214,26 @@ $InterfaceFlag = 'No' } } + if ($CertutilSecurity) { + [string[]]$CAAdministrator = $CertutilSecurity | ForEach-Object { + if ($_ -match '^.*Allow.*CA Administrator.*.*\t(.*)$') { + $matches[1].ToString() + } + } + [string[]]$CertificateManager = $CertutilSecurity | ForEach-Object { + if ($_ -match '^.*Allow.*Certificate Manager.*\t(.*)$') { + $matches[1].ToString() + } + } + } + if ($CertutilDisableExtensionList) { + [string]$DisableExtensionList = $CertutilDisableExtensionList | Select-String '1\.3\.6\.1\.4\.1\.311\.25\.2' + if ($DisableExtensionList) { + $DisableExtensionList = 'Yes' + } else { + $DisableExtensionList = 'No' + } + } Add-Member -InputObject $_ -MemberType NoteProperty -Name AuditFilter -Value $AuditFilter -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name CAEnrollmentEndpoint -Value $CAEnrollmentEndpoint -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name CAFullName -Value $CAFullName -Force @@ -197,6 +241,9 @@ Add-Member -InputObject $_ -MemberType NoteProperty -Name CAHostDistinguishedName -Value $CAHostDistinguishedName -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name SANFlag -Value $SANFlag -Force Add-Member -InputObject $_ -MemberType NoteProperty -Name InterfaceFlag -Value $InterfaceFlag -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name CAAdministrator -Value $CAAdministrator -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name CertificateManager -Value $CertificateManager -Force + Add-Member -InputObject $_ -MemberType NoteProperty -Name DisableExtensionList -Value $DisableExtensionList -Force } } } diff --git a/Private/Set-RiskRating.ps1 b/Private/Set-RiskRating.ps1 index d226dbc..c5e7d54 100644 --- a/Private/Set-RiskRating.ps1 +++ b/Private/Set-RiskRating.ps1 @@ -55,7 +55,7 @@ function Set-RiskRating { $RiskScoring = @() # CA issues don't rely on a principal and have a base risk of Medium. - if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC8', 'ESC11')) { + if ($Issue.Technique -in @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) { $RiskValue += 3 $RiskScoring += 'Base Score: 3' @@ -64,12 +64,47 @@ function Set-RiskRating { $RiskScoring += 'HTTP Enrollment: +2' } - # TODO Check NtAuthCertificates for CA thumbnail. If found, +2, else -1 + if ($Issue.Technique -eq 'ESC7') { + # If an Issue can be tied to a principal, the principal's objectClass impacts the Issue's risk + $SID = $Issue.IdentityReferenceSID.ToString() + $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass + + if ($Issue.IdentityReferenceSID -match $UnsafeUsers) { + # Authenticated Users, Domain Users, Domain Computers etc. are very risky + $RiskValue += 2 + $RiskScoring += 'Very Large Group: +2' + } elseif ($IdentityReferenceObjectClass -eq 'group') { + # Groups are riskier than individual principals + $RiskValue += 1 + $RiskScoring += 'Group: +1' + } elseif ($Issue.IdentityReferenceSID -notmatch $UnsafeUsers -and + $Issue.IdentityReferenceSID -notmatch $SafeUsers -and + $IdentityReferenceObjectClass -notlike '*ManagedServiceAccount') { + $RiskValue += 1 + $RiskScoring += 'Unprivileged Principal: +1' + } + } + + # Modifiers that rely on the existence of other ESCs + if ($Issue.Technique -eq 'ESC6') { + [array]$ESC9 = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus -UnsafeUsers $UnsafeUsers + [array]$ESC16 = Find-ESC16 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers + $ESC9and16 = $ESC9 + $ESC16 + if ($ESC9and16) { + $RiskValue += 2 + $RiskScoring += "Additional risky configurations exist which make this issue more severe: +2" + foreach ($otherIssue in $ESC9and16) { + $RiskScoring += " - $($otherIssue.Technique) exists on $($otherIssue.Name)" + } + } # end if ($ESC9and16) + } + + # TODO Check NtAuthCertificates for CA thumbprint. If found, +2, else -1 # TODO Check if NTLMv1 is allowed. } # Template and object issues rely on a principal and have complex scoring. - if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC8', 'ESC11')) { + if ($Issue.Technique -notin @('DETECT', 'ESC6', 'ESC7', 'ESC8', 'ESC11', 'ESC16')) { $RiskScoring += 'Base Score: 0' # Templates are more dangerous when enabled, but objects cannot be enabled/disabled. @@ -83,16 +118,17 @@ function Set-RiskRating { } } - # The principal's objectClass impacts the Issue's risk - $SID = $Issue.IdentityReferenceSID.ToString() - $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass - # ESC1 and ESC4 templates are more dangerous than other templates because they can result in immediate compromise. if ($Issue.Technique -in @('ESC1', 'ESC4')) { $RiskValue += 1 - $RiskScoring += 'ESC1/4: +1' + $RiskScoring += "$($Issue.Technique) +1" } + # If an Issue can be tied to a principal, the principal's objectClass impacts the Issue's risk + $SID = $Issue.IdentityReferenceSID.ToString() + $IdentityReferenceObjectClass = Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass + + if ($Issue.IdentityReferenceSID -match $UnsafeUsers) { # Authenticated Users, Domain Users, Domain Computers etc. are very risky $RiskValue += 2 @@ -101,10 +137,15 @@ function Set-RiskRating { # Groups are riskier than individual principals $RiskValue += 1 $RiskScoring += 'Group: +1' + } elseif ($Issue.IdentityReferenceSID -notmatch $UnsafeUsers -and + $Issue.IdentityReferenceSID -notmatch $SafeUsers -and + $IdentityReferenceObjectClass -notlike '*ManagedServiceAccount') { + $RiskValue += 1 + $RiskScoring += 'Unprivileged Principal: +1' } - # Safe users and managed service accounts are inherently safer than other principals - except in ESC3 Condition 2! - if ($Issue.Technique -eq 'ESC3' -and $Issue.Condition -eq 2) { + # Safe users and managed service accounts are inherently safer than other principals - except in ESC3 Condition 2 and ESC9! + if (($Issue.Technique -eq 'ESC9') -or ($Issue.Technique -eq 'ESC3' -and $Issue.Condition -eq 2)) { if ($Issue.IdentityReferenceSID -match $SafeUsers) { # Safe Users are admins. Authenticating as an admin is bad. $RiskValue += 2 @@ -130,7 +171,7 @@ function Set-RiskRating { foreach ($name in $ESC3C2Names) { $OtherTemplateRisk = 0 $Principals = @() - foreach ($esc in $($ESC3C2 | Where-Object Name -eq $name) ) { + foreach ($esc in $($ESC3C2 | Where-Object Name -EQ $name) ) { if ($CheckedESC3C2Templates.GetEnumerator().Name -contains $esc.Name) { $Principals = $CheckedESC3C2Templates.$($esc.Name) } else { @@ -170,13 +211,13 @@ function Set-RiskRating { # Default 'User' and 'Machine' templates are more dangerous $ESC15 = Find-ESC15 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers -SkipRisk | Where-Object { $_.Enabled -eq $true } - $ESC15Names = @(($ESC15 | Where-Object Name -in @('Machine', 'User')).Name) + $ESC15Names = @(($ESC15 | Where-Object Name -In @('Machine', 'User')).Name) if ($ESC15Names) { $CheckedESC15Templates = @{} foreach ($name in $ESC15Names) { $OtherTemplateRisk = 0 $Principals = @() - foreach ($esc in $($ESC15 | Where-Object Name -eq $name) ) { + foreach ($esc in $($ESC15 | Where-Object Name -EQ $name) ) { if ($CheckedESC15Templates.GetEnumerator().Name -contains $esc.Name) { $Principals = $CheckedESC15Templates.$($esc.Name) } else { @@ -228,7 +269,7 @@ function Set-RiskRating { foreach ($name in $ESC2Names) { $OtherTemplateRisk = 0 $Principals = @() - foreach ($esc in $($ESC2 | Where-Object Name -eq $name) ) { + foreach ($esc in $($ESC2 | Where-Object Name -EQ $name) ) { if ($CheckedESC2Templates.GetEnumerator().Name -contains $esc.Name) { $Principals = $CheckedESC2Templates.$($esc.Name) } else { @@ -264,7 +305,7 @@ function Set-RiskRating { foreach ($name in $ESC3C1Names) { $OtherTemplateRisk = 0 $Principals = @() - foreach ($esc in $($ESC3C1 | Where-Object Name -eq $name) ) { + foreach ($esc in $($ESC3C1 | Where-Object Name -EQ $name) ) { if ($CheckedESC3C1Templates.GetEnumerator().Name -contains $esc.Name) { $Principals = $CheckedESC3C1Templates.$($esc.Name) } else { @@ -294,8 +335,8 @@ function Set-RiskRating { $RiskValue += $OtherTemplateRisk } - # Disabled ESC1, ESC2, ESC3, ESC4, and ESC15 templates are more dangerous if there's an ESC5 on one or more CA objects - if ($Issue.Technique -match 'ESC1|ESC2|ESC3|ESC4' -and $Issue.Enabled -eq $false ) { + # Disabled ESC1, ESC2, ESC3, ESC4, ESC9, and ESC15 templates are more dangerous if there's an ESC5 on one or more CA objects + if ($Issue.Technique -match 'ESC1|ESC2|ESC3|ESC4|ESC9' -and $Issue.Enabled -eq $false ) { $ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -UnsafeUsers $UnsafeUsers -DangerousRights $DangerousRights -SafeOwners '-519$' -SafeObjectTypes $SafeObjectTypes -SkipRisk | Where-Object { $_.objectClass -eq 'pKIEnrollmentService' } $ESC5Names = @(($ESC5 | Select-Object -Property Name -Unique).Name) @@ -356,6 +397,18 @@ function Set-RiskRating { } } + # ESC9/ESC16 are much more dangerous when ESC6 exists + if ($Issue.Technique -match 'ESC9|ESC16') { + $ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects -UnsafeUsers $UnsafeUsers -SkipRisk + if ($ESC6) { + $RiskValue += 2 + $RiskScoring += "One or more CAs have an ESC6: +2" + foreach ($ca in $ESC6.CAFullName) { + $RiskScoring += " - ESC6 exists on $ca" + } + } # end if ($ESC6) + } + # Convert Value to Name $RiskName = switch ($RiskValue) { { $_ -le 1 } { 'Informational' } diff --git a/Private/Update-ESC1Remediation.ps1 b/Private/Update-ESC1Remediation.ps1 index ca90228..1c11002 100644 --- a/Private/Update-ESC1Remediation.ps1 +++ b/Private/Update-ESC1Remediation.ps1 @@ -44,13 +44,13 @@ function Update-ESC1Remediation { $Enroll = '' do { - $Enroll = Read-Host "`nDoes $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" + $Enroll = Read-Host "`n[?] Does $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" } while ( ($Enroll -ne 'y') -and ($Enroll -ne 'n') -and ($Enroll -ne 'unsure')) if ($Enroll -eq 'y') { $Frequent = '' do { - $Frequent = Read-Host "`nIs the $($Issue.Name) certificate frequently requested? [y/n/unsure]" + $Frequent = Read-Host "`n[?] Is the $($Issue.Name) certificate frequently requested? [y/n/unsure]" } while ( ($Frequent -ne 'y') -and ($Frequent -ne 'n') -and ($Frequent -ne 'unsure')) if ($Frequent -ne 'n') { diff --git a/Private/Update-ESC4Remediation.ps1 b/Private/Update-ESC4Remediation.ps1 index d4262e1..0759dc5 100644 --- a/Private/Update-ESC4Remediation.ps1 +++ b/Private/Update-ESC4Remediation.ps1 @@ -6,8 +6,10 @@ function Update-ESC4Remediation { .DESCRIPTION This function takes a single ESC4 issue as input. It then prompts the user if the principal with the ESC4 rights administers the template in question. - If the principal is an admin of the template, the Issue attribute is updated to indicate this configuration is - expected, and the Fix attribute for the issue is updated to indicate no remediation is needed. + If the principal is an admin of the template: + - the Issue attribute is updated to indicate this configuration is expected + - the Fix attribute for the issue is updated to indicate no remediation is needed + - the Risk Value, Risk Name, and Risk Scoring Details are updated to indicate no risk If the the principal is not an admin of the template AND the rights assigned is GenericAll, Locksmith will ask if Enroll or AutoEnroll rights are needed. Depending on the answers to the listed questions, the Fix attribute is updated accordingly. @@ -42,12 +44,20 @@ function Update-ESC4Remediation { $Admin = '' do { - $Admin = Read-Host "`nDoes $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) template? [y/n]" + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) template? [y/n]" } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) if ($Admin -eq 'y') { - $Issue.Issue = "$($Issue.IdentityReference) has $($Issue.ActiveDirectoryRights) rights on this template, but this is expected." + $Issue.Issue = @" +$($Issue.IdentityReference) has $($Issue.ActiveDirectoryRights) rights on this template, but this is expected. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 +"@ $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) administers this template" -Force } elseif ($Issue.Issue -match 'GenericAll') { $RightsToRestore = 0 while ($RightsToRestore -notin 1..5) { diff --git a/Private/Update-ESC7Remediation.ps1 b/Private/Update-ESC7Remediation.ps1 new file mode 100644 index 0000000..550445f --- /dev/null +++ b/Private/Update-ESC7Remediation.ps1 @@ -0,0 +1,95 @@ +function Update-ESC7Remediation { + <# + .SYNOPSIS + This function asks the user a set of questions to provide the most appropriate remediation for ESC7 issues. + + .DESCRIPTION + This function takes a single ESC7 issue as input. It then prompts the user if the principal with the ESC7 rights + administers the Certification Authority (CA) in question. + If the principal is an admin of the CA: + - the Issue attribute is updated to indicate this configuration is expected + - the Fix attribute for the issue is updated to indicate no remediation is needed + - the Risk Value and Risk Scoring are set to + If the the principal is not an admin of the CA, + Depending on the answers to the listed questions, the Fix attribute is updated accordingly. + + .PARAMETER Issue + A pscustomobject that includes all pertinent information about the ESC7 issue. + + .OUTPUTS + This function updates ESC7 remediations customized to the user's needs. + + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $DangerousRights = @('GenericAll', 'WriteProperty', 'WriteOwner', 'WriteDacl') + $SafeOwners = '-512$|-519$|-544$|-18$|-517$|-500$' + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $SafeObjectTypes = '0e10c968-78fb-11d2-90d4-00c04f79dc55|a05b8cc2-17bc-4802-a710-e7c15ab866a2' + $ESC7Issues = Find-ESC7 -ADCSObjects $ADCSObjects -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeUsers $SafeUsers -SafeObjectTypes $SafeObjectTypes -Mode 1 + foreach ($issue in $ESC7Issues) { Update-ESC7Remediation -Issue $Issue } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Issue + ) + + if ($Issue.Right -eq 'CA Administrator') { + $Header = "`n[!] ESC7 Issue detected on $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host "$($Issue.IdentityReference) has CA Administrator rights on this Certification Authority (CA).`n" + Write-Host 'To provide the most appropriate remediation for this issue, Locksmith will now ask you a few questions.' + + $Admin = '' + do { + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) administer and/or maintain the $($Issue.Name) CA? [y/n]" + } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) + + if ($Admin -eq 'y') { + $Issue.Issue = @" +$($Issue.IdentityReference) has CA Administrator rights on this CA, but this is expected. + +Note: +These rights grant $($Issue.IdentityReference) control of the forest. +This principal should be considered a Tier 0/control plane object and protected as such. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) administers this CA" -Force + } + } + + if ($Issue.Right -eq 'Certificate Manager') { + $Header = "`n[!] ESC7 Issue detected on $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host "$($Issue.IdentityReference) has Certificate Manager rights on this Certification Authority (CA).`n" + Write-Host 'To provide the most appropriate remediation for this issue, Locksmith will now ask you a few questions.' + + $Admin = '' + do { + $Admin = Read-Host "`n[?] Does $($Issue.IdentityReference) need to approve pending certificate requests on the $($Issue.Name) CA? [y/n]" + } while ( ($Admin -ne 'y') -and ($Admin -ne 'n') ) + + if ($Admin -eq 'y') { + $Issue.Issue = @" +$($Issue.IdentityReference) has Certificate Manager rights on this CA, but this is expected. + +More info: + - https://posts.specterops.io/certified-pre-owned-d95910965cd2 + +"@ + $Issue.Fix = "No immediate remediation required." + $Issue | Add-Member -NotePropertyName RiskValue -NotePropertyValue 0 -Force + $Issue | Add-Member -NotePropertyName RiskName -NotePropertyValue 'Informational' -Force + $Issue | Add-Member -NotePropertyName RiskScoring -NotePropertyValue "$($Issue.IdentityReference) approves pending certificate requests on this CA" -Force + } + } +} diff --git a/Private/Update-ESC9Remediation.ps1 b/Private/Update-ESC9Remediation.ps1 new file mode 100644 index 0000000..6cd16c7 --- /dev/null +++ b/Private/Update-ESC9Remediation.ps1 @@ -0,0 +1,110 @@ +function Update-ESC9Remediation { + <# + .SYNOPSIS + This function asks the user a set of questions to provide the most appropriate remediation for ESC9 issues. + + .DESCRIPTION + This function takes a single ESC9 issue as input then asks a series of questions to determine the correct + remediation. + + Questions: + 1. Does the identified principal need to enroll in this template? [Yes/No/Unsure] + 2. Is this certificate widely used and/or frequently requested? [Yes/No/Unsure] + + Depending on answers to these questions, the Issue and Fix attributes on the Issue object are updated. + + TODO: More questions: + Should the identified principal be able to request certs that include a SAN or SANs? + + .PARAMETER Issue + A pscustomobject that includes all pertinent information about the ESC1 issue. + + .OUTPUTS + This function updates ESC9 remediations customized to the user's needs. + + .EXAMPLE + $Targets = Get-Target + $ADCSObjects = Get-ADCSObject -Targets $Targets + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-521$|-498$|-9$|-526$|-527$|S-1-5-10' + $ESC9Issues = Find-ESC9 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers + foreach ($issue in $ESC9Issues) { Update-ESC9Remediation -Issue $Issue } + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [object]$Issue + ) + + $Header = "`n[!] ESC9 Issue detected in $($Issue.Name)" + Write-Host $Header -ForegroundColor Yellow + Write-Host $('-' * $Header.Length) -ForegroundColor Yellow + Write-Host @" +The $($Issue.Name) template has the szOID_NTDS_CA_SECURITY_EXT security extension +disabled. Certificates issued from this template will not enforce strong +certificate binding. Manager approval is not required for a certificate to be issued. +To provide the most appropriate remediation for this issue, Locksmith will now +ask you a few questions. +"@ + + $Enroll = '' + do { + $Enroll = Read-Host "`n[?] Does $($Issue.IdentityReference) need to Enroll in the $($Issue.Name) template? [y/n/unsure]" + } while ( ($Enroll -ne 'y') -and ($Enroll -ne 'n') -and ($Enroll -ne 'unsure')) + + if ($Enroll -eq 'y') { + $Frequent = '' + do { + $Frequent = Read-Host "`n[?] Is the $($Issue.Name) certificate frequently requested? [y/n/unsure]" + } while ( ($Frequent -ne 'y') -and ($Frequent -ne 'n') -and ($Frequent -ne 'unsure')) + + if ($Frequent -ne 'n') { + $Issue.Fix = @" +# Locksmith cannot currently determine the best remediation course. +# Remediation Options: +# 1. If $($Issue.IdentityReference) is a group, remove its Enroll/AutoEnroll rights and grant those rights +# to a smaller group or a single user/service account. + +# 2. Enable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} + +# 3. Enable the szOID_NTDS_CA_SECURITY_EXT security extension. +TODO +"@ + + $Issue.Revert = @" +# 1. Replace Enroll/AutoEnroll rights from the smaller group/single user/service account and grant those rights +# back to $($Issue.IdentityReference). + +# 2. Disable Manager Approval +`$Object = '$($_.DistinguishedName)' +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} + +# 3. Disable the szOID_NTDS_CA_SECURITY_EXT security extension. +TODO +"@ + } + } elseif ($Enroll -eq 'n') { + $Issue.Fix = @" +<# + 1. Open the Certification Templates Console: certtmpl.msc + 2. Double-click the $($Issue.Name) template to open its Properties page. + 3. Select the Security tab. + 4. Select the entry for $($Issue.IdentityReference). + 5. Uncheck the "Enroll" and/or "Autoenroll" boxes. + 6. Click OK. +#> +"@ + + $Issue.Revert = @" +<# + 1. Open the Certification Templates Console: certtmpl.msc + 2. Double-click the $($Issue.Name) template to open its Properties page. + 3. Select the Security tab. + 4. Select the entry for $($Issue.IdentityReference). + 5. Check the "Enroll" and/or "Autoenroll" boxes depending on your specific needs. + 6. Click OK. +#> +"@ + } # end if ($Enroll -eq 'y')/elseif ($Enroll -eq 'n') +} diff --git a/Public/Invoke-Locksmith.ps1 b/Public/Invoke-Locksmith.ps1 index 4dd14fb..92cc1de 100644 --- a/Public/Invoke-Locksmith.ps1 +++ b/Public/Invoke-Locksmith.ps1 @@ -93,11 +93,14 @@ function Invoke-Locksmith { 'ESC4', 'ESC5', 'ESC6', + 'ESC7', 'ESC8', + 'ESC9', 'ESC11', 'ESC13', 'ESC15', 'EKUwu', + 'ESC16', 'All', 'PromptMe' )] @@ -281,10 +284,13 @@ function Invoke-Locksmith { $ESC4 = $Results['ESC4'] $ESC5 = $Results['ESC5'] $ESC6 = $Results['ESC6'] + $ESC7 = $Results['ESC7'] $ESC8 = $Results['ESC8'] + $ESC9 = $Results['ESC9'] $ESC11 = $Results['ESC11'] $ESC13 = $Results['ESC13'] $ESC15 = $Results['ESC15'] + $ESC16 = $Results['ESC16'] # If these are all empty = no issues found, exit if ($null -eq $Results) { @@ -303,10 +309,13 @@ function Invoke-Locksmith { Format-Result -Issue $ESC4 -Mode 0 Format-Result -Issue $ESC5 -Mode 0 Format-Result -Issue $ESC6 -Mode 0 + Format-Result -Issue $ESC7 -Mode 0 Format-Result -Issue $ESC8 -Mode 0 + Format-Result -Issue $ESC9 -Mode 0 Format-Result -Issue $ESC11 -Mode 0 Format-Result -Issue $ESC13 -Mode 0 Format-Result -Issue $ESC15 -Mode 0 + Format-Result -Issue $ESC16 -Mode 0 Write-Host @" [!] You ran Locksmith in Mode 0 which only provides an high-level overview of issues identified in the environment. For more details including: @@ -332,10 +341,13 @@ Invoke-Locksmith -Mode 1 Format-Result -Issue $ESC4 -Mode 1 Format-Result -Issue $ESC5 -Mode 1 Format-Result -Issue $ESC6 -Mode 1 + Format-Result -Issue $ESC7 -Mode 1 Format-Result -Issue $ESC8 -Mode 1 + Format-Result -Issue $ESC9 -Mode 1 Format-Result -Issue $ESC11 -Mode 1 Format-Result -Issue $ESC13 -Mode 1 Format-Result -Issue $ESC15 -Mode 1 + Format-Result -Issue $ESC16 -Mode 1 } 2 { $Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV" diff --git a/README.md b/README.md index f9af5ae..2a53ab7 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Example Output for Mode 4: ### Scans: Select Which Scans to Invoke -Use the `-Scans` parameter to choose which vulnerabilities to scan for. Acceptable values include `All`, `Auditing`, `ESC1`, `ESC2`, `ESC3`, `ESC4`, `ESC5`, `ESC6`, `ESC8`, `ESC11`, `ESC13`, `ESC15`, `EKEUwu`, or `PromptMe`. The `PromptMe` option presents an interactive list allowing you to select one or more scans. +Use the `-Scans` parameter to choose which vulnerabilities to scan for. Acceptable values include `All`, `Auditing`, `ESC1`, `ESC2`, `ESC3`, `ESC4`, `ESC5`, `ESC6`, `ESC7`, `ESC8`, `ESC11`, `ESC13`, `ESC15`, `EKEUwu`, `ESC16` or `PromptMe`. The `PromptMe` option presents an interactive list allowing you to select one or more scans. ``` powershell # Run all scans