Private/Set/Set-NoSecurityExtension.ps1

function Set-NoSecurityExtension {
    <#
        .SYNOPSIS
        Adds a NoSecurityExtension property to AD CS certificate template objects.
 
        .DESCRIPTION
        Examines the msPKI-Enrollment-Flag attribute of Active Directory Certificate Services
        certificate template objects to determine if the szOID_NTDS_CA_SECURITY_EXT security
        extension is disabled (ESC9).
         
        The function checks if the CT_FLAG_NO_SECURITY_EXTENSION flag (0x80000) is set in the
        msPKI-Enrollment-Flag attribute. When this flag is set, certificates issued from the
        template will not enforce strong certificate binding, making them vulnerable to ESC9.
         
        Templates with this flag disabled combined with client authentication EKUs can be
        exploited by modifying userPrincipalName or dNSHostName attributes to impersonate
        higher-privileged accounts.
         
        The function adds a boolean NoSecurityExtension property to each input object
        indicating whether the template has the security extension disabled.
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS certificate templates.
        These objects must contain the msPKI-Enrollment-Flag attribute.
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe certificate template DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with an added NoSecurityExtension boolean property.
 
        .EXAMPLE
        $templates = Get-AdcsObject -RootDSE $rootDSE | Where-Object { $_.objectClass -contains 'pKICertificateTemplate' }
        $templates | Set-NoSecurityExtension
        Processes all certificate templates and adds the NoSecurityExtension property to each.
 
        .EXAMPLE
        Get-AdcsObject -RootDSE $rootDSE | Set-NoSecurityExtension | Where-Object NoSecurityExtension
        Retrieves all AD CS objects, adds NoSecurityExtension property, and filters to vulnerable templates.
 
        .NOTES
        The CT_FLAG_NO_SECURITY_EXTENSION flag (0x80000) in msPKI-Enrollment-Flag indicates that
        the szOID_NTDS_CA_SECURITY_EXT security extension is disabled on certificates issued
        from this template.
         
        When this flag is set (security extension disabled), certificates do not enforce strong
        certificate binding. Combined with client authentication EKU and the ability to modify
        userPrincipalName or dNSHostName, this creates the ESC9 vulnerability.
         
        Strong certificate binding enforcement was introduced in KB5014754 to prevent these
        types of attacks by requiring proper subject-to-certificate mapping.
 
        .LINK
        https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc9-no-security-extension-on-certificate-template
         
        .LINK
        https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16
    #>

    [CmdletBinding()]
    [OutputType([System.DirectoryServices.DirectoryEntry[]])]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.DirectoryServices.DirectoryEntry[]]$AdcsObject
    )

    #requires -Version 5.1

    begin {
        Write-Verbose "Identifying templates with szOID_NTDS_CA_SECURITY_EXT disabled (ESC9)..."
        $CT_FLAG_NO_SECURITY_EXTENSION = 0x80000
    }

    process {
        $AdcsObject | Where-Object SchemaClassName -eq pKICertificateTemplate | ForEach-Object {
            try {
                $objectName = if ($_.Properties.displayName.Count -gt 0) {
                    $_.Properties.displayName[0] 
                } elseif ($_.Properties.name.Count -gt 0) {
                    $_.Properties.name[0]
                } else {
                    $_.Properties.distinguishedName[0]
                }
                Write-Verbose "Processing template: $objectName"
                
                $noSecurityExtension = $false
                
                # Check if msPKI-Enrollment-Flag has CT_FLAG_NO_SECURITY_EXTENSION set
                if ($_.Properties.'msPKI-Enrollment-Flag'.Count -gt 0) {
                    [int]$enrollmentFlag = $_.Properties.'msPKI-Enrollment-Flag'[0]
                    Write-Verbose "msPKI-Enrollment-Flag value: $enrollmentFlag"
                    
                    if ($enrollmentFlag -band $CT_FLAG_NO_SECURITY_EXTENSION) {
                        $noSecurityExtension = $true
                        Write-Verbose "CT_FLAG_NO_SECURITY_EXTENSION is SET - security extension disabled (ESC9 vulnerable)"
                    } else {
                        Write-Verbose "CT_FLAG_NO_SECURITY_EXTENSION is NOT set - security extension enabled"
                    }
                } else {
                    Write-Verbose "msPKI-Enrollment-Flag not present - security extension enabled by default"
                }
                
                # Update the AdcsObjectStore with the NoSecurityExtension property
                $dn = $_.Properties.distinguishedName[0]
                if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn] | Add-Member -NotePropertyName NoSecurityExtension -NotePropertyValue $noSecurityExtension -Force
                    Write-Verbose "Updated AD CS Object Store for $dn with NoSecurityExtension = $noSecurityExtension"
                }
                
                # Also add to the pipeline object for backward compatibility
                $_ | Add-Member -NotePropertyName NoSecurityExtension -NotePropertyValue $noSecurityExtension -Force
                
                # Return the modified object
                $_
            }
            catch {
                Write-Error "Error processing template $($_.Properties.distinguishedName[0]): $_"
            }
        }
    }

    end {
        Write-Verbose "Finished identifying templates with security extension disabled"
    }
}