diff --git a/Build/Build-Module.ps1 b/Build/Build-Module.ps1 index 410bcfd..42ebc8d 100644 --- a/Build/Build-Module.ps1 +++ b/Build/Build-Module.ps1 @@ -1,5 +1,5 @@ param ( - # A CalVer string if you need to manually override the default yyyy.M version string. + # A CalVer string if you need to manually override the default yyyy.M.d version string. [string]$CalVer ) @@ -22,7 +22,7 @@ Import-Module -Name PSPublishModule -Force Build-Module -ModuleName 'Locksmith' { # Usual defaults as per standard module $Manifest = [ordered] @{ - ModuleVersion = if ($Calver) {$CalVer} else {(Get-Date -Format yyyy.M)} + ModuleVersion = if ($Calver) {$CalVer} else {(Get-Date -Format yyyy.M.d)} CompatiblePSEditions = @('Desktop', 'Core') GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a' Author = 'Jake Hildreth' diff --git a/Invoke-Locksmith.ps1 b/Invoke-Locksmith.ps1 index f293862..4a8a132 100644 --- a/Invoke-Locksmith.ps1 +++ b/Invoke-Locksmith.ps1 @@ -28,7 +28,7 @@ function ConvertFrom-IdentityReference { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$Object ) @@ -74,8 +74,25 @@ function Export-RevertScript { .PARAMETER ESC6 An array of ESC6 changes to be reverted. + .PARAMETER ESC11 + An array of ESC11 changes to be reverted. + + .PARAMETER ESC13 + An array of ESC13 changes to be reverted. + .EXAMPLE - Export-RevertScript -AuditingIssues $auditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Export-RevertScript @params Reverts the changes performed by Locksmith using the specified arrays of objects. #> @@ -87,19 +104,22 @@ function Export-RevertScript { [array]$ESC3, [array]$ESC4, [array]$ESC5, - [array]$ESC6 + [array]$ESC6, + [array]$ESC11, + [array]$ESC13 ) begin { $Output = 'Invoke-RevertLocksmith.ps1' - Set-Content -Path $Output -Value "<#`nScript to revert changes performed by Locksmith`nCreated $(Get-Date)`n#>" -Force - $Objects = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $RevertScript = [System.Text.StringBuilder]::New() + [void]$RevertScript.Append("<#`nScript to revert changes performed by Locksmith`nCreated $(Get-Date)`n#>`n") + $Objects = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC11 + $ESC13 } process { if ($Objects) { $Objects | ForEach-Object { - Add-Content -Path $Output -Value $_.Revert - Start-Sleep -Seconds 5 + [void]$RevertScript.Append("$($_.Revert)`n") } + $RevertScript.ToString() | Out-File -FilePath $Output } } } @@ -138,7 +158,7 @@ function Find-AuditingIssue { [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects ) @@ -151,7 +171,7 @@ function Find-AuditingIssue { Name = $_.Name DistinguishedName = $_.DistinguishedName Technique = 'DETECT' - Issue = "Auditing is not fully enabled on $($_.CAFullName). Current value is $($_.AuditFilter)" + Issue = "Auditing is not fully enabled on $($_.CAFullName). Important security events may go unnoticed." Fix = @" certutil.exe -config `'$($_.CAFullname)`' -setreg `'CA\AuditFilter`' 127 Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { @@ -190,21 +210,27 @@ function Find-ESC1 { .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 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 $ADCSObjects = Get-ADCSObjects $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-9$|-526$|-527$|S-1-5-10' - $Results = $ADCSObjects | Find-ESC1 -SafeUsers $SafeUsers + $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 = $ADCSObjects | Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs $Results #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] - [array]$SafeUsers + [Parameter(Mandatory)] + [array]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs ) $ADCSObjects | Where-Object { ($_.objectClass -eq 'pKICertificateTemplate') -and @@ -213,6 +239,7 @@ function Find-ESC1 { !($_.'msPKI-Enrollment-Flag' -band 2) -and ( ($_.'msPKI-RA-Signature' -eq 0) -or ($null -eq $_.'msPKI-RA-Signature') ) } | ForEach-Object { + # Write-Host $_; continue foreach ($entry in $_.nTSecurityDescriptor.Access) { $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) if ($Principal -match '^(S-1|O:)') { @@ -221,21 +248,32 @@ function Find-ESC1 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Client Authentication template using a SAN without Manager Approval" + Issue = @" +$($entry.IdentityReference) can provide a Subject Alternative Name (SAN) while +enrolling in this Client Authentication template, and enrollment does not require +Manager Approval. + +The resultant certificate can be used by an attacker to authenticate as any +principal listed in the SAN up to and including Domain Admins, Enterprise Admins, +or Domain Controllers. + +"@ Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC1' } @@ -245,6 +283,176 @@ Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = } } +function Find-ESC11 { + <# + .SYNOPSIS + This script finds AD CS (Active Directory Certificate Services) objects that have the ESC11 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 InterfaceFlag set to 'No'. 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 ADCS 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. + + .EXAMPLE + $ADCSObjects = Get-ADCSObject -Target (Get-Target) + Find-ESC11 -ADCSObjects $ADCSObjects + $Results + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + $ADCSObjects + ) + process { + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKIEnrollmentService') -and + ($_.InterfaceFlag -ne 'Yes') + } | ForEach-Object { + [string]$CAFullName = "$($_.dNSHostName)\$($_.Name)" + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + Technique = 'ESC11' + Issue = $_.InterfaceFlag + Fix = 'N/A' + Revert = 'N/A' + } + if ($_.InterfaceFlag -eq 'No') { + $Issue.Issue = @" +The IF_ENFORCEENCRYPTICERTREQUEST flag is disabled on this Certification +Authority (CA). It is possible to relay NTLM authentication to the RPC interface +of this CA. + +If the LAN Manager authentication level of any domain in this forest is 2 or +less, an attacker can coerce authentication from a Domain Controller (DC) to +receive a certificate which can be used to authenticate as that DC. + +"@ + $Issue.Fix = @" +# Enable the flag +certutil -config $CAFullname -setreg CA\InterfaceFlags +IF_ENFORCEENCRYPTICERTREQUEST + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { + Get-Service -Name `'certsvc`' | Restart-Service -Force +} +"@ + $Issue.Revert = @" +# Disable the flag +certutil -config $CAFullname -setreg CA\InterfaceFlags -IF_ENFORCEENCRYPTICERTREQUEST + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { + Get-Service -Name `'certsvc`' | Restart-Service -Force +} +"@ + } + $Issue + } + } +} + +function Find-ESC13 { + <# + .SYNOPSIS + This script finds AD CS (Active Directory Certificate Services) objects that have the ESC13 vulnerability. + + .DESCRIPTION + 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. + + .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 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 + $ADCSObjects = Get-ADCSObjects + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-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 = $ADCSObjects | Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs + $Results + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [array]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs + ) + + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKICertificateTemplate') -and + ($_.pkiExtendedKeyUsage -match $ClientAuthEKUs) -and + ($_.'msPKI-Certificate-Policy') + } | ForEach-Object { + foreach ($policy in $_.'msPKI-Certificate-Policy') { + if ($ADCSObjects.'msPKI-Cert-Template-OID' -contains $policy) { + $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -EQ $policy + if ($OidToCheck.'msDS-OIDToGroupLink') { + 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') ) { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + IdentityReference = $entry.IdentityReference + ActiveDirectoryRights = $entry.ActiveDirectoryRights + LinkedGroup = $OidToCheck.'msDS-OIDToGroupLink' + Issue = @" +$($entry.IdentityReference) can enroll in this Client Authentication template +which is linked to the group $($OidToCheck.'msDS-OIDToGroupLink'). + +If $($entry.IdentityReference) uses this certificate for authentication, they +will gain the rights of the linked group while the group membership appears empty. + +"@ + 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 = 'ESC13' + } + $Issue + } + } + } + } + } + } +} + function Find-ESC2 { <# .SYNOPSIS @@ -272,15 +480,14 @@ function Find-ESC2 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [string]$SafeUsers ) $ADCSObjects | Where-Object { ($_.ObjectClass -eq 'pKICertificateTemplate') -and ( (!$_.pkiExtendedKeyUsage) -or ($_.pkiExtendedKeyUsage -match '2.5.29.37.0') ) -and - ($_.'msPKI-Certificate-Name-Flag' -band 1) -and !($_.'msPKI-Enrollment-Flag' -band 2) -and ( ($_.'msPKI-RA-Signature' -eq 0) -or ($null -eq $_.'msPKI-RA-Signature') ) } | ForEach-Object { @@ -292,21 +499,37 @@ function Find-ESC2 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can request a SubCA certificate without Manager Approval" + Issue = @" +$($entry.IdentityReference) can use this template to request a Subordinate +Certification Authority (SubCA) certificate without Manager Approval. + +The resultant certificate can be used by an attacker to instantiate their own +SubCA which is trusted by AD. + +By default, certificates created from this attacker-controlled SubCA cannot be +used for authentication, but they can be used for other purposes such as TLS +certs and code signing. + +However, if an attacker can modify the NtAuthCertificates object (see ESC5), +they can convert their rogue CA into one trusted for authentication. + +"@ Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC2' } @@ -343,9 +566,9 @@ function Find-ESC3Condition1 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$SafeUsers ) $ADCSObjects | Where-Object { @@ -362,21 +585,30 @@ function Find-ESC3Condition1 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Enrollment Agent template without Manager Approval" + Issue = @" +$($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 +an Enrollment Agent to submit the request. + +"@ Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC3' } @@ -413,9 +645,9 @@ function Find-ESC3Condition2 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$SafeUsers ) $ADCSObjects | Where-Object { @@ -433,21 +665,30 @@ function Find-ESC3Condition2 { else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Client Authentication template using a SAN without Manager Approval" + Issue = @" +If the holder of an Enrollment Agent certificate requests a certificate using +this template, they will receive a certificate which allows them to authenticate +as $($entry.IdentityReference). + +"@ Fix = @" +First, eliminate unused Enrollment Agent templates. +Then, tightly scope any Enrollment Agent templates that remain and: +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC3' } @@ -521,15 +762,15 @@ function Find-ESC4 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $DangerousRights, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeOwners, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeUsers, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeObjectTypes, [int]$Mode ) @@ -549,7 +790,11 @@ function Find-ESC4 { Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName - Issue = "$($_.nTSecurityDescriptor.Owner) has Owner rights on this template" + Issue = @" +$($_.nTSecurityDescriptor.Owner) has Owner rights on this template and can +modify it into a template that can create ESC1, ESC2, and ESC3 templates. + +"@ Fix = @" `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$PreferredOwner`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' @@ -587,7 +832,9 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) rights on this template" + Issue = "$($entry.IdentityReference) has been granted " + + "$($entry.ActiveDirectoryRights) rights on this template.`n" + + "$($entry.IdentityReference) can likely modify this template into an ESC1 template." Fix = @" `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' foreach ( `$ace in `$ACL.access ) { @@ -674,15 +921,15 @@ function Find-ESC5 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $DangerousRights, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeOwners, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeUsers, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeObjectTypes ) $ADCSObjects | ForEach-Object { @@ -697,11 +944,62 @@ function Find-ESC5 { } if ( ($_.objectClass -ne 'pKICertificateTemplate') -and ($SID -notmatch $SafeOwners) ) { + switch ($_.objectClass) { + computer { + $IssueDetail = @" +This computer is hosting a Certification Authority (CA). $($_.nTSecurityDescriptor.Owner) +has full control of this object. + +There is little reason for anyone other than AD Admins to have elevated rights +to this CA host. +"@ + } + 'msPKI-Cert-Template-OID' { + $IssueDetail = @" +This Object Identifier (OID) can be modified into an Application Policy and linked +to an empty Universal Group. + +If $($_.nTSecurityDescriptor.Owner) also has control over a certificate template +(see ESC4), an attacker could link this Application Policy to the template. Once +linked, any certificates issued from that template would allow an attacker to +act as a member of the linked group (see ESC13). +"@ + } + pKIEnrollmentService { + $IssueDetail = @" +$($_.nTSecurityDescriptor.Owner) can use these elevated rights to publish currently +unpublished templates. + +If $($_.nTSecurityDescriptor.Owner) also has control over an unpublished certificate +template (see ESC4), they could modify the template into an ESC1 template then +publish the certificate. This published certificate could be use for privilege +escalation and persistence. +"@ + } + } + if ($_.objectClass -eq 'certificationAuthority' -and $_.Name -eq 'NTAuthCertificates') { + $IssueDetail = @" +The NTAuthCertificates object determines which Certification Authorities are +trusted by Active Directory (AD) for client authentication of all forms. + +$($_.nTSecurityDescriptor.Owner) can use their granted rights on NTAuthCertificates +to add their own rogue CAs. Once the rogue CA is trusted, any client authentication +certificates generated by the it can be used by the attacker. +"@ + } + $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName - Issue = "$($_.nTSecurityDescriptor.Owner) has Owner rights on this template" + objectClass = $_.objectClass + Issue = @" +$($_.nTSecurityDescriptor.Owner) has Owner rights on this object. They are able +to modify this object in whatever way they wish. + +$IssueDetail + +"@ Fix = @" `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$PreferredOwner`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' @@ -726,6 +1024,51 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL" else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } + + switch ($_.objectClass) { + computer { + $IssueDetail = @" +This computer is hosting a Certification Authority (CA). It is likely +$($entry.IdentityReference) can take control of this object. + +There is little reason for anyone other than AD Admins to have elevated rights +to this CA host. +"@ + } + 'msPKI-Cert-Template-OID' { + $IssueDetail = @" +This Object Identifier (OID) can be modified into an Application Policy and linked +to an empty Universal Group. + +If $($entry.IdentityReference) also has control over a certificate template +(see ESC4), an attacker could link this Application Policy to the template. Once +linked, any certificates issued from that template would allow an attacker to +act as a member of the linked group (see ESC13). +"@ + } + pKIEnrollmentService { + $IssueDetail = @" +$($entry.IdentityReference) can use these elevated rights to publish currently +unpublished templates. + +If $($entry.IdentityReference) also has control over an unpublished certificate +template (see ESC4), they could modify the template into an ESC1 template then +publish the certificate. This published certificate could be use for privilege +escalation and persistence. +"@ + } + } + if ($_.objectClass -eq 'certificationAuthority' -and $_.Name -eq 'NTAuthCertificates') { + $IssueDetail = @" +The NTAuthCertificates object determines which Certification Authorities are +trusted by Active Directory (AD) for client authentication of all forms. + +$($entry.IdentityReference) can use their granted rights on NTAuthCertificates +to add their own rogue CAs. Once the rogue CA is trusted, any client authentication +certificates generated by the it can be used by the attacker. +"@ + } + if ( ($_.objectClass -ne 'pKICertificateTemplate') -and ($SID -notmatch $SafeUsers) -and ($entry.AccessControlType -eq 'Allow') -and @@ -737,11 +1080,18 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL" DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) rights on this object" + Issue = @" +$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) elevated rights +on this $($_.objectClass) object. + +$IssueDetail + +"@ Fix = @" `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' foreach ( `$ace in `$ACL.access ) { - if ( (`$ace.IdentityReference.Value -like '$($Principal.Value)' ) -and ( `$ace.ActiveDirectoryRights -notmatch '^ExtendedRight$') ) { + if ( (`$ace.IdentityReference.Value -like '$($Principal.Value)' ) -and + ( `$ace.ActiveDirectoryRights -notmatch '^ExtendedRight$') ) { `$ACL.RemoveAccessRule(`$ace) | Out-Null } } @@ -780,7 +1130,7 @@ function Find-ESC6 { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) process { @@ -794,21 +1144,38 @@ function Find-ESC6 { Name = $_.Name DistinguishedName = $_.DistinguishedName Technique = 'ESC6' - Issue = $_.AuditFilter + Issue = $_.SANFlag Fix = 'N/A' Revert = 'N/A' } if ($_.SANFlag -eq 'Yes') { - $Issue.Issue = 'EDITF_ATTRIBUTESUBJECTALTNAME2 is enabled.' + $Issue.Issue = @" +The dangerous EDITF_ATTRIBUTESUBJECTALTNAME2 flag is enabled on $CAFullname. +All templates published on this CA will accept a Subject Alternative Name (SAN) +during enrollment even if the template is not specifically configured to allow a SAN. + +As of May 2022, Microsoft has neutered this situation by requiring all SANs to +be strongly mapped to certificates. + +However, if strong mapping has been explicitly disabled on Domain Controllers, +this configuration remains vulnerable to privilege escalation attacks. + +"@ $Issue.Fix = @" +# Disable the flag certutil -config $CAFullname -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2 -Invoke-Command -ComputerName `"$($_.dNSHostName)`" -ScriptBlock { + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { Get-Service -Name `'certsvc`' | Restart-Service -Force } "@ $Issue.Revert = @" +# Enable the flag certutil -config $CAFullname -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2 -Invoke-Command -ComputerName `"$($_.dNSHostName)`" -ScriptBlock { + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { Get-Service -Name `'certsvc`' | Restart-Service -Force } "@ @@ -849,7 +1216,7 @@ function Find-ESC8 { [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) @@ -864,7 +1231,16 @@ function Find-ESC8 { DistinguishedName = $_.DistinguishedName CAEnrollmentEndpoint = $endpoint.URL AuthType = $endpoint.Auth - Issue = 'An HTTP enrollment endpoint is available.' + Issue = @' +An HTTP enrollment endpoint is available. It is possible to relay NTLM +authentication to this HTTP endpoint. + +If the LAN Manager authentication level of any domain in this forest is 2 or +less, an attacker can coerce authentication from a Domain Controller (DC) and +relay it to this HTTP enrollment enpoint to receive a certificate which can be +used to authenticate as that DC. + +'@ Fix = @' Disable HTTP access and enforce HTTPS. Enable EPA. @@ -874,7 +1250,18 @@ Disable NTLM authentication (if possible.) Technique = 'ESC8' } if ($endpoint.URL -match '^https:') { - $Issue.Issue = 'An HTTPS enrollment endpoint is available.' + $Issue.Issue = @' +An HTTPS enrollment endpoint is available. It may be possible to relay NTLM +authentication to this HTTPS endpoint. Enabling IIS Extended Protection for +Authentication or disabling NTLM authentication completely, NTLM relay is not +possible. + +If those protection are not in place, and the LAN Manager authentication level +of any domain in this forest is 2 or less, an attacker can coerce authentication +from a Domain Controller (DC) and relay it to this HTTPS enrollment enpoint to +receive a certificate which can be used to authenticate as that DC. + +'@ $Issue.Fix = @' Ensure EPA is enabled. Disable NTLM authentication (if possible.) @@ -938,7 +1325,7 @@ function Find-ESC9 { [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) @@ -1028,7 +1415,7 @@ function Format-Result { [CmdletBinding()] param( $Issue, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [int]$Mode ) @@ -1037,21 +1424,32 @@ function Format-Result { ESC1 = 'ESC1 - Vulnerable Certificate Template - Authentication' ESC2 = 'ESC2 - Vulnerable Certificate Template - Subordinate CA' ESC3 = 'ESC3 - Vulnerable Certificate Template - Enrollment Agent' - ESC4 = 'ESC4 - Vulnerable Access Control - Certifcate Template' + ESC4 = 'ESC4 - Vulnerable Access Control - Certificate Template' ESC5 = 'ESC5 - Vulnerable Access Control - PKI Object' ESC6 = 'ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2 Flag Enabled' ESC8 = 'ESC8 - HTTP/S Enrollment Enabled' + ESC11 = 'ESC11 - IF_ENFORCEENCRYPTICERTREQUEST Flag Disabled' + ESC13 = 'ESC13 - Vulnerable Certificate Temple - Group-Linked' } if ($null -ne $Issue) { $UniqueIssue = $Issue.Technique | Sort-Object -Unique - Write-Host "`n########## $($IssueTable[$UniqueIssue]) ##########`n" + $Title = $($IssueTable[$UniqueIssue]) + Write-Host "$('-'*($($Title.ToString().Length + 10)))" -ForegroundColor Black -BackgroundColor Magenta -NoNewline; Write-Host + Write-Host " " -BackgroundColor Magenta -NoNewline + Write-Host $Title -BackgroundColor Magenta -ForegroundColor Black -NoNewline + Write-Host " " -BackgroundColor Magenta -NoNewline; Write-Host + Write-Host "$('-'*($($Title.ToString().Length + 10)))" -ForegroundColor Black -BackgroundColor Magenta -NoNewline; Write-Host + switch ($Mode) { 0 { $Issue | Format-Table Technique, Name, Issue -Wrap } 1 { - if ($Issue.Technique -eq 'ESC8') { + if ($Issue.Technique -eq 'ESC5') { + $Issue | Format-List Technique, Name, objectClass, DistinguishedName, Issue, Fix + } + elseif ($Issue.Technique -eq 'ESC8') { $Issue | Format-List Technique, Name, DistinguishedName, CAEnrollmentEndpoint, AuthType, Issue, Fix } else { @@ -1347,6 +1745,12 @@ function Invoke-Remediation { .PARAMETER ESC6 A PS Object containing all necessary information about ESC6 issues. + .PARAMETER ESC11 + A PS Object containing all necessary information about ESC11 issues. + + .PARAMETER ESC13 + A PS Object containing all necessary information about ESC13 issues. + .INPUTS PS Objects @@ -1362,7 +1766,9 @@ function Invoke-Remediation { $ESC3, $ESC4, $ESC5, - $ESC6 + $ESC6, + $ESC11, + $ESC13 ) Write-Host "`nExecuting Mode 4 - Attempting to fix identified issues!`n" -ForegroundColor Green @@ -1370,7 +1776,18 @@ function Invoke-Remediation { Write-Host 'Invoke-RevertLocksmith.ps1' -ForegroundColor White -NoNewline Write-Host ") which can be used to revert all changes made by Locksmith...`n" try { - Export-RevertScript -AuditingIssues $AuditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Export-RevertScript @params } catch { Write-Warning 'Creation of Invoke-RevertLocksmith.ps1 failed.' @@ -1390,7 +1807,7 @@ function Invoke-Remediation { $AuditingIssues | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Auditing is not fully enabled on Certification Authority `"$($_.Name)`".`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -1423,7 +1840,7 @@ function Invoke-Remediation { $ESC1 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Security Principals can enroll in `"$($_.Name)`" template using a Subject Alternative Name without Manager Approval.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -1456,7 +1873,7 @@ function Invoke-Remediation { $ESC2 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Security Principals can enroll in `"$($_.Name)`" template and create a Subordinate Certification Authority without Manager Approval.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -1519,7 +1936,7 @@ function Invoke-Remediation { } } if ($ESC5) { - $ESC5 | Where-Object Issue -Like "* Owner rights *" | ForEach-Object { # This selector sucks - Jake + $ESC5 | Where-Object Issue -Like "* Owner rights *" | ForEach-Object { # TODO This selector sucks - Jake $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White Write-Host "$($_.Issue)`n" @@ -1555,7 +1972,7 @@ function Invoke-Remediation { $ESC6 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "The Certification Authority `"$($_.Name)`" has the dangerous EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -1586,6 +2003,75 @@ function Invoke-Remediation { } } + if ($ESC11) { + $ESC11 | ForEach-Object { + $FixBlock = [scriptblock]::Create($_.Fix) + Write-Host 'ISSUE:' -ForegroundColor White + Write-Host "$($_.Issue)`n" + Write-Host 'TECHNIQUE:' -ForegroundColor White + Write-Host "$($_.Technique)`n" + Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White + Write-Host "Locksmith will attempt to enable the IF_ENFORCEENCRYPTICERTREQUEST flag on Certifiction Authority `"$($_.Name)`".`n" + Write-Host 'COMMAND(S) TO BE RUN' -ForegroundColor White + Write-Host 'PS> ' -NoNewline + Write-Host "$($_.Fix)`n" -ForegroundColor Cyan + $WarningError = 'n' + Write-Host 'OPERATIONAL IMPACT:' -ForegroundColor White + Write-Host "WARNING: This change could cause some services to stop working.`n" -ForegroundColor Yellow + Write-Host "If you continue, Locksmith will attempt to fix this issue.`n" -ForegroundColor Yellow + Write-Host "Continue with this operation? [Y] Yes " -NoNewline + Write-Host "[N] " -ForegroundColor Yellow -NoNewline + Write-Host "No: " -NoNewline + $WarningError = '' + $WarningError = Read-Host + if ($WarningError -like 'y') { + try { + Invoke-Command -ScriptBlock $FixBlock + } + catch { + Write-Error 'Could not enable the IF_ENFORCEENCRYPTICERTREQUEST flag. Are you an Active Directory or AD CS admin?' + } + } + else { + Write-Host "SKIPPED!`n" -ForegroundColor Yellow + } + } + } + + if ($ESC13) { + $ESC13 | ForEach-Object { + $FixBlock = [scriptblock]::Create($_.Fix) + Write-Host 'ISSUE:' -ForegroundColor White + Write-Host "$($_.Issue)`n" + Write-Host 'TECHNIQUE:' -ForegroundColor White + Write-Host "$($_.Technique)`n" + Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White + Write-Host "Locksmith will attempt to enable Manager Approval on the `"$($_.Name)`" template.`n" + Write-Host 'CCOMMAND(S) TO BE RUN:' + Write-Host 'PS> ' -NoNewline + Write-Host "$($_.Fix)`n" -ForegroundColor Cyan + Write-Host 'OPERATIONAL IMPACT:' -ForegroundColor White + Write-Host "WARNING: This change could cause some services to stop working until certificates are approved.`n" -ForegroundColor Yellow + Write-Host "If you continue, Locksmith will attempt to fix this issue.`n" -ForegroundColor Yellow + Write-Host "Continue with this operation? [Y] Yes " -NoNewline + Write-Host "[N] " -ForegroundColor Yellow -NoNewline + Write-Host "No: " -NoNewline + $WarningError = '' + $WarningError = Read-Host + if ($WarningError -like 'y') { + try { + Invoke-Command -ScriptBlock $FixBlock + } + catch { + Write-Error 'Could not enable Manager Approval. Are you an Active Directory or AD CS admin?' + } + } + else { + Write-Host "SKIPPED!`n" -ForegroundColor Yellow + } + } + } + Write-Host "Mode 4 Complete! There are no more issues that Locksmith can automatically resolve.`n" -ForegroundColor Green Write-Host 'If you experience any operational impact from using Locksmith Mode 4, use ' -NoNewline Write-Host 'Invoke-RevertLocksmith.ps1 ' -ForegroundColor White @@ -1603,40 +2089,27 @@ function Invoke-Scans { .SYNOPSIS Invoke-Scans.ps1 is a script that performs various scans on ADCS (Active Directory Certificate Services) objects. - .DESCRIPTION - This script accepts a parameter named $Scans, which specifies the type of scans to perform. The available scan options are: - - Auditing - - ESC1 - - ESC2 - - ESC3 - - ESC4 - - ESC5 - - ESC6 - - ESC8 - - All - - PromptMe - .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', 'All', 'PromptMe'. + The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'All', 'PromptMe'. .NOTES - The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3Condition1, - Find-ESC3Condition2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8. + Find-ESC3Condition2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13 - 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. .EXAMPLE - # Perform all scans - Invoke-Scans + Invoke-Scans + # Perform all scans .EXAMPLE - # Perform only the 'Auditing' and 'ESC1' scans - Invoke-Scans -Scans 'Auditing', 'ESC1' + Invoke-Scans -Scans 'Auditing', 'ESC1' + # Perform only the 'Auditing' and 'ESC1' scans .EXAMPLE - # Prompt the user to select the scans to perform - Invoke-Scans -Scans 'PromptMe' + Invoke-Scans -Scans 'PromptMe' + # Prompt the user to select the scans to perform #> [CmdletBinding()] @@ -1651,7 +2124,7 @@ function Invoke-Scans { [int]$Mode, $SafeObjectTypes, $SafeOwners, - [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'All', 'PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'All', 'PromptMe')] [array]$Scans = 'All', $UnsafeOwners, $UnsafeUsers, @@ -1699,46 +2172,55 @@ function Invoke-Scans { [array]$ESC3 += Find-ESC3Condition2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers } ESC4 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC4)...' + Write-Host 'Identifying AD CS templates with poor access control (ESC4)...' [array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes } ESC5 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC5)...' + Write-Host 'Identifying AD CS objects with poor access control (ESC5)...' [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes } ESC6 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC6)...' + Write-Host 'Identifying Issuing CAs with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects } ESC8 { Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects } + ESC6 { + Write-Host 'Identifying Issuing CAs with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' + [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects + } All { Write-Host 'Identifying auditing issues...' [array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects Write-Host 'Identifying AD CS templates with dangerous ESC1 configurations...' - [array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers + [array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus Write-Host 'Identifying AD CS templates with dangerous ESC2 configurations...' [array]$ESC2 = Find-ESC2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC3 configurations...' [array]$ESC3 = Find-ESC3Condition1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers [array]$ESC3 += Find-ESC3Condition2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC4)...' + Write-Host 'Identifying AD CS templates with poor access control (ESC4)...' [array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -Mode $Mode - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC5)...' + Write-Host 'Identifying AD CS objects with poor access control (ESC5)...' [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes - Write-Host 'Identifying Certificate Authorities configured with dangerous flags (ESC6)...' + Write-Host 'Identifying Certificate Authorities with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled v (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects + Write-Host 'Identifying Certificate Authorities with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' + [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects + Write-Host 'Identifying AD CS templates with dangerous ESC13 configurations...' + [array]$ESC13 = Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus + Write-Host } } - [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + $ESC11 + $ESC13 # If these are all empty = no issues found, exit - if ((!$AuditingIssues) -and (!$ESC1) -and (!$ESC2) -and (!$ESC3) -and (!$ESC4) -and (!$ESC5) -and (!$ESC6) -and (!$ESC8) ) { + if ((!$AuditingIssues) -and (!$ESC1) -and (!$ESC2) -and (!$ESC3) -and (!$ESC4) -and (!$ESC5) -and (!$ESC6) -and (!$ESC8) -and ($ESC11) -and ($ESC13) ) { Write-Host "`n$(Get-Date) : No ADCS issues were found." -ForegroundColor Green break } @@ -1754,6 +2236,8 @@ function Invoke-Scans { ESC5 = $ESC5 ESC6 = $ESC6 ESC8 = $ESC8 + ESC11 = $ESC11 + ESC13 = $ESC13 } } @@ -1884,15 +2368,24 @@ function New-Dictionary { # FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} # ReferenceUrls = '' # }, - # [VulnerableConfigurationItem]@{ - # Name = 'ESC11' - # Category = 'Escalation Path' - # Subcategory = '' - # Summary = '' - # FindIt = {Find-ESC11} - # FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} - # ReferenceUrls = '' - # }, + [VulnerableConfigurationItem]@{ + Name = 'ESC11' + Category = 'Escalation Path' + Subcategory = 'IF_ENFORCEENCRYPTICERTREQUEST' + Summary = '' + FindIt = { Find-ESC11 } + FixIt = { Write-Output 'Add code to fix the vulnerable configuration.' } + ReferenceUrls = 'https://blog.compass-security.com/2022/11/relaying-to-ad-certificate-services-over-rpc/' + }, + [VulnerableConfigurationItem]@{ + Name = 'ESC13' + Category = 'Escalation Path' + Subcategory = 'Certificate Template linked to Group' + Summary = '' + FindIt = { Find-ESC13 } + FixIt = { Write-Output 'Add code to fix the vulnerable configuration.' } + ReferenceUrls = 'https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53' + }, [VulnerableConfigurationItem]@{ Name = 'Auditing' Category = 'Server Configuration' @@ -1975,17 +2468,35 @@ function Set-AdditionalCAProperty { begin { $CAEnrollmentEndpoint = @() - $code = @" -using System.Net; -using System.Security.Cryptography.X509Certificates; -public class TrustAllCertsPolicy : ICertificatePolicy { - public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { - return true; - } -} + if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy') ) { + if ($PSVersionTable.PSEdition -eq 'Desktop') { + $code = @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { + return true; + } + } +"@ + Add-Type -TypeDefinition $code -Language CSharp + [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + } + else { + Add-Type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + using System.Net.Security; + public class TrustAllCertsPolicy { + public static bool TrustAllCerts(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { + return true; + } + } "@ - Add-Type -TypeDefinition $code -Language CSharp - [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + # Set the ServerCertificateValidationCallback + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [TrustAllCertsPolicy]::TrustAllCerts + } + } } process { @@ -2011,7 +2522,7 @@ public class TrustAllCertsPolicy : ICertificatePolicy { try { $FullURL = "https$URL" $Request = [System.Net.WebRequest]::Create($FullURL) - + $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -2048,7 +2559,7 @@ public class TrustAllCertsPolicy : ICertificatePolicy { $CAHostFQDN = (Get-ADObject -Filter { (Name -eq $CAHostName) -and (objectclass -eq 'computer') } -Properties DnsHostname -Server $ForestGC).DnsHostname } $ping = Test-Connection -ComputerName $CAHostFQDN -Quiet -Count 1 - if ($ping) { + if ($ping) { try { if ($Credential) { $CertutilAudit = Invoke-Command -ComputerName $CAHostname -Credential $Credential -ScriptBlock { param($CAFullName); certutil -config $CAFullName -getreg CA\AuditFilter } -ArgumentList $CAFullName @@ -2069,12 +2580,24 @@ public class TrustAllCertsPolicy : ICertificatePolicy { } } catch { - $AuditFilter = 'Failure' + $SANFlag = 'Failure' + } + try { + if ($Credential) { + $CertutilInterfaceFlag = Invoke-Command -ComputerName $CAHostname -Credential $Credential -ScriptBlock { param($CAFullName); certutil -config $CAFullName -getreg policy\EditFlags } -ArgumentList $CAFullName + } + else { + $CertutilInterfaceFlag = certutil -config $CAFullName -getreg CA\InterfaceFlags + } + } + catch { + $InterfaceFlag = 'Failure' } } else { $AuditFilter = 'CA Unavailable' $SANFlag = 'CA Unavailable' + $InterfaceFlag = 'CA Unavailable' } if ($CertutilAudit) { try { @@ -2100,12 +2623,22 @@ public class TrustAllCertsPolicy : ICertificatePolicy { $SANFlag = 'No' } } + if ($CertutilInterfaceFlag) { + [string]$InterfaceFlag = $CertutilInterfaceFlag | Select-String ' IF_ENFORCEENCRYPTICERTREQUEST -- 200 \(' + if ($InterfaceFlag) { + $InterfaceFlag = 'Yes' + } + else { + $InterfaceFlag = '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 Add-Member -InputObject $_ -MemberType NoteProperty -Name CAHostname -Value $CAHostname -Force 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 } } } @@ -2114,7 +2647,7 @@ function Set-Severity { [OutputType([string])] [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$Issue ) foreach ($Finding in $Issue) { @@ -2147,6 +2680,44 @@ function Set-Severity { } } +function Show-LocksmithLogo { + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%#+==============#%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%#=====================#%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%#=========================#%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%=============================%%%%%%%%%%%' + Write-Host '%%%%%%%%%#==============+++==============#%%%%%%%%%' + Write-Host '%%%%%%%%#===========#%%%%%%%%%#===========#%%%%%%%%' + Write-Host '%%%%%%%%==========%%%%%%%%%%%%%%%==========%%%%%%%%' + Write-Host '%%%%%%%*=========%%%%%%%%%%%%%%%%%=========*%%%%%%%' + Write-Host '%%%%%%%+========*%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '#=================================================#' + Write-Host '#=================================================#' + Write-Host '#=================+%%%============================#' + Write-Host '#==================%%%%*==========================#' + Write-Host '#===================*%%%%+========================#' + Write-Host '#=====================#%%%%=======================#' + Write-Host '#======================+%%%%#=====================#' + Write-Host '#========================*%%%%*===================#' + Write-Host '#========================+%%%%%===================#' + Write-Host '#======================#%%%%%+====================#' + Write-Host '#===================+%%%%%%=======================#' + Write-Host '#=================#%%%%%+=========================#' + Write-Host '#==============+%%%%%#============================#' + Write-Host '#============*%%%%%+====+%%%%%%%%%%===============#' + Write-Host '#=============%%*========+********+===============#' + Write-Host '#=================================================#' + Write-Host '#=================================================#' + Write-Host '#=================================================#' +} + function Test-IsADAdmin { <# .SYNOPSIS @@ -2569,7 +3140,7 @@ function Invoke-Locksmith { Finds the most common malconfigurations of Active Directory Certificate Services (AD CS). .DESCRIPTION - Locksmith uses the Active Directory (AD) Powershell (PS) module to identify 7 misconfigurations + Locksmith uses the Active Directory (AD) Powershell (PS) module to identify 10 misconfigurations commonly found in Enterprise mode AD CS installations. .COMPONENT @@ -2643,7 +3214,7 @@ function Invoke-Locksmith { [Parameter()] [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'All', 'PromptMe')] [array]$Scans = 'All', - + # The directory to save the output in (defaults to the current working directory). [Parameter()] [ValidateScript({ Test-Path -Path $_ -PathType Container })] @@ -2654,7 +3225,8 @@ function Invoke-Locksmith { [System.Management.Automation.PSCredential]$Credential ) - $Version = '2024.10' + $Version = '2024.11.10' + $LogoPart1 = @" _ _____ _______ _ _ _______ _______ _____ _______ _ _ | | | | |____/ |______ | | | | | |_____| @@ -2691,7 +3263,7 @@ function Invoke-Locksmith { # For output filenames [string]$FilePrefix = "Locksmith $(Get-Date -Format 'yyyy-MM-dd hh-mm-ss')" - # Extended Key Usages for client authentication. A requirement for ESC1 + # Extended Key Usages for client authentication. A requirement for ESC1, ESC3 Condition 2, and ESC13 $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' # GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object. @@ -2824,7 +3396,8 @@ function Invoke-Locksmith { $ESC5 = $Results['ESC5'] $ESC6 = $Results['ESC6'] $ESC8 = $Results['ESC8'] - #} + $ESC11 = $Results['ESC11'] + $ESC13 = $Results['ESC13'] # If these are all empty = no issues found, exit if ($null -eq $Results) { @@ -2844,6 +3417,8 @@ function Invoke-Locksmith { Format-Result $ESC5 '0' Format-Result $ESC6 '0' Format-Result $ESC8 '0' + Format-Result $ESC11 '0' + Format-Result $ESC13 '0' } 1 { Format-Result $AuditingIssues '1' @@ -2854,6 +3429,8 @@ function Invoke-Locksmith { Format-Result $ESC5 '1' Format-Result $ESC6 '1' Format-Result $ESC8 '1' + Format-Result $ESC11 '1' + Format-Result $ESC13 '1' } 2 { $Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV" @@ -2878,13 +3455,23 @@ function Invoke-Locksmith { } } 4 { - Invoke-Remediation -AuditingIssues $AuditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Invoke-Remediation @params } } Write-Host 'Thank you for using ' -NoNewline - Write-Host "❤ Locksmith ❤`n" -ForegroundColor Magenta + Write-Host "Locksmith ❤`n" -ForegroundColor Magenta } -# Export functions and aliases as required Invoke-Locksmith -Mode $Mode -Scans $Scans diff --git a/Locksmith.psd1 b/Locksmith.psd1 index 7d3dcd3..c5564ac 100644 --- a/Locksmith.psd1 +++ b/Locksmith.psd1 @@ -7,7 +7,7 @@ Description = 'A small tool to find and fix common misconfigurations in Active Directory Certificate Services.' FunctionsToExport = @('*') GUID = 'b1325b42-8dc4-4f17-aa1f-dcb5984ca14a' - ModuleVersion = '2024.10' + ModuleVersion = '2024.11.10' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ diff --git a/Private/ConvertFrom-IdentityReference.ps1 b/Private/ConvertFrom-IdentityReference.ps1 index 5e3ec68..79f2a5a 100644 --- a/Private/ConvertFrom-IdentityReference.ps1 +++ b/Private/ConvertFrom-IdentityReference.ps1 @@ -22,7 +22,7 @@ function ConvertFrom-IdentityReference { #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$Object ) diff --git a/Private/Export-RevertScript.ps1 b/Private/Export-RevertScript.ps1 index afffed9..8632239 100644 --- a/Private/Export-RevertScript.ps1 +++ b/Private/Export-RevertScript.ps1 @@ -30,8 +30,25 @@ .PARAMETER ESC6 An array of ESC6 changes to be reverted. + .PARAMETER ESC11 + An array of ESC11 changes to be reverted. + + .PARAMETER ESC13 + An array of ESC13 changes to be reverted. + .EXAMPLE - Export-RevertScript -AuditingIssues $auditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Export-RevertScript @params Reverts the changes performed by Locksmith using the specified arrays of objects. #> @@ -43,19 +60,22 @@ [array]$ESC3, [array]$ESC4, [array]$ESC5, - [array]$ESC6 + [array]$ESC6, + [array]$ESC11, + [array]$ESC13 ) begin { $Output = 'Invoke-RevertLocksmith.ps1' - Set-Content -Path $Output -Value "<#`nScript to revert changes performed by Locksmith`nCreated $(Get-Date)`n#>" -Force - $Objects = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $RevertScript = [System.Text.StringBuilder]::New() + [void]$RevertScript.Append("<#`nScript to revert changes performed by Locksmith`nCreated $(Get-Date)`n#>`n") + $Objects = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC11 + $ESC13 } process { if ($Objects) { $Objects | ForEach-Object { - Add-Content -Path $Output -Value $_.Revert - Start-Sleep -Seconds 5 + [void]$RevertScript.Append("$($_.Revert)`n") } + $RevertScript.ToString() | Out-File -FilePath $Output } } } diff --git a/Private/Find-AuditingIssue.ps1 b/Private/Find-AuditingIssue.ps1 index 7a2e99d..4b42398 100644 --- a/Private/Find-AuditingIssue.ps1 +++ b/Private/Find-AuditingIssue.ps1 @@ -32,7 +32,7 @@ [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects ) @@ -45,7 +45,7 @@ Name = $_.Name DistinguishedName = $_.DistinguishedName Technique = 'DETECT' - Issue = "Auditing is not fully enabled on $($_.CAFullName). Current value is $($_.AuditFilter)" + Issue = "Auditing is not fully enabled on $($_.CAFullName). Important security events may go unnoticed." Fix = @" certutil.exe -config `'$($_.CAFullname)`' -setreg `'CA\AuditFilter`' 127 Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { diff --git a/Private/Find-ESC1.ps1 b/Private/Find-ESC1.ps1 index a6499e3..f632b2f 100644 --- a/Private/Find-ESC1.ps1 +++ b/Private/Find-ESC1.ps1 @@ -14,21 +14,27 @@ .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 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 $ADCSObjects = Get-ADCSObjects $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-9$|-526$|-527$|S-1-5-10' - $Results = $ADCSObjects | Find-ESC1 -SafeUsers $SafeUsers + $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 = $ADCSObjects | Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs $Results #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] - [array]$SafeUsers + [Parameter(Mandatory)] + [array]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs ) $ADCSObjects | Where-Object { ($_.objectClass -eq 'pKICertificateTemplate') -and @@ -37,6 +43,7 @@ !($_.'msPKI-Enrollment-Flag' -band 2) -and ( ($_.'msPKI-RA-Signature' -eq 0) -or ($null -eq $_.'msPKI-RA-Signature') ) } | ForEach-Object { + # Write-Host $_; continue foreach ($entry in $_.nTSecurityDescriptor.Access) { $Principal = New-Object System.Security.Principal.NTAccount($entry.IdentityReference) if ($Principal -match '^(S-1|O:)') { @@ -44,23 +51,34 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Client Authentication template using a SAN without Manager Approval" - Fix = @" + Issue = @" +$($entry.IdentityReference) can provide a Subject Alternative Name (SAN) while +enrolling in this Client Authentication template, and enrollment does not require +Manager Approval. + +The resultant certificate can be used by an attacker to authenticate as any +principal listed in the SAN up to and including Domain Admins, Enterprise Admins, +or Domain Controllers. + +"@ + Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ - Revert = @" + Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ - Technique = 'ESC1' + Technique = 'ESC1' } $Issue } diff --git a/Private/Find-ESC11.ps1 b/Private/Find-ESC11.ps1 new file mode 100644 index 0000000..da501b8 --- /dev/null +++ b/Private/Find-ESC11.ps1 @@ -0,0 +1,76 @@ +function Find-ESC11 { + <# + .SYNOPSIS + This script finds AD CS (Active Directory Certificate Services) objects that have the ESC11 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 InterfaceFlag set to 'No'. 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 ADCS 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. + + .EXAMPLE + $ADCSObjects = Get-ADCSObject -Target (Get-Target) + Find-ESC11 -ADCSObjects $ADCSObjects + $Results + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + $ADCSObjects + ) + process { + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKIEnrollmentService') -and + ($_.InterfaceFlag -ne 'Yes') + } | ForEach-Object { + [string]$CAFullName = "$($_.dNSHostName)\$($_.Name)" + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + Technique = 'ESC11' + Issue = $_.InterfaceFlag + Fix = 'N/A' + Revert = 'N/A' + } + if ($_.InterfaceFlag -eq 'No') { + $Issue.Issue = @" +The IF_ENFORCEENCRYPTICERTREQUEST flag is disabled on this Certification +Authority (CA). It is possible to relay NTLM authentication to the RPC interface +of this CA. + +If the LAN Manager authentication level of any domain in this forest is 2 or +less, an attacker can coerce authentication from a Domain Controller (DC) to +receive a certificate which can be used to authenticate as that DC. + +"@ + $Issue.Fix = @" +# Enable the flag +certutil -config $CAFullname -setreg CA\InterfaceFlags +IF_ENFORCEENCRYPTICERTREQUEST + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { + Get-Service -Name `'certsvc`' | Restart-Service -Force +} +"@ + $Issue.Revert = @" +# Disable the flag +certutil -config $CAFullname -setreg CA\InterfaceFlags -IF_ENFORCEENCRYPTICERTREQUEST + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { + Get-Service -Name `'certsvc`' | Restart-Service -Force +} +"@ + } + $Issue + } + } +} diff --git a/Private/Find-ESC13.ps1 b/Private/Find-ESC13.ps1 new file mode 100644 index 0000000..d3007d1 --- /dev/null +++ b/Private/Find-ESC13.ps1 @@ -0,0 +1,91 @@ +function Find-ESC13 { + <# + .SYNOPSIS + This script finds AD CS (Active Directory Certificate Services) objects that have the ESC13 vulnerability. + + .DESCRIPTION + 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. + + .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 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 + $ADCSObjects = Get-ADCSObjects + $SafeUsers = '-512$|-519$|-544$|-18$|-517$|-500$|-516$|-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 = $ADCSObjects | Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEKUs + $Results + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [Microsoft.ActiveDirectory.Management.ADEntity[]]$ADCSObjects, + [Parameter(Mandatory)] + [array]$SafeUsers, + [Parameter(Mandatory)] + $ClientAuthEKUs + ) + + $ADCSObjects | Where-Object { + ($_.objectClass -eq 'pKICertificateTemplate') -and + ($_.pkiExtendedKeyUsage -match $ClientAuthEKUs) -and + ($_.'msPKI-Certificate-Policy') + } | ForEach-Object { + foreach ($policy in $_.'msPKI-Certificate-Policy') { + if ($ADCSObjects.'msPKI-Cert-Template-OID' -contains $policy) { + $OidToCheck = $ADCSObjects | Where-Object 'msPKI-Cert-Template-OID' -eq $policy + if ($OidToCheck.'msDS-OIDToGroupLink') { + 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') ) { + $Issue = [pscustomobject]@{ + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + IdentityReference = $entry.IdentityReference + ActiveDirectoryRights = $entry.ActiveDirectoryRights + LinkedGroup = $OidToCheck.'msDS-OIDToGroupLink' + Issue = @" +$($entry.IdentityReference) can enroll in this Client Authentication template +which is linked to the group $($OidToCheck.'msDS-OIDToGroupLink'). + +If $($entry.IdentityReference) uses this certificate for authentication, they +will gain the rights of the linked group while the group membership appears empty. + +"@ + 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 = 'ESC13' + } + $Issue + } + } + } + } + } + } +} diff --git a/Private/Find-ESC2.ps1 b/Private/Find-ESC2.ps1 index 9d333a1..b45b1c7 100644 --- a/Private/Find-ESC2.ps1 +++ b/Private/Find-ESC2.ps1 @@ -25,15 +25,14 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [string]$SafeUsers ) $ADCSObjects | Where-Object { ($_.ObjectClass -eq 'pKICertificateTemplate') -and - ( (!$_.pkiExtendedKeyUsage) -or ($_.pkiExtendedKeyUsage -match '2.5.29.37.0') )-and - ($_.'msPKI-Certificate-Name-Flag' -band 1) -and + ( (!$_.pkiExtendedKeyUsage) -or ($_.pkiExtendedKeyUsage -match '2.5.29.37.0') ) -and !($_.'msPKI-Enrollment-Flag' -band 2) -and ( ($_.'msPKI-RA-Signature' -eq 0) -or ($null -eq $_.'msPKI-RA-Signature') ) } | ForEach-Object { @@ -44,21 +43,37 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can request a SubCA certificate without Manager Approval" - Fix = @" + Issue = @" +$($entry.IdentityReference) can use this template to request a Subordinate +Certification Authority (SubCA) certificate without Manager Approval. + +The resultant certificate can be used by an attacker to instantiate their own +SubCA which is trusted by AD. + +By default, certificates created from this attacker-controlled SubCA cannot be +used for authentication, but they can be used for other purposes such as TLS +certs and code signing. + +However, if an attacker can modify the NtAuthCertificates object (see ESC5), +they can convert their rogue CA into one trusted for authentication. + +"@ + Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ - Revert = @" + Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC2' } diff --git a/Private/Find-ESC3Condition1.ps1 b/Private/Find-ESC3Condition1.ps1 index 7da08ee..b9fee01 100644 --- a/Private/Find-ESC3Condition1.ps1 +++ b/Private/Find-ESC3Condition1.ps1 @@ -25,9 +25,9 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$SafeUsers ) $ADCSObjects | Where-Object { @@ -43,21 +43,30 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Enrollment Agent template without Manager Approval" - Fix = @" + Issue = @" +$($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 +an Enrollment Agent to submit the request. + +"@ + Fix = @" +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ - Revert = @" + Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC3' } diff --git a/Private/Find-ESC3Condition2.ps1 b/Private/Find-ESC3Condition2.ps1 index c8ab537..4a82fc3 100644 --- a/Private/Find-ESC3Condition2.ps1 +++ b/Private/Find-ESC3Condition2.ps1 @@ -25,9 +25,9 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$SafeUsers ) $ADCSObjects | Where-Object { @@ -44,21 +44,30 @@ } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } - if ( ($SID -notmatch $SafeUsers) -and ($entry.ActiveDirectoryRights -match 'ExtendedRight') ) { + if ( ($SID -notmatch $SafeUsers) -and ( ($entry.ActiveDirectoryRights -match 'ExtendedRight') -or ($entry.ActiveDirectoryRights -match 'GenericAll') ) ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) can enroll in this Client Authentication template using a SAN without Manager Approval" + Issue = @" +If the holder of an Enrollment Agent certificate requests a certificate using +this template, they will receive a certificate which allows them to authenticate +as $($entry.IdentityReference). + +"@ Fix = @" +First, eliminate unused Enrollment Agent templates. +Then, tightly scope any Enrollment Agent templates that remain and: +# Enable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 0} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 2} "@ Revert = @" +# Disable Manager Approval `$Object = `'$($_.DistinguishedName)`' -Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Certificate-Name-Flag' = 1} +Get-ADObject `$Object | Set-ADObject -Replace @{'msPKI-Enrollment-Flag' = 0} "@ Technique = 'ESC3' } diff --git a/Private/Find-ESC4.ps1 b/Private/Find-ESC4.ps1 index 1f103be..0c5ce72 100644 --- a/Private/Find-ESC4.ps1 +++ b/Private/Find-ESC4.ps1 @@ -62,15 +62,15 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $DangerousRights, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeOwners, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeUsers, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeObjectTypes, [int]$Mode ) @@ -86,23 +86,27 @@ if ( ($_.objectClass -eq 'pKICertificateTemplate') -and ($SID -notmatch $SafeOwners) ) { $Issue = [pscustomobject]@{ - Forest = $_.CanonicalName.split('/')[0] - Name = $_.Name - DistinguishedName = $_.DistinguishedName - Issue = "$($_.nTSecurityDescriptor.Owner) has Owner rights on this template" - Fix = @" + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + Issue = @" +$($_.nTSecurityDescriptor.Owner) has Owner rights on this template and can +modify it into a template that can create ESC1, ESC2, and ESC3 templates. + +"@ + Fix = @" `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$PreferredOwner`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' `$ACL.SetOwner(`$Owner) Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL "@ - Revert = @" + Revert = @" `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$($_.nTSecurityDescriptor.Owner)`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' `$ACL.SetOwner(`$Owner) Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL "@ - Technique = 'ESC4' + Technique = 'ESC4' } $Issue } @@ -119,14 +123,16 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL ($entry.AccessControlType -eq 'Allow') -and ($entry.ActiveDirectoryRights -match $DangerousRights) -and ($entry.ObjectType -notmatch $SafeObjectTypes) - ) { + ) { $Issue = [pscustomobject]@{ Forest = $_.CanonicalName.split('/')[0] Name = $_.Name DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) rights on this template" + Issue = "$($entry.IdentityReference) has been granted " + + "$($entry.ActiveDirectoryRights) rights on this template.`n" + + "$($entry.IdentityReference) can likely modify this template into an ESC1 template." Fix = @" `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' foreach ( `$ace in `$ACL.access ) { @@ -140,7 +146,7 @@ Set-Acl -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL Technique = 'ESC4' } - if ( $Mode -in @(1,3,4) ) { + if ( $Mode -in @(1, 3, 4) ) { Update-ESC4Remediation -Issue $Issue } diff --git a/Private/Find-ESC5.ps1 b/Private/Find-ESC5.ps1 index 25f3d31..02ee876 100644 --- a/Private/Find-ESC5.ps1 +++ b/Private/Find-ESC5.ps1 @@ -61,15 +61,15 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $DangerousRights, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeOwners, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeUsers, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $SafeObjectTypes ) $ADCSObjects | ForEach-Object { @@ -83,23 +83,74 @@ } if ( ($_.objectClass -ne 'pKICertificateTemplate') -and ($SID -notmatch $SafeOwners) ) { + switch ($_.objectClass) { + computer { + $IssueDetail = @" +This computer is hosting a Certification Authority (CA). $($_.nTSecurityDescriptor.Owner) +has full control of this object. + +There is little reason for anyone other than AD Admins to have elevated rights +to this CA host. +"@ + } + 'msPKI-Cert-Template-OID' { + $IssueDetail = @" +This Object Identifier (OID) can be modified into an Application Policy and linked +to an empty Universal Group. + +If $($_.nTSecurityDescriptor.Owner) also has control over a certificate template +(see ESC4), an attacker could link this Application Policy to the template. Once +linked, any certificates issued from that template would allow an attacker to +act as a member of the linked group (see ESC13). +"@ + } + pKIEnrollmentService { + $IssueDetail = @" +$($_.nTSecurityDescriptor.Owner) can use these elevated rights to publish currently +unpublished templates. + +If $($_.nTSecurityDescriptor.Owner) also has control over an unpublished certificate +template (see ESC4), they could modify the template into an ESC1 template then +publish the certificate. This published certificate could be use for privilege +escalation and persistence. +"@ + } + } + if ($_.objectClass -eq 'certificationAuthority' -and $_.Name -eq 'NTAuthCertificates') { + $IssueDetail = @" +The NTAuthCertificates object determines which Certification Authorities are +trusted by Active Directory (AD) for client authentication of all forms. + +$($_.nTSecurityDescriptor.Owner) can use their granted rights on NTAuthCertificates +to add their own rogue CAs. Once the rogue CA is trusted, any client authentication +certificates generated by the it can be used by the attacker. +"@ + } + $Issue = [pscustomobject]@{ - Forest = $_.CanonicalName.split('/')[0] - Name = $_.Name - DistinguishedName = $_.DistinguishedName - Issue = "$($_.nTSecurityDescriptor.Owner) has Owner rights on this template" - Fix = @" + Forest = $_.CanonicalName.split('/')[0] + Name = $_.Name + DistinguishedName = $_.DistinguishedName + objectClass = $_.objectClass + Issue = @" +$($_.nTSecurityDescriptor.Owner) has Owner rights on this object. They are able +to modify this object in whatever way they wish. + +$IssueDetail + +"@ + Fix = @" `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$PreferredOwner`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' `$ACL.SetOwner(`$Owner) Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL "@ - Revert = " + Revert = " `$Owner = New-Object System.Security.Principal.SecurityIdentifier(`'$($_.nTSecurityDescriptor.Owner)`') `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' `$ACL.SetOwner(`$Owner) Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL" - Technique = 'ESC5' + Technique = 'ESC5' } $Issue } @@ -111,6 +162,51 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL" } else { $SID = ($Principal.Translate([System.Security.Principal.SecurityIdentifier])).Value } + + switch ($_.objectClass) { + computer { + $IssueDetail = @" +This computer is hosting a Certification Authority (CA). It is likely +$($entry.IdentityReference) can take control of this object. + +There is little reason for anyone other than AD Admins to have elevated rights +to this CA host. +"@ + } + 'msPKI-Cert-Template-OID' { + $IssueDetail = @" +This Object Identifier (OID) can be modified into an Application Policy and linked +to an empty Universal Group. + +If $($entry.IdentityReference) also has control over a certificate template +(see ESC4), an attacker could link this Application Policy to the template. Once +linked, any certificates issued from that template would allow an attacker to +act as a member of the linked group (see ESC13). +"@ + } + pKIEnrollmentService { + $IssueDetail = @" +$($entry.IdentityReference) can use these elevated rights to publish currently +unpublished templates. + +If $($entry.IdentityReference) also has control over an unpublished certificate +template (see ESC4), they could modify the template into an ESC1 template then +publish the certificate. This published certificate could be use for privilege +escalation and persistence. +"@ + } + } + if ($_.objectClass -eq 'certificationAuthority' -and $_.Name -eq 'NTAuthCertificates') { + $IssueDetail = @" +The NTAuthCertificates object determines which Certification Authorities are +trusted by Active Directory (AD) for client authentication of all forms. + +$($entry.IdentityReference) can use their granted rights on NTAuthCertificates +to add their own rogue CAs. Once the rogue CA is trusted, any client authentication +certificates generated by the it can be used by the attacker. +"@ + } + if ( ($_.objectClass -ne 'pKICertificateTemplate') -and ($SID -notmatch $SafeUsers) -and ($entry.AccessControlType -eq 'Allow') -and @@ -122,11 +218,18 @@ Set-ACL -Path `'AD:$($_.DistinguishedName)`' -AclObject `$ACL" DistinguishedName = $_.DistinguishedName IdentityReference = $entry.IdentityReference ActiveDirectoryRights = $entry.ActiveDirectoryRights - Issue = "$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) rights on this object" + Issue = @" +$($entry.IdentityReference) has $($entry.ActiveDirectoryRights) elevated rights +on this $($_.objectClass) object. + +$IssueDetail + +"@ Fix = @" `$ACL = Get-Acl -Path `'AD:$($_.DistinguishedName)`' foreach ( `$ace in `$ACL.access ) { - if ( (`$ace.IdentityReference.Value -like '$($Principal.Value)' ) -and ( `$ace.ActiveDirectoryRights -notmatch '^ExtendedRight$') ) { + if ( (`$ace.IdentityReference.Value -like '$($Principal.Value)' ) -and + ( `$ace.ActiveDirectoryRights -notmatch '^ExtendedRight$') ) { `$ACL.RemoveAccessRule(`$ace) | Out-Null } } diff --git a/Private/Find-ESC6.ps1 b/Private/Find-ESC6.ps1 index 0969f38..f8289d1 100644 --- a/Private/Find-ESC6.ps1 +++ b/Private/Find-ESC6.ps1 @@ -22,7 +22,7 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) process { @@ -36,21 +36,38 @@ Name = $_.Name DistinguishedName = $_.DistinguishedName Technique = 'ESC6' - Issue = $_.AuditFilter + Issue = $_.SANFlag Fix = 'N/A' Revert = 'N/A' } if ($_.SANFlag -eq 'Yes') { - $Issue.Issue = 'EDITF_ATTRIBUTESUBJECTALTNAME2 is enabled.' - $Issue.Fix = @" + $Issue.Issue = @" +The dangerous EDITF_ATTRIBUTESUBJECTALTNAME2 flag is enabled on $CAFullname. +All templates published on this CA will accept a Subject Alternative Name (SAN) +during enrollment even if the template is not specifically configured to allow a SAN. + +As of May 2022, Microsoft has neutered this situation by requiring all SANs to +be strongly mapped to certificates. + +However, if strong mapping has been explicitly disabled on Domain Controllers, +this configuration remains vulnerable to privilege escalation attacks. + +"@ + $Issue.Fix = @" +# Disable the flag certutil -config $CAFullname -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2 -Invoke-Command -ComputerName `"$($_.dNSHostName)`" -ScriptBlock { + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { Get-Service -Name `'certsvc`' | Restart-Service -Force } "@ $Issue.Revert = @" +# Enable the flag certutil -config $CAFullname -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2 -Invoke-Command -ComputerName `"$($_.dNSHostName)`" -ScriptBlock { + +# Restart the Ceritification Authority service +Invoke-Command -ComputerName `'$($_.dNSHostName)`' -ScriptBlock { Get-Service -Name `'certsvc`' | Restart-Service -Force } "@ diff --git a/Private/Find-ESC8.ps1 b/Private/Find-ESC8.ps1 index 6d8137e..48dca56 100644 --- a/Private/Find-ESC8.ps1 +++ b/Private/Find-ESC8.ps1 @@ -29,7 +29,7 @@ [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) @@ -44,7 +44,16 @@ DistinguishedName = $_.DistinguishedName CAEnrollmentEndpoint = $endpoint.URL AuthType = $endpoint.Auth - Issue = 'An HTTP enrollment endpoint is available.' + Issue = @' +An HTTP enrollment endpoint is available. It is possible to relay NTLM +authentication to this HTTP endpoint. + +If the LAN Manager authentication level of any domain in this forest is 2 or +less, an attacker can coerce authentication from a Domain Controller (DC) and +relay it to this HTTP enrollment enpoint to receive a certificate which can be +used to authenticate as that DC. + +'@ Fix = @' Disable HTTP access and enforce HTTPS. Enable EPA. @@ -54,8 +63,19 @@ Disable NTLM authentication (if possible.) Technique = 'ESC8' } if ($endpoint.URL -match '^https:') { - $Issue.Issue = 'An HTTPS enrollment endpoint is available.' - $Issue.Fix = @' + $Issue.Issue = @' +An HTTPS enrollment endpoint is available. It may be possible to relay NTLM +authentication to this HTTPS endpoint. Enabling IIS Extended Protection for +Authentication or disabling NTLM authentication completely, NTLM relay is not +possible. + +If those protection are not in place, and the LAN Manager authentication level +of any domain in this forest is 2 or less, an attacker can coerce authentication +from a Domain Controller (DC) and relay it to this HTTPS enrollment enpoint to +receive a certificate which can be used to authenticate as that DC. + +'@ + $Issue.Fix = @' Ensure EPA is enabled. Disable NTLM authentication (if possible.) '@ diff --git a/Private/Find-ESC9.ps1 b/Private/Find-ESC9.ps1 index 0208f0e..ee4f992 100644 --- a/Private/Find-ESC9.ps1 +++ b/Private/Find-ESC9.ps1 @@ -50,7 +50,7 @@ function Find-ESC9 { [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] $ADCSObjects ) diff --git a/Private/Format-Result.ps1 b/Private/Format-Result.ps1 index 7104e27..f8554db 100644 --- a/Private/Format-Result.ps1 +++ b/Private/Format-Result.ps1 @@ -26,7 +26,7 @@ function Format-Result { [CmdletBinding()] param( $Issue, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [int]$Mode ) @@ -39,17 +39,27 @@ function Format-Result { ESC5 = 'ESC5 - Vulnerable Access Control - PKI Object' ESC6 = 'ESC6 - EDITF_ATTRIBUTESUBJECTALTNAME2 Flag Enabled' ESC8 = 'ESC8 - HTTP/S Enrollment Enabled' + ESC11 = 'ESC11 - IF_ENFORCEENCRYPTICERTREQUEST Flag Disabled' + ESC13 = 'ESC13 - Vulnerable Certificate Temple - Group-Linked' } if ($null -ne $Issue) { $UniqueIssue = $Issue.Technique | Sort-Object -Unique - Write-Host "`n########## $($IssueTable[$UniqueIssue]) ##########`n" + $Title = $($IssueTable[$UniqueIssue]) + Write-Host "$('-'*($($Title.ToString().Length + 10)))" -ForeGroundColor Black -BackgroundColor Magenta -NoNewline; Write-Host + Write-Host " " -BackgroundColor Magenta -NoNewline + Write-Host $Title -BackgroundColor Magenta -ForeGroundColor Black -NoNewline + Write-Host " " -BackgroundColor Magenta -NoNewline; Write-Host + Write-Host "$('-'*($($Title.ToString().Length + 10)))" -ForeGroundColor Black -BackgroundColor Magenta -NoNewline; Write-Host + switch ($Mode) { 0 { $Issue | Format-Table Technique, Name, Issue -Wrap } 1 { - if ($Issue.Technique -eq 'ESC8') { + if ($Issue.Technique -eq 'ESC5') { + $Issue | Format-List Technique, Name, objectClass, DistinguishedName, Issue, Fix + } elseif ($Issue.Technique -eq 'ESC8') { $Issue | Format-List Technique, Name, DistinguishedName, CAEnrollmentEndpoint, AuthType, Issue, Fix } else { $Issue | Format-List Technique, Name, DistinguishedName, Issue, Fix diff --git a/Private/Invoke-Remediation.ps1 b/Private/Invoke-Remediation.ps1 index b098ed9..b26ab02 100644 --- a/Private/Invoke-Remediation.ps1 +++ b/Private/Invoke-Remediation.ps1 @@ -27,6 +27,12 @@ function Invoke-Remediation { .PARAMETER ESC6 A PS Object containing all necessary information about ESC6 issues. + .PARAMETER ESC11 + A PS Object containing all necessary information about ESC11 issues. + + .PARAMETER ESC13 + A PS Object containing all necessary information about ESC13 issues. + .INPUTS PS Objects @@ -42,7 +48,9 @@ function Invoke-Remediation { $ESC3, $ESC4, $ESC5, - $ESC6 + $ESC6, + $ESC11, + $ESC13 ) Write-Host "`nExecuting Mode 4 - Attempting to fix identified issues!`n" -ForegroundColor Green @@ -50,7 +58,18 @@ function Invoke-Remediation { Write-Host 'Invoke-RevertLocksmith.ps1' -ForegroundColor White -NoNewline Write-Host ") which can be used to revert all changes made by Locksmith...`n" try { - Export-RevertScript -AuditingIssues $AuditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Export-RevertScript @params } catch { Write-Warning 'Creation of Invoke-RevertLocksmith.ps1 failed.' Write-Host "Continue with this operation? [Y] Yes " -NoNewline @@ -68,7 +87,7 @@ function Invoke-Remediation { $AuditingIssues | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Auditing is not fully enabled on Certification Authority `"$($_.Name)`".`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -99,7 +118,7 @@ function Invoke-Remediation { $ESC1 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Security Principals can enroll in `"$($_.Name)`" template using a Subject Alternative Name without Manager Approval.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -131,7 +150,7 @@ function Invoke-Remediation { $ESC2 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "Security Principals can enroll in `"$($_.Name)`" template and create a Subordinate Certification Authority without Manager Approval.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -190,7 +209,7 @@ function Invoke-Remediation { } } if ($ESC5) { - $ESC5 | Where-Object Issue -like "* Owner rights *" | ForEach-Object { # This selector sucks - Jake + $ESC5 | Where-Object Issue -like "* Owner rights *" | ForEach-Object { # TODO This selector sucks - Jake $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White Write-Host "$($_.Issue)`n" @@ -224,7 +243,7 @@ function Invoke-Remediation { $ESC6 | ForEach-Object { $FixBlock = [scriptblock]::Create($_.Fix) Write-Host 'ISSUE:' -ForegroundColor White - Write-Host "The Certification Authority `"$($_.Name)`" has the dangerous EDITF_ATTRIBUTESUBJECTALTNAME2 flag enabled.`n" + Write-Host "$($_.Issue)`n" Write-Host 'TECHNIQUE:' -ForegroundColor White Write-Host "$($_.Technique)`n" Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White @@ -253,6 +272,71 @@ function Invoke-Remediation { } } + if ($ESC11) { + $ESC11 | ForEach-Object { + $FixBlock = [scriptblock]::Create($_.Fix) + Write-Host 'ISSUE:' -ForegroundColor White + Write-Host "$($_.Issue)`n" + Write-Host 'TECHNIQUE:' -ForegroundColor White + Write-Host "$($_.Technique)`n" + Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White + Write-Host "Locksmith will attempt to enable the IF_ENFORCEENCRYPTICERTREQUEST flag on Certifiction Authority `"$($_.Name)`".`n" + Write-Host 'COMMAND(S) TO BE RUN' -ForegroundColor White + Write-Host 'PS> ' -NoNewline + Write-Host "$($_.Fix)`n" -ForegroundColor Cyan + $WarningError = 'n' + Write-Host 'OPERATIONAL IMPACT:' -ForegroundColor White + Write-Host "WARNING: This change could cause some services to stop working.`n" -ForegroundColor Yellow + Write-Host "If you continue, Locksmith will attempt to fix this issue.`n" -ForegroundColor Yellow + Write-Host "Continue with this operation? [Y] Yes " -NoNewline + Write-Host "[N] " -ForegroundColor Yellow -NoNewline + Write-Host "No: " -NoNewLine + $WarningError = '' + $WarningError = Read-Host + if ($WarningError -like 'y') { + try { + Invoke-Command -ScriptBlock $FixBlock + } catch { + Write-Error 'Could not enable the IF_ENFORCEENCRYPTICERTREQUEST flag. Are you an Active Directory or AD CS admin?' + } + } else { + Write-Host "SKIPPED!`n" -ForegroundColor Yellow + } + } + } + + if ($ESC13) { + $ESC13 | ForEach-Object { + $FixBlock = [scriptblock]::Create($_.Fix) + Write-Host 'ISSUE:' -ForegroundColor White + Write-Host "$($_.Issue)`n" + Write-Host 'TECHNIQUE:' -ForegroundColor White + Write-Host "$($_.Technique)`n" + Write-Host 'ACTION TO BE PERFORMED:' -ForegroundColor White + Write-Host "Locksmith will attempt to enable Manager Approval on the `"$($_.Name)`" template.`n" + Write-Host 'CCOMMAND(S) TO BE RUN:' + Write-Host 'PS> ' -NoNewline + Write-Host "$($_.Fix)`n" -ForegroundColor Cyan + Write-Host 'OPERATIONAL IMPACT:' -ForegroundColor White + Write-Host "WARNING: This change could cause some services to stop working until certificates are approved.`n" -ForegroundColor Yellow + Write-Host "If you continue, Locksmith will attempt to fix this issue.`n" -ForegroundColor Yellow + Write-Host "Continue with this operation? [Y] Yes " -NoNewline + Write-Host "[N] " -ForegroundColor Yellow -NoNewline + Write-Host "No: " -NoNewLine + $WarningError = '' + $WarningError = Read-Host + if ($WarningError -like 'y') { + try { + Invoke-Command -ScriptBlock $FixBlock + } catch { + Write-Error 'Could not enable Manager Approval. Are you an Active Directory or AD CS admin?' + } + } else { + Write-Host "SKIPPED!`n" -ForegroundColor Yellow + } + } + } + Write-Host "Mode 4 Complete! There are no more issues that Locksmith can automatically resolve.`n" -ForegroundColor Green Write-Host 'If you experience any operational impact from using Locksmith Mode 4, use ' -NoNewline Write-Host 'Invoke-RevertLocksmith.ps1 ' -ForegroundColor White diff --git a/Private/Invoke-Scans.ps1 b/Private/Invoke-Scans.ps1 index 75ed69e..640dfd9 100644 --- a/Private/Invoke-Scans.ps1 +++ b/Private/Invoke-Scans.ps1 @@ -3,55 +3,42 @@ function Invoke-Scans { .SYNOPSIS Invoke-Scans.ps1 is a script that performs various scans on ADCS (Active Directory Certificate Services) objects. - .DESCRIPTION - This script accepts a parameter named $Scans, which specifies the type of scans to perform. The available scan options are: - - Auditing - - ESC1 - - ESC2 - - ESC3 - - ESC4 - - ESC5 - - ESC6 - - ESC8 - - All - - PromptMe - .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', 'All', 'PromptMe'. + The available scan options are: 'Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'All', 'PromptMe'. .NOTES - The script requires the following functions to be defined: Find-AuditingIssue, Find-ESC1, Find-ESC2, Find-ESC3Condition1, - Find-ESC3Condition2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8. + Find-ESC3Condition2, Find-ESC4, Find-ESC5, Find-ESC6, Find-ESC8, Find-ESC11, Find-ESC13 - 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. .EXAMPLE - # Perform all scans - Invoke-Scans + Invoke-Scans + # Perform all scans .EXAMPLE - # Perform only the 'Auditing' and 'ESC1' scans - Invoke-Scans -Scans 'Auditing', 'ESC1' + Invoke-Scans -Scans 'Auditing', 'ESC1' + # Perform only the 'Auditing' and 'ESC1' scans .EXAMPLE - # Prompt the user to select the scans to perform - Invoke-Scans -Scans 'PromptMe' + Invoke-Scans -Scans 'PromptMe' + # Prompt the user to select the scans to perform #> [CmdletBinding()] [OutputType([hashtable])] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Invoke-Scans', Justification='Performing multiple scans.')] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', 'Invoke-Scans', Justification = 'Performing multiple scans.')] param ( - # Could split Scans and PromptMe into separate parameter sets. - [Parameter()] + # Could split Scans and PromptMe into separate parameter sets. + [Parameter()] $ClientAuthEkus, $DangerousRights, $EnrollmentAgentEKU, [int]$Mode, $SafeObjectTypes, $SafeOwners, - [ValidateSet('Auditing','ESC1','ESC2','ESC3','ESC4','ESC5','ESC6','ESC8','All','PromptMe')] + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'ESC11', 'ESC13', 'All', 'PromptMe')] [array]$Scans = 'All', $UnsafeOwners, $UnsafeUsers, @@ -68,12 +55,10 @@ function Invoke-Scans { # Check for Out-GridView or Out-ConsoleGridView if ((Get-Command Out-ConsoleGridView -ErrorAction SilentlyContinue) -and ($PSVersionTable.PSVersion.Major -ge 7)) { - [array]$Scans = ($Dictionary | Select-Object Name,Category,Subcategory | Out-ConsoleGridView -OutputMode Multiple -Title $GridViewTitle).Name | Sort-Object -Property Name - } - elseif (Get-Command -Name Out-GridView -ErrorAction SilentlyContinue) { - [array]$Scans = ($Dictionary | Select-Object Name,Category,Subcategory | Out-GridView -PassThru -Title $GridViewTitle).Name | Sort-Object -Property Name - } - else { + [array]$Scans = ($Dictionary | Select-Object Name, Category, Subcategory | Out-ConsoleGridView -OutputMode Multiple -Title $GridViewTitle).Name | Sort-Object -Property Name + } elseif (Get-Command -Name Out-GridView -ErrorAction SilentlyContinue) { + [array]$Scans = ($Dictionary | Select-Object Name, Category, Subcategory | Out-GridView -PassThru -Title $GridViewTitle).Name | Sort-Object -Property Name + } else { # To Do: Check for admin and prompt to install features/modules or revert to 'All'. Write-Information "Out-GridView and Out-ConsoleGridView were not found on your system. Defaulting to `'All`'." $Scans = 'All' @@ -99,60 +84,71 @@ function Invoke-Scans { [array]$ESC3 += Find-ESC3Condition2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers } ESC4 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC4)...' + Write-Host 'Identifying AD CS templates with poor access control (ESC4)...' [array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes } ESC5 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC5)...' + Write-Host 'Identifying AD CS objects with poor access control (ESC5)...' [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes } ESC6 { - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC6)...' + Write-Host 'Identifying Issuing CAs with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects } ESC8 { Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects } + ESC11 { + Write-Host 'Identifying Issuing CAs with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' + [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects + } All { Write-Host 'Identifying auditing issues...' [array]$AuditingIssues = Find-AuditingIssue -ADCSObjects $ADCSObjects Write-Host 'Identifying AD CS templates with dangerous ESC1 configurations...' - [array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers + [array]$ESC1 = Find-ESC1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus Write-Host 'Identifying AD CS templates with dangerous ESC2 configurations...' [array]$ESC2 = Find-ESC2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers Write-Host 'Identifying AD CS templates with dangerous ESC3 configurations...' [array]$ESC3 = Find-ESC3Condition1 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers [array]$ESC3 += Find-ESC3Condition2 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC4)...' + Write-Host 'Identifying AD CS templates with poor access control (ESC4)...' [array]$ESC4 = Find-ESC4 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes -Mode $Mode - Write-Host 'Identifying AD CS template and other objects with poor access control (ESC5)...' + Write-Host 'Identifying AD CS objects with poor access control (ESC5)...' [array]$ESC5 = Find-ESC5 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -DangerousRights $DangerousRights -SafeOwners $SafeOwners -SafeObjectTypes $SafeObjectTypes - Write-Host 'Identifying Certificate Authorities configured with dangerous flags (ESC6)...' + Write-Host 'Identifying Certificate Authorities with EDITF_ATTRIBUTESUBJECTALTNAME2 enabled v (ESC6)...' [array]$ESC6 = Find-ESC6 -ADCSObjects $ADCSObjects Write-Host 'Identifying HTTP-based certificate enrollment interfaces (ESC8)...' [array]$ESC8 = Find-ESC8 -ADCSObjects $ADCSObjects + Write-Host 'Identifying Certificate Authorities with IF_ENFORCEENCRYPTICERTREQUEST disabled (ESC11)...' + [array]$ESC11 = Find-ESC11 -ADCSObjects $ADCSObjects + Write-Host 'Identifying AD CS templates with dangerous ESC13 configurations...' + [array]$ESC13 = Find-ESC13 -ADCSObjects $ADCSObjects -SafeUsers $SafeUsers -ClientAuthEKUs $ClientAuthEkus + Write-Host } } - [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + [array]$AllIssues = $AuditingIssues + $ESC1 + $ESC2 + $ESC3 + $ESC4 + $ESC5 + $ESC6 + $ESC8 + $ESC11 + $ESC13 # If these are all empty = no issues found, exit - if ((!$AuditingIssues) -and (!$ESC1) -and (!$ESC2) -and (!$ESC3) -and (!$ESC4) -and (!$ESC5) -and (!$ESC6) -and (!$ESC8) ) { + if ((!$AuditingIssues) -and (!$ESC1) -and (!$ESC2) -and (!$ESC3) -and (!$ESC4) -and (!$ESC5) -and (!$ESC6) -and (!$ESC8) -and ($ESC11) -and ($ESC13) ) { Write-Host "`n$(Get-Date) : No ADCS issues were found." -ForegroundColor Green break } # Return a hash table of array names (keys) and arrays (values) so they can be directly referenced with other functions Return @{ - AllIssues = $AllIssues + AllIssues = $AllIssues AuditingIssues = $AuditingIssues - ESC1 = $ESC1 - ESC2 = $ESC2 - ESC3 = $ESC3 - ESC4 = $ESC4 - ESC5 = $ESC5 - ESC6 = $ESC6 - ESC8 = $ESC8 + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC8 = $ESC8 + ESC11 = $ESC11 + ESC13 = $ESC13 } } diff --git a/Private/New-Dictionary.ps1 b/Private/New-Dictionary.ps1 index 9177062..3227798 100644 --- a/Private/New-Dictionary.ps1 +++ b/Private/New-Dictionary.ps1 @@ -21,7 +21,7 @@ descriptions, code used to find, code used to fix, and reference URLs. This is i function New-Dictionary { class VulnerableConfigurationItem { - static [string] $Version = '2023.10.01.000' + static [string] $Version = '2024.11.03.000' [string]$Name [ValidateSet('Escalation Path','Server Configuration','GPO Setting')][string]$Category [string]$Subcategory @@ -125,15 +125,24 @@ function New-Dictionary { # FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} # ReferenceUrls = '' # }, - # [VulnerableConfigurationItem]@{ - # Name = 'ESC11' - # Category = 'Escalation Path' - # Subcategory = '' - # Summary = '' - # FindIt = {Find-ESC11} - # FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} - # ReferenceUrls = '' - # }, + [VulnerableConfigurationItem]@{ + Name = 'ESC11' + Category = 'Escalation Path' + Subcategory = 'IF_ENFORCEENCRYPTICERTREQUEST' + Summary = '' + FindIt = {Find-ESC11} + FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} + ReferenceUrls = 'https://blog.compass-security.com/2022/11/relaying-to-ad-certificate-services-over-rpc/' + }, + [VulnerableConfigurationItem]@{ + Name = 'ESC13' + Category = 'Escalation Path' + Subcategory = 'Certificate Template linked to Group' + Summary = '' + FindIt = {Find-ESC13} + FixIt = {Write-Output 'Add code to fix the vulnerable configuration.'} + ReferenceUrls = 'https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53' + }, [VulnerableConfigurationItem]@{ Name = 'Auditing' Category = 'Server Configuration' diff --git a/Private/Set-AdditionalCAProperty.ps1 b/Private/Set-AdditionalCAProperty.ps1 index a397a47..a085ece 100644 --- a/Private/Set-AdditionalCAProperty.ps1 +++ b/Private/Set-AdditionalCAProperty.ps1 @@ -37,17 +37,34 @@ begin { $CAEnrollmentEndpoint = @() - $code= @" -using System.Net; -using System.Security.Cryptography.X509Certificates; -public class TrustAllCertsPolicy : ICertificatePolicy { - public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { - return true; - } -} + if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy') ) { + if ($PSVersionTable.PSEdition -eq 'Desktop') { + $code= @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { + return true; + } + } "@ - Add-Type -TypeDefinition $code -Language CSharp - [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + Add-Type -TypeDefinition $code -Language CSharp + [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy + } else { + Add-Type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + using System.Net.Security; + public class TrustAllCertsPolicy { + public static bool TrustAllCerts(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { + return true; + } + } +"@ + # Set the ServerCertificateValidationCallback + [System.Net.ServicePointManager]::ServerCertificateValidationCallback = [TrustAllCertsPolicy]::TrustAllCerts + } + } } process { @@ -72,7 +89,7 @@ public class TrustAllCertsPolicy : ICertificatePolicy { try { $FullURL = "https$URL" $Request = [System.Net.WebRequest]::Create($FullURL) - + $Request.GetResponse() | Out-Null $CAEnrollmentEndpoint += @{ 'URL' = $FullURL @@ -106,7 +123,7 @@ public class TrustAllCertsPolicy : ICertificatePolicy { $CAHostFQDN = (Get-ADObject -Filter { (Name -eq $CAHostName) -and (objectclass -eq 'computer') } -Properties DnsHostname -Server $ForestGC).DnsHostname } $ping = Test-Connection -ComputerName $CAHostFQDN -Quiet -Count 1 - if ($ping) { + if ($ping) { try { if ($Credential) { $CertutilAudit = Invoke-Command -ComputerName $CAHostname -Credential $Credential -ScriptBlock { param($CAFullName); certutil -config $CAFullName -getreg CA\AuditFilter } -ArgumentList $CAFullName @@ -123,11 +140,21 @@ public class TrustAllCertsPolicy : ICertificatePolicy { $CertutilFlag = certutil -config $CAFullName -getreg policy\EditFlags } } catch { - $AuditFilter = 'Failure' + $SANFlag = 'Failure' + } + try { + if ($Credential) { + $CertutilInterfaceFlag = Invoke-Command -ComputerName $CAHostname -Credential $Credential -ScriptBlock { param($CAFullName); certutil -config $CAFullName -getreg policy\EditFlags } -ArgumentList $CAFullName + } else { + $CertutilInterfaceFlag = certutil -config $CAFullName -getreg CA\InterfaceFlags + } + } catch { + $InterfaceFlag = 'Failure' } } else { $AuditFilter = 'CA Unavailable' $SANFlag = 'CA Unavailable' + $InterfaceFlag = 'CA Unavailable' } if ($CertutilAudit) { try { @@ -150,12 +177,21 @@ public class TrustAllCertsPolicy : ICertificatePolicy { $SANFlag = 'No' } } + if ($CertutilInterfaceFlag) { + [string]$InterfaceFlag = $CertutilInterfaceFlag | Select-String ' IF_ENFORCEENCRYPTICERTREQUEST -- 200 \(' + if ($InterfaceFlag) { + $InterfaceFlag = 'Yes' + } else { + $InterfaceFlag = '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 Add-Member -InputObject $_ -MemberType NoteProperty -Name CAHostname -Value $CAHostname -Force 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 } } } diff --git a/Private/Set-Severity.ps1 b/Private/Set-Severity.ps1 index 76e1362..d6909e9 100644 --- a/Private/Set-Severity.ps1 +++ b/Private/Set-Severity.ps1 @@ -2,7 +2,7 @@ function Set-Severity { [OutputType([string])] [CmdletBinding()] param( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [array]$Issue ) foreach ($Finding in $Issue) { diff --git a/Private/Show-LocksmithLogo.ps1 b/Private/Show-LocksmithLogo.ps1 new file mode 100644 index 0000000..ae93dbc --- /dev/null +++ b/Private/Show-LocksmithLogo.ps1 @@ -0,0 +1,37 @@ +function Show-LocksmithLogo { + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%#+==============#%%%%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%%%#=====================#%%%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%%#=========================#%%%%%%%%%%%%' + Write-Host '%%%%%%%%%%%=============================%%%%%%%%%%%' + Write-Host '%%%%%%%%%#==============+++==============#%%%%%%%%%' + Write-Host '%%%%%%%%#===========#%%%%%%%%%#===========#%%%%%%%%' + Write-Host '%%%%%%%%==========%%%%%%%%%%%%%%%==========%%%%%%%%' + Write-Host '%%%%%%%*=========%%%%%%%%%%%%%%%%%=========*%%%%%%%' + Write-Host '%%%%%%%+========*%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%+========#%%%%%%%%%%%%%%%%%#=========%%%%%%%' + Write-Host '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' + Write-Host '#=================================================#' + Write-Host '#=================================================#' + Write-Host '#=================+%%%============================#' + Write-Host '#==================%%%%*==========================#' + Write-Host '#===================*%%%%+========================#' + Write-Host '#=====================#%%%%=======================#' + Write-Host '#======================+%%%%#=====================#' + Write-Host '#========================*%%%%*===================#' + Write-Host '#========================+%%%%%===================#' + Write-Host '#======================#%%%%%+====================#' + Write-Host '#===================+%%%%%%=======================#' + Write-Host '#=================#%%%%%+=========================#' + Write-Host '#==============+%%%%%#============================#' + Write-Host '#============*%%%%%+====+%%%%%%%%%%===============#' + Write-Host '#=============%%*========+********+===============#' + Write-Host '#=================================================#' + Write-Host '#=================================================#' + Write-Host '#=================================================#' +} diff --git a/Public/Invoke-Locksmith.ps1 b/Public/Invoke-Locksmith.ps1 index 3882deb..dfc4775 100644 --- a/Public/Invoke-Locksmith.ps1 +++ b/Public/Invoke-Locksmith.ps1 @@ -4,7 +4,7 @@ Finds the most common malconfigurations of Active Directory Certificate Services (AD CS). .DESCRIPTION - Locksmith uses the Active Directory (AD) Powershell (PS) module to identify 7 misconfigurations + Locksmith uses the Active Directory (AD) Powershell (PS) module to identify 10 misconfigurations commonly found in Enterprise mode AD CS installations. .COMPONENT @@ -71,18 +71,18 @@ # The mode to run Locksmith in. Defaults to 0. [Parameter()] - [ValidateSet(0,1,2,3,4)] - [int]$Mode = 0, + [ValidateSet(0, 1, 2, 3, 4)] + [int]$Mode = 0, # The scans to run. Defaults to 'All'. [Parameter()] - [ValidateSet('Auditing','ESC1','ESC2','ESC3','ESC4','ESC5','ESC6','ESC8','All','PromptMe')] - [array]$Scans = 'All', - + [ValidateSet('Auditing', 'ESC1', 'ESC2', 'ESC3', 'ESC4', 'ESC5', 'ESC6', 'ESC8', 'All', 'PromptMe')] + [array]$Scans = 'All', + # The directory to save the output in (defaults to the current working directory). [Parameter()] - [ValidateScript({Test-Path -Path $_ -PathType Container})] - [string]$OutputPath = $PWD, + [ValidateScript({ Test-Path -Path $_ -PathType Container })] + [string]$OutputPath = $PWD, # The credential to use for working with ADCS. [Parameter()] @@ -125,7 +125,7 @@ # For output filenames [string]$FilePrefix = "Locksmith $(Get-Date -format 'yyyy-MM-dd hh-mm-ss')" - # Extended Key Usages for client authentication. A requirement for ESC1 + # Extended Key Usages for client authentication. A requirement for ESC1, ESC3 Condition 2, and ESC13 $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' # GenericAll, WriteDacl, and WriteOwner all permit full control of an AD object. @@ -193,7 +193,7 @@ -517 = Cert Publishers -512 = Domain Admins group #> - $SafeGroupRIDs = @('-517','-512') + $SafeGroupRIDs = @('-517', '-512') # Administrators group $SafeGroupSIDs = @('S-1-5-32-544') @@ -207,7 +207,7 @@ $SafeUsers += '|' + $user } } - $SafeUsers = $SafeUsers.Replace('||','|') + $SafeUsers = $SafeUsers.Replace('||', '|') if ($Credential) { $Targets = Get-Target -Credential $Credential @@ -232,31 +232,32 @@ $CAHosts | ForEach-Object { $SafeUsers += '|' + $_.objectSid } #if ( $Scans ) { - # If the Scans parameter was used, Invoke-Scans with the specified checks. - $ScansParameters = @{ - ClientAuthEkus = $ClientAuthEKUs - DangerousRights = $DangerousRights - EnrollmentAgentEKU = $EnrollmentAgentEKU - Mode = $Mode - SafeObjectTypes = $SafeObjectTypes - SafeOwners = $SafeOwners - Scans = $Scans - UnsafeOwners = $UnsafeOwners - UnsafeUsers = $UnsafeUsers - PreferredOwner = $PreferredOwner - } - $Results = Invoke-Scans @ScansParameters - # Re-hydrate the findings arrays from the Results hash table - $AllIssues = $Results['AllIssues'] - $AuditingIssues = $Results['AuditingIssues'] - $ESC1 = $Results['ESC1'] - $ESC2 = $Results['ESC2'] - $ESC3 = $Results['ESC3'] - $ESC4 = $Results['ESC4'] - $ESC5 = $Results['ESC5'] - $ESC6 = $Results['ESC6'] - $ESC8 = $Results['ESC8'] - #} + # If the Scans parameter was used, Invoke-Scans with the specified checks. + $ScansParameters = @{ + ClientAuthEkus = $ClientAuthEKUs + DangerousRights = $DangerousRights + EnrollmentAgentEKU = $EnrollmentAgentEKU + Mode = $Mode + SafeObjectTypes = $SafeObjectTypes + SafeOwners = $SafeOwners + Scans = $Scans + UnsafeOwners = $UnsafeOwners + UnsafeUsers = $UnsafeUsers + PreferredOwner = $PreferredOwner + } + $Results = Invoke-Scans @ScansParameters + # Re-hydrate the findings arrays from the Results hash table + $AllIssues = $Results['AllIssues'] + $AuditingIssues = $Results['AuditingIssues'] + $ESC1 = $Results['ESC1'] + $ESC2 = $Results['ESC2'] + $ESC3 = $Results['ESC3'] + $ESC4 = $Results['ESC4'] + $ESC5 = $Results['ESC5'] + $ESC6 = $Results['ESC6'] + $ESC8 = $Results['ESC8'] + $ESC11 = $Results['ESC11'] + $ESC13 = $Results['ESC13'] # If these are all empty = no issues found, exit if ($null -eq $Results) { @@ -276,6 +277,8 @@ Format-Result $ESC5 '0' Format-Result $ESC6 '0' Format-Result $ESC8 '0' + Format-Result $ESC11 '0' + Format-Result $ESC13 '0' } 1 { Format-Result $AuditingIssues '1' @@ -286,6 +289,8 @@ Format-Result $ESC5 '1' Format-Result $ESC6 '1' Format-Result $ESC8 '1' + Format-Result $ESC11 '1' + Format-Result $ESC13 '1' } 2 { $Output = Join-Path -Path $OutputPath -ChildPath "$FilePrefix ADCSIssues.CSV" @@ -308,9 +313,20 @@ } } 4 { - Invoke-Remediation -AuditingIssues $AuditingIssues -ESC1 $ESC1 -ESC2 $ESC2 -ESC3 $ESC3 -ESC4 $ESC4 -ESC5 $ESC5 -ESC6 $ESC6 + $params = @{ + AuditingIssues = $AuditingIssues + ESC1 = $ESC1 + ESC2 = $ESC2 + ESC3 = $ESC3 + ESC4 = $ESC4 + ESC5 = $ESC5 + ESC6 = $ESC6 + ESC11 = $ESC11 + ESC13 = $ESC13 + } + Invoke-Remediation @params } } Write-Host 'Thank you for using ' -NoNewline - Write-Host "❤ Locksmith ❤`n" -ForegroundColor Magenta + Write-Host "Locksmith ❤`n" -ForegroundColor Magenta } diff --git a/Tests/Invoke-TSS.ps1 b/Tests/Invoke-TSS.ps1 index 0c94fce..4f7fad8 100644 --- a/Tests/Invoke-TSS.ps1 +++ b/Tests/Invoke-TSS.ps1 @@ -17,51 +17,51 @@ #requires -Modules ActiveDirectory Write-Output @" - _______ _ _ _ + _______ _ _ _ |__ __| | | (_) | | | | __ _ ___| |_ _ ___ __ _| | | |/ _`` |/ __| __| |/ __/ _`` | | | | (_| | (__| |_| | (_| (_| | | _|_|\__,_|\___|\__|_|\___\__,_|_| - / ____| | | -| (___ _ __ ___ ___ __| | - \___ \| `'_ \ / _ \/ _ \/ _`` | - ____) | |_) | __/ __/ (_| | -|_____/| .__/ \___|\___|\__,_| - / ____| | -| (___ |_|_ _ _ _ __ _ _ __ ___ + / ____| | | +| (___ _ __ ___ ___ __| | + \___ \| `'_ \ / _ \/ _ \/ _`` | + ____) | |_) | __/ __/ (_| | +|_____/| .__/ \___|\___|\__,_| + / ____| | +| (___ |_|_ _ _ _ __ _ _ __ ___ \___ \ / _`` | | | |/ _`` | `'__/ _ \ ____) | (_| | |_| | (_| | | | __/ |_____/ \__, |\__,_|\__,_|_| \___| - | | - |_| - The UnLocksmith + | | + |_| + The UnLocksmith "@ $NewTemplates = @( 'ESC1and2AutoEnroll' 'ESC1and2Enroll' 'ESC1and2FilteredAutoEnroll' - 'ESC1and2FilteredEnroll' + 'ESC1and2FilteredEnroll' 'ESC1AutoEnroll' 'ESC1Enroll' - 'ESC1FilteredAutoEnroll' - 'ESC1FilteredEnroll' - 'ESC2AutoEnroll' + 'ESC1FilteredAutoEnroll' + 'ESC1FilteredEnroll' + 'ESC2AutoEnroll' 'ESC2Enroll' - 'ESC2FilteredAutoEnroll' + 'ESC2FilteredAutoEnroll' 'ESC2FilteredEnroll' 'ESC3Condition1' 'ESC3Condition2Schema1' 'ESC3Condition2Schema2' - 'ESC4FilteredAutoEnroll' - 'ESC4FilteredEnroll' + 'ESC4FilteredAutoEnroll' + 'ESC4FilteredEnroll' 'ESC4FilteredOwner' 'ESC4FilteredSafeUsers' 'ESC4GenericAll' 'ESC4UnsafeOwner' 'ESC4WriteProperty' - 'ESC4WriteOwner' + 'ESC4WriteOwner' ) $NewObjects = @( @@ -104,166 +104,166 @@ $NewObjects | ForEach-Object { $ESC1and2AutoEnroll = Get-ADObject "CN=ESC1and2AutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1and2AutoEnrollProperties = @{ - 'DisplayName' = 'ESC1and2AutoEnroll' + 'DisplayName' = 'ESC1and2AutoEnroll' 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '2.5.29.37.0' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '2.5.29.37.0' +} Set-ADObject $ESC1and2AutoEnroll.DistinguishedName -Add $ESC1and2AutoEnrollProperties $ACL = Get-Acl "AD:$ESC1and2AutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1and2AutoEnroll" -AclObject $ACL $ESC1and2Enroll = Get-ADObject "CN=ESC1and2Enroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1and2EnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '2.5.29.37.0' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '2.5.29.37.0' +} Set-ADObject $ESC1and2Enroll.DistinguishedName -Add $ESC1and2EnrollProperties $ACL = Get-Acl "AD:$ESC1and2Enroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1and2Enroll" -AclObject $ACL $ESC1and2FilteredAutoEnroll = Get-ADObject "CN=ESC1and2FilteredAutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1and2FilteredAutoEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '2.5.29.37.0' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '2.5.29.37.0' +} Set-ADObject $ESC1and2FilteredAutoEnroll.DistinguishedName -Add $ESC1and2FilteredAutoEnrollProperties $ACL = Get-Acl "AD:$ESC1and2FilteredAutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1and2FilteredAutoEnroll" -AclObject $ACL $ESC1and2FilteredEnroll = Get-ADObject "CN=ESC1and2FilteredEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1and2FilteredEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '2.5.29.37.0' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '2.5.29.37.0' +} Set-ADObject $ESC1and2FilteredEnroll.DistinguishedName -Add $ESC1and2FilteredEnrollProperties $ACL = Get-Acl "AD:$ESC1and2FilteredEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1and2FilteredEnroll" -AclObject $ACL $ESC1AutoEnroll = Get-ADObject "CN=ESC1AutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1AutoEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' +} Set-ADObject $ESC1AutoEnroll.DistinguishedName -Add $ESC1AutoEnrollProperties $ACL = Get-Acl "AD:$ESC1AutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1AutoEnroll" -AclObject $ACL $ESC1Enroll = Get-ADObject "CN=ESC1Enroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1EnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' +} Set-ADObject $ESC1Enroll.DistinguishedName -Add $ESC1EnrollProperties $ACL = Get-Acl "AD:$ESC1Enroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1Enroll" -AclObject $ACL $ESC1FilteredAutoEnroll = Get-ADObject "CN=ESC1FilteredAutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1FilteredAutoEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' +} Set-ADObject $ESC1FilteredAutoEnroll.DistinguishedName -Add $ESC1FilteredAutoEnrollProperties $ACL = Get-Acl "AD:$ESC1FilteredAutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1FilteredAutoEnroll" -AclObject $ACL $ESC1FilteredEnroll = Get-ADObject "CN=ESC1FilteredEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC1FilteredEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 - 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' -} + 'msPKI-Enrollment-Flag' = 0 + 'pKIExtendedKeyUsage' = '1.3.6.1.5.5.7.3.2' +} Set-ADObject $ESC1FilteredEnroll.DistinguishedName -Add $ESC1FilteredEnrollProperties $ACL = Get-Acl "AD:$ESC1FilteredEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC1FilteredEnroll" -AclObject $ACL $ESC2AutoEnroll = Get-ADObject "CN=ESC2AutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC2AutoEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 -} + 'msPKI-Enrollment-Flag' = 0 +} Set-ADObject $ESC2AutoEnroll.DistinguishedName -Add $ESC2AutoEnrollProperties Set-ADObject $ESC2AutoEnroll.DistinguishedName -Clear pKIExtendedKeyUsage $ACL = Get-Acl "AD:$ESC2AutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC2AutoEnroll" -AclObject $ACL $ESC2Enroll = Get-ADObject "CN=ESC2Enroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC2EnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 -} + 'msPKI-Enrollment-Flag' = 0 +} Set-ADObject $ESC2Enroll.DistinguishedName -Add $ESC2EnrollProperties Set-ADObject $ESC2Enroll.DistinguishedName -Clear pKIExtendedKeyUsage $ACL = Get-Acl "AD:$ESC2Enroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC2Enroll" -AclObject $ACL $ESC2FilteredAutoEnroll = Get-ADObject "CN=ESC2FilteredAutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC2FilteredAutoEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 -} + 'msPKI-Enrollment-Flag' = 0 +} Set-ADObject $ESC2FilteredAutoEnroll.DistinguishedName -Add $ESC2FilteredAutoEnrollProperties Set-ADObject $ESC2FilteredAutoEnroll.DistinguishedName -Clear pKIExtendedKeyUsage $ACL = Get-Acl "AD:$ESC2FilteredAutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC2FilteredAutoEnroll" -AclObject $ACL $ESC2FilteredEnroll = Get-ADObject "CN=ESC2FilteredEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ESC2FilteredEnrollProperties = @{ 'msPKI-Certificate-Name-Flag' = 1 - 'msPKI-Enrollment-Flag' = 0 -} + 'msPKI-Enrollment-Flag' = 0 +} Set-ADObject $ESC2FilteredEnroll.DistinguishedName -Add $ESC2FilteredEnrollProperties Set-ADObject $ESC2FilteredEnroll.DistinguishedName -Clear pKIExtendedKeyUsage $ACL = Get-Acl "AD:$ESC2FilteredEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC2FilteredEnroll" -AclObject $ACL $ESC3Condition1 = Get-ADObject "CN=ESC3Condition1,CN=Certificate Templates,$PKSContainer" -Properties * $ESC3Condition1Properties = @{ - 'DisplayName' = 'ESC3Condition1' - 'msPKI-Enrollment-Flag' = 0 + 'DisplayName' = 'ESC3Condition1' + 'msPKI-Enrollment-Flag' = 0 'msPKI-Certificate-Application-Policy' = '1.3.6.1.4.1.311.20.2.1' - 'msPKI-Certificate-Name-Flag' = -2113929216 + 'msPKI-Certificate-Name-Flag' = -2113929216 # 'msPKI-Cert-Template-OID' = '1.3.6.1.4.1.311.21.8.11772860.15111666.14435736.6562275.12440657.32.7694220.3484220' #'msPKI-Minimal-Key-Size' = 2048 #'msPKI-Private-Key-Flag' = 16842752 - 'msPKI-RA-Signature' = 0 + 'msPKI-RA-Signature' = 0 # 'msPKI-Template-Minor-Revision' = 7 - 'msPKI-Template-Schema-Version' = 2 - 'pKIExtendedKeyUsage' = '1.3.6.1.4.1.311.20.2.1' -} + 'msPKI-Template-Schema-Version' = 2 + 'pKIExtendedKeyUsage' = '1.3.6.1.4.1.311.20.2.1' +} Set-ADObject $ESC3Condition1.DistinguishedName -Add $ESC3Condition1Properties $ACL = Get-Acl "AD:$ESC3Condition1" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC3Condition1" -AclObject $ACL @@ -273,7 +273,7 @@ Set-Acl "AD:$ESC3Condition1" -AclObject $ACL # 'msPKI-Enrollment-Flag' = 0 # 'msPKI-RA-Signature' = 1 # # 'msPKI-Certificate-Application-Policy' = '' -# } +# } # Set-ADObject $ESC3Condition2Schema1.DistinguishedName -Add $ESC3Condition2Schema1Properties # Set-ADObject $ESC3Condition2Schema1.DistinguishedName -Clear pKIExtendedKeyUsage # $ACL = Get-Acl "AD:$ESC3Condition2Schema1" @@ -300,7 +300,7 @@ Set-Acl "AD:$ESC3Condition1" -AclObject $ACL # 'msPKI-Private-Key-Flag' = 16842768 # 'msPKI-Template-Minor-Revision' = 18 # 'msPKI-Template-Schema-Version' = 2 -# } +# } # Set-ADObject $ESC3Condition2Schema2.DistinguishedName -Add $ESC3Condition2Schema2Properties # Set-ADObject $ESC3Condition2Schema2.DistinguishedName -Clear pKIExtendedKeyUsage # $ACL = Get-Acl "AD:$ESC3Condition2Schema2" @@ -310,13 +310,13 @@ Set-Acl "AD:$ESC3Condition1" -AclObject $ACL $ESC4FilteredAutoEnroll = Get-ADObject "CN=ESC4FilteredAutoEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4FilteredAutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4FilteredAutoEnroll" -AclObject $ACL $ESC4FilteredEnroll = Get-ADObject "CN=ESC4FilteredEnroll,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4FilteredEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4FilteredEnroll" -AclObject $ACL @@ -327,13 +327,13 @@ Set-Acl "AD:$ESC4FilteredOwner" -AclObject $ACL $ESC4FilteredSafeUsers = Get-ADObject "CN=ESC4FilteredSafeUsers,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4FilteredSafeUsers" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$GenericAll,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $GenericAll, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4FilteredSafeUsers" -AclObject $ACL $ESC4GenericAll = Get-ADObject "CN=ESC4GenericAll,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4GenericAll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$GenericAll,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $GenericAll, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4GenericAll" -AclObject $ACL @@ -344,25 +344,25 @@ Set-Acl "AD:$ESC4UnsafeOwner" -AclObject $ACL $ESC4WriteProperty = Get-ADObject "CN=ESC4WriteProperty,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4WriteProperty" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$WriteProperty,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $WriteProperty, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4WriteProperty" -AclObject $ACL $ESC4WriteOwner = Get-ADObject "CN=ESC4WriteOwner,CN=Certificate Templates,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC4WriteOwner" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$WriteOwner,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $WriteOwner, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC4WriteOwner" -AclObject $ACL $ESC5FilteredAutoEnroll = Get-ADObject "CN=ESC5FilteredAutoEnroll,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5FilteredAutoEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$AutoEnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $AutoEnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5FilteredAutoEnroll" -AclObject $ACL $ESC5FilteredEnroll = Get-ADObject "CN=ESC5FilteredEnroll,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5FilteredEnroll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$DefaultRights,$Allow,$EnrollGUID +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $DefaultRights, $Allow, $EnrollGUID $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5FilteredEnroll" -AclObject $ACL @@ -373,13 +373,13 @@ Set-Acl "AD:$ESC5FilteredOwner" -AclObject $ACL $ESC5FilteredSafeUsers = Get-ADObject "CN=ESC5FilteredSafeUsers,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5FilteredSafeUsers" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators,$GenericAll,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $Administrators, $GenericAll, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5FilteredSafeUsers" -AclObject $ACL $ESC5GenericAll = Get-ADObject "CN=ESC5GenericAll,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5GenericAll" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$GenericAll,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $GenericAll, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5GenericAll" -AclObject $ACL @@ -390,13 +390,13 @@ Set-Acl "AD:$ESC5UnsafeOwner" -AclObject $ACL $ESC5WriteProperty = Get-ADObject "CN=ESC5WriteProperty,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5WriteProperty" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$WriteProperty,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $WriteProperty, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5WriteProperty" -AclObject $ACL $ESC5WriteOwner = Get-ADObject "CN=ESC5WriteOwner,$PKSContainer" -Properties * $ACL = Get-Acl "AD:$ESC5WriteOwner" -$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers,$WriteOwner,$Allow +$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $AuthenticatedUsers, $WriteOwner, $Allow $ACL.AddAccessRule($AccessRule) Set-Acl "AD:$ESC5WriteOwner" -AclObject $ACL @@ -417,9 +417,15 @@ Get-ADObject -Filter 'objectClass -eq "pKIEnrollmentService"' -SearchBase $PKSCo } catch { $AuditFilter = 'Failure' } + try { + $CertutilInterfaceFlag = certutil -config $CAFullName -getreg CA\InterfaceFlags + } catch { + $CertutilInterfaceFlag = 'Failure' + } } else { $AuditFilter = 'CA Unavailable' $SANFlag = 'CA Unavailable' + $InterfaceFlag = 'CA Unavailable' } if ($CertutilAudit) { try { @@ -442,6 +448,14 @@ Get-ADObject -Filter 'objectClass -eq "pKIEnrollmentService"' -SearchBase $PKSCo $SANFlag = 'No' } } + if ($CertutilInterfaceFlag) { + [string]$InterfaceFlag = $CertutilInterfaceFlag | Select-String ' IF_ENFORCEENCRYPTICERTREQUEST -- 200 \(' + if ($InterfaceFlag) { + $InterfaceFlag = 'Yes' + } else { + $InterfaceFlag = 'No' + } + } if ( ($AuditFilter -ne '0') -and ($AuditFilter -ne 'Never Configured') ) { certutil -config $CAFullname -setreg CA\AuditFilter 0 @@ -452,4 +466,9 @@ Get-ADObject -Filter 'objectClass -eq "pKIEnrollmentService"' -SearchBase $PKSCo certutil -config $CAFullname -setreg policy\EditFlags +EDITF_ATTRIBUTESUBJECTALTNAME2 Invoke-Command -ComputerName $CAHostFQDN -ScriptBlock { Get-Service -Name 'certsvc' | Restart-Service -Force } } -} \ No newline at end of file + + if ($InterfaceFlag -eq 'Yes') { + certutil -config $CAFullname -setreg CA\InterfaceFlags -IF_ENFORCEENCRYPTICERTREQUEST + Invoke-Command -ComputerName $CAHostFQDN -ScriptBlock { Get-Service -Name 'certsvc' | Restart-Service -Force } + } +}